Dies wird erwartet, dokumentiertes Verhalten.
Tom Lane erklärt es hier.
Hier im Handbuch dokumentiert:
Datenmodifizierende Anweisungen in WITH
werden genau einmal und
immer vollständig ausgeführt , unabhängig davon, ob die primäre Abfrage alle (oder tatsächlich alle) Ausgaben liest. Beachten Sie, dass dies von der Regel für SELECT
in WITH
abweicht: Wie im vorherigen Abschnitt angegeben, wird die Ausführung von a SELECT
nur so lange ausgeführt, wie die primäre Abfrage ihre Ausgabe erfordert .
Meine kühne Betonung. „Data-modifizierende“ sind INSERT
, UPDATE
und DELETE
Abfragen. (Im Gegensatz zu SELECT
.). Das Handbuch noch einmal:
Sie können Daten modifizierende Anweisungen (verwenden INSERT
, UPDATE
oder DELETE
) in WITH
.
Passende Funktion
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;
Ich habe Standardklauseln (Noise) fallen gelassen und
STRICT
ist das kurze Synonym fürRETURNS NULL ON NULL INPUT
.
Stellen Sie irgendwie sicher, dass Parameternamen nicht mit Spaltennamen in Konflikt stehen. Ich habe mit vorangestellt _
, aber das ist nur meine persönliche Präferenz.
Wenn coin
kann NULL
ich vorschlagen:
SET coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
Wenn users.id
ist der Primärschlüssel, dann weder RETURNS TABLE
noch ROWs 1000
Sinn. Es kann nur eine einzelne Zeile aktualisiert / zurückgegeben werden. Aber das ist alles neben dem Hauptpunkt.
Richtiger Anruf
Es macht keinen Sinn, das zu benutzen RETURNING
Klausel zu verwenden und Werte aus Ihrer Funktion zurückzugeben, wenn Sie die zurückgegebenen Werte im Aufruf trotzdem ignorieren wollen. Es macht auch keinen Sinn, zurückgegebene Zeilen mit zu zerlegen, SELECT * FROM ...
wenn Sie sie trotzdem ignorieren.
Geben Sie einfach eine Skalarkonstante ( RETURNING 1
) zurück, definieren Sie die Funktion als RETURNS int
(oder lassen Sie sie fallenRETURNING
ganz und machen Sie sie RETURNS void
) und rufen Sie sie mit aufSELECT my_function(...)
Lösung
Seit du ...
Das Ergebnis ist mir egal
.. nur SELECT
eine konstante Form der CTE. Es ist garantiert ausgeführt, solange es in der äußeren SELECT
(direkt oder indirekt) verwiesen wird .
WITH test AS (SELECT __post_users_id_coin(10, 1))
SELECT 1 FROM test;
Wenn Sie tatsächlich eine Set-Return- Funktion haben und sich trotzdem nicht um die Ausgabe kümmern:
WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
SELECT 1 FROM test LIMIT 1;
Es muss nicht mehr als eine Zeile zurückgegeben werden. Die Funktion wird weiterhin aufgerufen.
Schließlich ist unklar, warum Sie den CTE benötigen. Wahrscheinlich nur ein Proof of Concept.
Eng verwandt:
Verwandte Antwort auf SO:
Und bedenken Sie: