Il s'agit d'un comportement attendu et documenté.
Tom Lane l'explique ici.
Documenté dans le manuel ici:
Les instructions de modification des données dans WITHsont exécutées exactement une fois, et
toujours jusqu'à la fin , indépendamment du fait que la requête principale lit tout (ou même n'importe lequel) de leur sortie. Notez que ceci est différent de la règle pour SELECTdans WITH: comme indiqué dans la section précédente, l'exécution de a SELECTn'est effectuée que dans la mesure où la requête principale demande sa sortie .
Accentuation sur moi. "Modification des données" sont INSERT, UPDATEet les DELETErequêtes. (Par opposition à SELECT.). Le manuel une fois de plus:
Vous pouvez utiliser des instructions de modification de données ( INSERT, UPDATEou DELETE) dans WITH.
Fonction appropriée
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;
J'ai supprimé les clauses par défaut (bruit) et
STRICTest 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 coinpossible, NULLje suggère:
SET coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
Si users.idest la clé primaire, ni , RETURNS TABLEni ROWs 1000aucun 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 RETURNINGclause 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ées SELECT * FROM ...si vous les ignorez de toute façon.
Il suffit de renvoyer une constante scalaire ( RETURNING 1), de définir la fonction comme RETURNS int(ou de la supprimer RETURNINGet de la créer RETURNS void) et de l'appeler avecSELECT my_function(...)
Solution
Depuis que vous ...
ne se soucient pas vraiment du résultat
.. juste SELECTune constante du CTE. Il est garanti d'être exécuté tant qu'il est référencé à l'extérieur SELECT(directement ou indirectement).
WITH test AS (SELECT __post_users_id_coin(10, 1))
SELECT 1 FROM test;
Si vous avez réellement une fonction de retour d'ensemble et que vous ne vous souciez toujours pas de la sortie:
WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
SELECT 1 FROM test LIMIT 1;
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: