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 WITH
sont 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 SELECT
dans WITH
: comme indiqué dans la section précédente, l'exécution de a SELECT
n'est effectuée que dans la mesure où la requête principale demande sa sortie .
Accentuation sur moi. "Modification des données" sont INSERT
, UPDATE
et les DELETE
requêtes. (Par opposition à SELECT
.). Le manuel une fois de plus:
Vous pouvez utiliser des instructions de modification de données ( INSERT
, UPDATE
ou 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
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:
SET coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
Si users.id
est la clé primaire, ni , RETURNS TABLE
ni ROWs 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é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 RETURNING
et 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 SELECT
une 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: