Ich hoffe nur, meine Beobachtung zu bestätigen und eine Erklärung zu erhalten, warum dies geschieht.
Ich habe eine Funktion definiert als:
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
Wenn ich diese Funktion von einem CTE aus aufrufe, führt sie den SQL-Befehl aus, löst die Funktion jedoch nicht aus. Beispiel:
WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))
SELECT
1 -- Select 1 but update not performed
Wenn ich dagegen die Funktion von einem CTE aus aufrufe und dann das Ergebnis des CTE auswähle (oder die Funktion direkt ohne CTE aufrufe), führt sie den SQL-Befehl aus und löst die Funktion aus, zum Beispiel:
WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))
SELECT
*
FROM
test -- Select result and update performed
oder
SELECT * FROM __post_users_id_coin(10,1)
Da mir das Ergebnis der Funktion nicht wirklich wichtig ist (ich brauche es nur, um das Update durchzuführen), gibt es eine Möglichkeit, dies zum Laufen zu bringen, ohne das Ergebnis des CTE auszuwählen?
quelle
Dies wird erwartet, dokumentiertes Verhalten.
Tom Lane erklärt es hier.
Hier im Handbuch dokumentiert:
Meine kühne Betonung. „Data-modifizierende“ sind
INSERT
,UPDATE
undDELETE
Abfragen. (Im Gegensatz zuSELECT
.). Das Handbuch noch einmal:Passende Funktion
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
kannNULL
ich vorschlagen:Wenn
users.id
ist der Primärschlüssel, dann wederRETURNS TABLE
nochROWs 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 alsRETURNS int
(oder lassen Sie sie fallenRETURNING
ganz und machen Sie sieRETURNS void
) und rufen Sie sie mit aufSELECT my_function(...)
Lösung
Seit du ...
.. nur
SELECT
eine konstante Form der CTE. Es ist garantiert ausgeführt, solange es in der äußerenSELECT
(direkt oder indirekt) verwiesen wird .Wenn Sie tatsächlich eine Set-Return- Funktion haben und sich trotzdem nicht um die Ausgabe kümmern:
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:
quelle
INSERT
vorUPDATE
der gleichen Wrapping-Funktion mache - keine Transaktionen verfügbar.test
inWITH test AS (SELECT * FROM __post_users_id_coin(10, 1)) SELECT ... LIMIT 1;
einer Modifizierung CTE oder nicht in Betracht gezogen?SELECT
ist gemäß CTE-Terminologie nicht " datenmodifizierend ". Ich habe oben einige Klarstellungen hinzugefügt. Es liegt in der Verantwortung des Benutzers, wenn er einer Funktion, die Daten hinter den Kulissen ändert, Code hinzufügt.