Espérant simplement confirmer mon observation et obtenir une explication de la raison pour laquelle cela se produit.
J'ai une fonction définie comme:
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
Lorsque j'appelle cette fonction à partir d'un CTE, elle exécute la commande SQL mais ne déclenche pas la fonction, par exemple:
WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))
SELECT
1 -- Select 1 but update not performed
D'un autre côté, si j'appelle la fonction à partir d'un CTE et que je sélectionne ensuite le résultat du CTE (ou que j'appelle la fonction directement sans CTE), il exécute la commande SQL et déclenche la fonction, par exemple:
WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))
SELECT
*
FROM
test -- Select result and update performed
ou
SELECT * FROM __post_users_id_coin(10,1)
Étant donné que je ne me soucie pas vraiment du résultat de la fonction (j'en ai juste besoin pour effectuer la mise à jour), existe-t-il un moyen de faire fonctionner cela sans sélectionner le résultat du CTE?
la source
Il s'agit d'un comportement attendu et documenté.
Tom Lane l'explique ici.
Documenté dans le manuel ici:
Accentuation sur moi. "Modification des données" sont
INSERT
,UPDATE
et lesDELETE
requêtes. (Par opposition àSELECT
.). Le manuel une fois de plus:Fonction appropriée
J'ai supprimé les clauses par défaut (bruit) et
STRICT
est le synonyme court deRETURNS NULL ON NULL INPUT
.Assurez-vous que les noms de paramètres n'entrent pas en conflit avec les noms de colonnes. J'ai ajouté avant
_
, mais c'est juste ma préférence personnelle.Si
coin
possible,NULL
je suggère:Si
users.id
est la clé primaire, ni ,RETURNS TABLE
niROWs 1000
aucun sens. Une seule ligne peut être mise à jour / renvoyée. Mais c'est tout à côté du point principal.Appel approprié
Cela n'a aucun sens d'utiliser la
RETURNING
clause et de renvoyer des valeurs de votre fonction si vous voulez quand même ignorer les valeurs renvoyées dans l'appel. Il est également inutile de décomposer les lignes retournéesSELECT * FROM ...
si vous les ignorez de toute façon.Il suffit de renvoyer une constante scalaire (
RETURNING 1
), de définir la fonction commeRETURNS int
(ou de la supprimerRETURNING
et de la créerRETURNS void
) et de l'appeler avecSELECT my_function(...)
Solution
Depuis que vous ...
.. juste
SELECT
une constante du CTE. Il est garanti d'être exécuté tant qu'il est référencé à l'extérieurSELECT
(directement ou indirectement).Si vous avez réellement une fonction de retour d'ensemble et que vous ne vous souciez toujours pas de la sortie:
Pas besoin de retourner plus d'une ligne. La fonction est toujours appelée.
Enfin, on ne sait pas pourquoi vous avez besoin du CTE pour commencer. Probablement juste une preuve de concept.
Étroitement liés:
Réponse connexe sur SO:
Et considérez:
la source
INSERT
avantUPDATE
dans la même fonction d'encapsulation - aucune transaction disponible.test
dansWITH test AS (SELECT * FROM __post_users_id_coin(10, 1)) SELECT ... LIMIT 1;
ou non considéré comme un CTE de modification?SELECT
ne "modifie pas les données" selon la terminologie CTE. J'ai ajouté quelques précisions ci-dessus. Il est de la responsabilité de l'utilisateur s'il ajoute du code à une fonction qui modifie les données derrière les rideaux.