Ciò è previsto, comportamento documentato.
Tom Lane lo spiega qui.
Documentato nel manuale qui:
Le istruzioni che modificano i dati in WITH
vengono 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 SELECT
in WITH
: come indicato nella sezione precedente, l'esecuzione di a SELECT
viene eseguita solo nella misura in cui la query primaria richiede il suo output .
Enorme enfasi sulla mia. "Data modificanti" sono INSERT
, UPDATE
e DELETE
le 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 coin
possibile, NULL
suggerisco:
SET coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
Se users.id
è la chiave primaria, allora né ha RETURNS TABLE
né ROWs 1000
senso. È possibile aggiornare / restituire una sola riga. Ma questo è tutto a parte il punto principale.
Chiamata corretta
Non ha senso utilizzare la RETURNING
clausola 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 RETURNING
tutto e creala RETURNS void
) e chiamala conSELECT my_function(...)
Soluzione
Dal momento che tu ...
non importa davvero del risultato
.. solo SELECT
una 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: