TL;DR :tu puoi seleziona da funzioni (con valori di tabella) o da qualsiasi tipo di funzione in PostgreSQL. Ma non da stored procedure.
Ecco una spiegazione "intuitiva", in qualche modo indipendente dal database, perché credo che SQL e i suoi numerosi dialetti siano troppo di un linguaggio/concetto cresciuto organicamente perché ci sia una spiegazione fondamentale e "scientifica" per questo.
Procedure vs. Funzioni, storicamente
Non vedo davvero il senso di selezionare dalle stored procedure, ma sono influenzato da anni di esperienza e dall'accettazione dello status quo, e sicuramente vedo come la distinzione tra procedure e funzioni può confondere e come si vorrebbe che fossero più versatili e potenti. In particolare in SQL Server, Sybase o MySQL, le procedure possono restituire un numero arbitrario di set di risultati/conteggi di aggiornamenti, sebbene questo non sia lo stesso di una funzione che restituisce un tipo ben definito.
Pensa alle procedure come a routine imperative (con effetti collaterali) e di funzioni come pure routine senza effetti collaterali. Un SELECT
l'affermazione stessa è anche "pura" senza effetti collaterali (a parte potenziali effetti di blocco), quindi ha senso pensare alle funzioni come agli unici tipi di routine che possono essere utilizzati in un SELECT
dichiarazione.
In effetti, considera le funzioni come routine con forti vincoli di comportamento, mentre le procedure possono eseguire programmi arbitrari.
Lingue 4GL e 3GL
Un altro modo per guardare a questo è dal punto di vista di SQL come un linguaggio di programmazione di quarta generazione (4GL) . Un 4GL può funzionare ragionevolmente solo se è fortemente limitato in ciò che può fare. Espressioni di tabelle comuni rese SQL turing-complete , sì, ma la natura dichiarativa di SQL impedisce comunque che sia un linguaggio generico da una prospettiva pratica e quotidiana.
Le stored procedure sono un modo per aggirare questa limitazione. A volte, vuoi essere completo e pratico. Pertanto, le procedure archiviate ricorrono all'essere imperative, avere effetti collaterali, essere transazionali, ecc.
Le funzioni memorizzate sono un modo intelligente per introdurne alcune 3GL / linguaggio procedurale entra nel mondo 4GL più puro al prezzo di vietare gli effetti collaterali al loro interno (a meno che tu non voglia aprire il vaso di Pandora e avere SELECT
completamente imprevedibile dichiarazioni).
Il fatto che alcuni database consentano alle loro procedure memorizzate di restituire numeri arbitrari di set di risultati/cursori è una caratteristica del loro comportamento arbitrario, inclusi gli effetti collaterali. In linea di principio, nulla di ciò che ho detto impedirebbe questo particolare comportamento anche nelle funzioni memorizzate, ma sarebbe molto poco pratico e difficile da gestire se fosse consentito farlo all'interno del contesto di SQL, il linguaggio 4GL.
Quindi:
- Le procedure possono chiamare procedure, qualsiasi funzione e SQL
- Le funzioni "pure" possono chiamare funzioni "pure" e SQL
- SQL può chiamare funzioni "pure" e SQL
Ma:
- Le funzioni "pure" che chiamano le procedure diventano funzioni "impure" (come le procedure)
E:
- SQL non può chiamare procedure
- SQL non può chiamare funzioni "impure"
Esempi di funzioni con valori di tabella "puri":
Ecco alcuni esempi di utilizzo di funzioni "pure" con valori di tabella:
Oracolo
CREATE TYPE numbers AS TABLE OF number(10);
/
CREATE OR REPLACE FUNCTION my_function (a number, b number)
RETURN numbers
IS
BEGIN
return numbers(a, b);
END my_function;
/
E poi:
SELECT * FROM TABLE (my_function(1, 2))
SQL Server
CREATE FUNCTION my_function(@v1 INTEGER, @v2 INTEGER)
RETURNS @out_table TABLE (
column_value INTEGER
)
AS
BEGIN
INSERT @out_table
VALUES (@v1), (@v2)
RETURN
END
E poi
SELECT * FROM my_function(1, 2)
PostgreSQL
Consentitemi una parola su PostgreSQL.
PostgreSQL è fantastico e quindi un'eccezione. È anche strano e probabilmente il 50% delle sue funzionalità non dovrebbe essere utilizzato in produzione. Supporta solo "funzioni", non "procedure", ma quelle funzioni possono agire come qualsiasi cosa. Dai un'occhiata a quanto segue:
CREATE OR REPLACE FUNCTION wow ()
RETURNS SETOF INT
AS $$
BEGIN
CREATE TABLE boom (i INT);
RETURN QUERY
INSERT INTO boom VALUES (1)
RETURNING *;
END;
$$ LANGUAGE plpgsql;
Effetti collaterali:
- Viene creata una tabella
- Viene inserito un record
Eppure:
SELECT * FROM wow();
Rendimenti
wow
---
1