Mam nadzieję potwierdzić moją obserwację i uzyskać wyjaśnienie, dlaczego tak się dzieje.
Mam funkcję zdefiniowaną jako:
CREATE OR REPLACE FUNCTION "public"."__post_users_id_coin" ("coins" integer, "userid" integer) RETURNS TABLE (id integer) AS '
UPDATE
users
SET
coin = coin + coins
WHERE
userid = users.id
RETURNING
users.id' LANGUAGE "sql" COST 100 ROWS 1000
VOLATILE
RETURNS NULL ON NULL INPUT
SECURITY INVOKER
Kiedy wywołuję tę funkcję z CTE, wykonuje polecenie SQL, ale nie wyzwala funkcji, na przykład:
WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))
SELECT
1 -- Select 1 but update not performed
Z drugiej strony, jeśli wywołam funkcję z CTE, a następnie wybiorę wynik CTE (lub wywołam funkcję bezpośrednio bez CTE), wykona polecenie SQL i wyzwoli funkcję, na przykład:
WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))
SELECT
*
FROM
test -- Select result and update performed
lub
SELECT * FROM __post_users_id_coin(10,1)
Ponieważ tak naprawdę nie dbam o wynik funkcji (wystarczy, że przeprowadzi aktualizację), czy jest jakiś sposób, aby to zadziałało bez wybierania wyniku CTE?
źródło
Jest to oczekiwane, udokumentowane zachowanie.
Tom Lane wyjaśnia to tutaj.
Udokumentowane w instrukcji tutaj:
Odważny nacisk moje. „Dane modyfikujący” są
INSERT
,UPDATE
iDELETE
zapytań. (W przeciwieństwie doSELECT
.). Instrukcja jeszcze raz:Prawidłowa funkcja
Porzuciłem domyślne klauzule (szum) i
STRICT
jest to krótki synonimRETURNS NULL ON NULL INPUT
.Upewnij się, że nazwy parametrów nie powodują konfliktu z nazwami kolumn. Byłem zajęty
_
, ale to tylko moje osobiste preferencje.Jeśli
coin
mogęNULL
, sugeruję:Jeśli
users.id
jest kluczem podstawowym, to aniRETURNS TABLE
nieROWs 1000
ma sensu. Tylko jeden wiersz może być aktualizowany / zwracany. Ale to wszystko poza głównym punktem.Prawidłowe połączenie
Nie ma sensu używać
RETURNING
klauzuli i zwracać wartości z funkcji, jeśli i tak chcesz zignorować zwrócone wartości w wywołaniu. Nie ma również sensu rozkładanie zwróconych wierszy,SELECT * FROM ...
jeśli i tak je zignorujesz.Po prostu zwróć stałą skalarną (
RETURNING 1
), zdefiniuj funkcję jakoRETURNS int
(lub upuśćRETURNING
i zrób toRETURNS void
) i wywołaj jąSELECT my_function(...)
Rozwiązanie
Ponieważ ty ...
.. po prostu
SELECT
stała z CTE. Gwarantuje się, że będzie wykonywany tak długo, jak długo będzie się do niego odwoływałSELECT
(bezpośrednio lub pośrednio).Jeśli faktycznie masz funkcję zwracania zestawu i nadal nie obchodzi Cię wynik:
Nie musisz zwracać więcej niż 1 wiersz. Funkcja jest nadal wywoływana.
Wreszcie, nie jest jasne, dlaczego potrzebujesz CTE na początek. Prawdopodobnie tylko dowód koncepcji.
Blisko związane:
Powiązana odpowiedź na temat SO:
I zastanów się:
źródło
INSERT
przedUPDATE
tą samą funkcją owijania - brak dostępnych transakcji.test
in jestWITH test AS (SELECT * FROM __post_users_id_coin(10, 1)) SELECT ... LIMIT 1;
uważany za modyfikujący CTE, czy nie?SELECT
nie oznacza „modyfikowania danych” zgodnie z terminologią CTE. Dodałem wyjaśnienie powyżej. Obowiązkiem użytkownika jest dodanie kodu do funkcji modyfikującej dane za zasłonami.