TL;DR :вие можете изберете от (таблични) функции или от всякакъв вид функция в PostgreSQL. Но не и от съхранени процедури.
Ето едно „интуитивно“, донякъде агностично на базата данни обяснение, тъй като вярвам, че SQL и неговите многобройни диалекти са твърде много органично развит език/концепция, за да има фундаментално, „научно“ обяснение за това.
Процедури срещу функции, исторически
Всъщност не виждам смисъл да избирам от съхранени процедури, но съм предубеден от години опит и приемане на статуквото и със сигурност виждам каква е разликата между процедурите и функции могат да бъдат объркващи и как човек би искал те да бъдат по-гъвкави и мощни. По-конкретно в SQL Server, Sybase или MySQL, процедурите могат да върнат произволен брой набори от резултати / брой актуализации, въпреки че това не е същото като функция, която връща добре дефиниран тип.
Мислете за процедурите като за наложителни процедури (със странични ефекти) и на функции като чисти рутинни процедури без странични ефекти. A SELECT
самото изявление също е "чисто" без странични ефекти (освен потенциалните ефекти на заключване), така че има смисъл да мислим за функциите като за единствените типове рутинни процедури, които могат да се използват в SELECT
изявление.
Всъщност, мислете за функциите като за рутинни процедури със силни ограничения върху поведението, докато на процедурите е разрешено да изпълняват произволни програми.
4GL срещу 3GL езици
Друг начин да погледнете това е от гледна точка на това, че SQL е език за програмиране от 4-то поколение (4GL) . 4GL може да работи разумно само ако е силно ограничен в това, което може да прави. Общи таблични изрази направиха SQL turing-complete , да, но декларативният характер на SQL все още му пречи да бъде език с общо предназначение от практическа, ежедневна гледна точка.
Съхранените процедури са начин за заобикаляне на това ограничение. Понякога искате за да бъде завършен и практичен. И така, съхранените процедури прибягват до това да бъдат императивни, да имат странични ефекти, да са транзакционни и т.н.
Съхранените функции са умен начин за въвеждане на някои Функциите на 3GL / процедурния език в по-чистия свят на 4GL на цената на забраната на страничните ефекти вътре в тях (освен ако не искате да отворите кутията на Пандора и да имате напълно непредсказуем SELECT
изявления).
Фактът, че някои бази данни позволяват на техните съхранени процедури да връщат произволен брой набори от резултати / курсори, е характеристика на тяхното разрешаване на произволно поведение, включително странични ефекти. По принцип нищо от казаното от мен не би предотвратило това конкретно поведение и в съхранените функции, но би било много непрактично и трудно за управление, ако им беше позволено да го правят в контекста на SQL, езика 4GL.
Така:
- Процедурите могат да извикват процедури, всяка функция и SQL
- "Чистите" функции могат да извикват "чисти" функции и SQL
- SQL може да извиква „чисти“ функции и SQL
Но:
- „Чистите“ функции, извикващи процедури, стават „нечисти“ функции (като процедури)
И:
- SQL не може да извиква процедури
- SQL не може да извиква "нечисти" функции
Примери за „чисти“ функции с таблични стойности:
Ето няколко примера за използване на „чисти“ функции с таблични стойности:
Оракул
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;
/
И след това:
SELECT * FROM TABLE (my_function(1, 2))
SQL сървър
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
И след това
SELECT * FROM my_function(1, 2)
PostgreSQL
Позволете ми да кажа нещо за PostgreSQL.
PostgreSQL е страхотен и следователно е изключение. Освен това е странен и вероятно 50% от функциите му не трябва да се използват в производството. Той поддържа само „функции“, не „процедури“, но тези функции могат да действат като всичко. Вижте следното:
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;
Странични ефекти:
- Създава се таблица
- Вмъква се запис
И все пак:
SELECT * FROM wow();
Добив
wow
---
1