TL;DR :você pode selecione de funções (com valor de tabela) ou de qualquer tipo de função no PostgreSQL. Mas não de procedimentos armazenados.
Aqui está uma explicação "intuitiva", um tanto agnóstica de banco de dados, pois acredito que o SQL e seus muitos dialetos são muito uma linguagem / conceito cultivado organicamente para que haja uma explicação "científica" fundamental para isso.
Procedimentos x Funções, historicamente
Não vejo sentido em selecionar procedimentos armazenados, mas sou influenciado por anos de experiência e aceitação do status quo, e certamente vejo como a distinção entre procedimentos e funções pode ser confuso e como se deseja que eles sejam mais versáteis e poderosos. Especificamente no SQL Server, Sybase ou MySQL, os procedimentos podem retornar um número arbitrário de conjuntos de resultados/contagens de atualização, embora isso não seja o mesmo que uma função que retorna um tipo bem definido.
Pense nos procedimentos como rotinas imperativas (com efeitos colaterais) e de funções como rotinas puras sem efeitos colaterais. Um
SELECT
declaração em si também é "pura" sem efeitos colaterais (além de possíveis efeitos de bloqueio), então faz sentido pensar em funções como os únicos tipos de rotinas que podem ser usadas em um SELECT
declaração. Na verdade, pense nas funções como rotinas com fortes restrições de comportamento, enquanto os procedimentos podem executar programas arbitrários.
Linguagens 4GL x 3GL
Outra maneira de ver isso é da perspectiva do SQL ser uma linguagem de programação de 4ª geração (4GL) . Um 4GL só pode funcionar razoavelmente se for muito restrito no que pode fazer. Expressões de tabela comuns feitas SQL turing-complete , sim, mas a natureza declarativa do SQL ainda impede que seja uma linguagem de propósito geral do ponto de vista prático do dia a dia.
Os procedimentos armazenados são uma maneira de contornar essa limitação. Às vezes, você quer estar completando e prático. Assim, os procedimentos armazenados se tornam imperativos, tendo efeitos colaterais, sendo transacionais, etc.
As funções armazenadas são uma maneira inteligente de apresentar algumas 3GL / recursos de linguagem procedural para o mundo 4GL mais puro ao preço de proibir efeitos colaterais dentro deles (a menos que você queira abrir a caixa de pandora e ter um
SELECT
completamente imprevisível declarações). O fato de alguns bancos de dados permitirem que seus procedimentos armazenados retornem números arbitrários de conjuntos de resultados/cursores é uma característica de permitirem comportamentos arbitrários, incluindo efeitos colaterais. Em princípio, nada do que eu dissesse impediria esse comportamento específico também em funções armazenadas, mas seria muito impraticável e difícil de gerenciar se fosse permitido fazê-lo no contexto do SQL, a linguagem 4GL.
Desta forma:
- Procedures podem chamar procedures, qualquer função e SQL
- Funções "puras" podem chamar funções "puras" e SQL
- SQL pode chamar funções "puras" e SQL
Mas:
- Funções "puras" que chamam procedimentos tornam-se funções "impuras" (como procedimentos)
E:
- SQL não pode chamar procedimentos
- SQL não pode chamar funções "impuras"
Exemplos de funções "puras" com valor de tabela:
Aqui estão alguns exemplos de uso de funções "puras" com valor de tabela:
Oráculo
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 depois:
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 depois
SELECT * FROM my_function(1, 2)
PostgreSQL
Deixe-me dar uma palavrinha sobre o PostgreSQL.
PostgreSQL é incrível e, portanto, uma exceção. Também é estranho e provavelmente 50% de seus recursos não devem ser usados em produção. Ele suporta apenas "funções", não "procedimentos", mas essas funções podem atuar como qualquer coisa. Confira a seguir:
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;
Efeitos colaterais:
- Uma tabela é criada
- Um registro é inserido
Ainda:
SELECT * FROM wow();
Rendimentos
wow
---
1