Jest to oczekiwane, udokumentowane zachowanie.
Tom Lane wyjaśnia to tutaj.
Udokumentowane w instrukcji tutaj:
Dane modyfikujące oświadczenia w WITH
wykonywane są tylko raz i
zawsze do końca , niezależnie od tego, czy zapytanie podstawowej czyta wszystko (lub nawet każdy) ich produkcji. Zauważ, że różni się to od reguły dla SELECT
in WITH
: jak podano w poprzedniej sekcji, wykonanie a SELECT
jest przeprowadzane tylko tak długo, jak podstawowe zapytanie wymaga jego wyniku .
Odważny nacisk moje. „Dane modyfikujący” są INSERT
, UPDATE
i DELETE
zapytań. (W przeciwieństwie do SELECT
.). Instrukcja jeszcze raz:
Można użyć stwierdzenia danych modyfikujące ( INSERT
, UPDATE
lub DELETE
) w WITH
.
Prawidłowa funkcja
CREATE OR REPLACE FUNCTION public.__post_users_id_coin (_coins integer, _userid integer)
RETURNS TABLE (id integer) AS
$func$
UPDATE users u
SET coin = u.coin + _coins -- see below
WHERE u.id = _userid
RETURNING u.id
$func$ LANGUAGE sql COST 100 ROWS 1000 STRICT;
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ę:
SET coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
Jeśli users.id
jest kluczem podstawowym, to ani RETURNS TABLE
nie ROWs 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ę jako RETURNS int
(lub upuść RETURNING
i zrób to RETURNS void
) i wywołaj jąSELECT my_function(...)
Rozwiązanie
Ponieważ ty ...
tak naprawdę nie dbam o wynik
.. 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).
WITH test AS (SELECT __post_users_id_coin(10, 1))
SELECT 1 FROM test;
Jeśli faktycznie masz funkcję zwracania zestawu i nadal nie obchodzi Cię wynik:
WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
SELECT 1 FROM test LIMIT 1;
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ę: