Ciò è previsto, comportamento documentato.
Tom Lane lo spiega qui.
Documentato nel manuale qui:
Le istruzioni che modificano i dati in WITHvengono eseguite esattamente una volta e
sempre fino al completamento , indipendentemente dal fatto che la query primaria legga tutto (o effettivamente qualsiasi) del loro output. Si noti che questo è diverso dalla regola per SELECTin WITH: come indicato nella sezione precedente, l'esecuzione di a SELECTviene eseguita solo nella misura in cui la query primaria richiede il suo output .
Enorme enfasi sulla mia. "Data modificanti" sono INSERT, UPDATEe DELETEle query. (Al contrario di SELECT.). Il manuale ancora una volta:
È possibile utilizzare le istruzioni di dati modificanti ( INSERT, UPDATE, o DELETE) in WITH.
Funzione corretta
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;
Ho abbandonato le clausole predefinite (rumore) ed
STRICTè il breve sinonimo diRETURNS NULL ON NULL INPUT .
Assicurarsi in qualche modo che i nomi dei parametri non siano in conflitto con i nomi delle colonne. Ho anteposto _, ma questa è solo la mia preferenza personale.
Se coinpossibile, NULLsuggerisco:
SET coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
Se users.idè la chiave primaria, allora né ha RETURNS TABLEné ROWs 1000senso. È possibile aggiornare / restituire una sola riga. Ma questo è tutto a parte il punto principale.
Chiamata corretta
Non ha senso utilizzare la RETURNINGclausola e restituire i valori dalla propria funzione se si desidera ignorare comunque i valori restituiti nella chiamata. Inoltre, non ha senso scomporre le righe restituite SELECT * FROM ...se le ignori comunque.
Restituisci semplicemente una costante scalare ( RETURNING 1), definisci la funzione come RETURNS int(o rilasciala del RETURNINGtutto e creala RETURNS void) e chiamala conSELECT my_function(...)
Soluzione
Dal momento che tu ...
non importa davvero del risultato
.. solo SELECTuna forma costante del CTE. È garantito che venga eseguito purché sia referenziato all'esterno SELECT(direttamente o indirettamente).
WITH test AS (SELECT __post_users_id_coin(10, 1))
SELECT 1 FROM test;
Se in realtà hai una funzione di restituzione set e ancora non ti interessa l'output:
WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
SELECT 1 FROM test LIMIT 1;
Non è necessario restituire più di 1 riga. La funzione è ancora chiamata.
Infine, non è chiaro il motivo per cui è necessario il CTE per cominciare. Probabilmente solo una prova di concetto.
Strettamente correlato:
Risposta correlata su SO:
E considera: