Esto se espera, comportamiento documentado.
Tom Lane lo explica aquí.
Documentado en el manual aquí:
Declaraciones de datos modificadores en WITH
se ejecutan exactamente una vez, y
siempre hasta el final , independientemente de si la consulta primaria lee todos (o cualquier) de su producción. Tenga en cuenta que esto es diferente de la regla para SELECT
in WITH
: como se indicó en la sección anterior, la ejecución de SELECT
a solo se lleva a cabo hasta que la consulta primaria exija su salida .
El énfasis audaz es mío. "Datos modificadores" son INSERT
, UPDATE
y DELETE
consultas. (A diferencia de SELECT
). El manual una vez más:
Puede utilizar las declaraciones de datos modificadores ( INSERT
, UPDATE
o DELETE
) en WITH
.
Función adecuada
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;
Eliminé las cláusulas predeterminadas (ruido) y
STRICT
es el sinónimo corto deRETURNS NULL ON NULL INPUT
.
Asegúrese de alguna manera de que los nombres de los parámetros no entren en conflicto con los nombres de las columnas. Prefiero _
, pero esa es solo mi preferencia personal.
Si coin
puede ser NULL
sugiero:
SET coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
Si users.id
es la clave principal, entonces RETURNS TABLE
ni ROWs 1000
tiene sentido. Solo se puede actualizar / devolver una sola fila. Pero eso está al lado del punto principal.
Llamada adecuada
No tiene sentido usar la RETURNING
cláusula y devolver los valores de su función si va a ignorar los valores devueltos en la llamada de todos modos. Tampoco tiene sentido descomponer filas devueltas SELECT * FROM ...
si las ignora de todos modos.
Simplemente devuelva una constante escalar ( RETURNING 1
), defina la función como RETURNS int
(o suelte por RETURNING
completo y hágala RETURNS void
) y llámela conSELECT my_function(...)
Solución
Desde que tu ...
realmente no me importa el resultado
.. solo SELECT
una forma constante del CTE. Se garantiza que se ejecutará siempre que se haga referencia en el exterior SELECT
(directa o indirectamente).
WITH test AS (SELECT __post_users_id_coin(10, 1))
SELECT 1 FROM test;
Si realmente tiene una función de retorno de conjunto y aún no le importa la salida:
WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
SELECT 1 FROM test LIMIT 1;
No es necesario devolver más de 1 fila. La función todavía se llama.
Finalmente, no está claro por qué necesita el CTE para empezar. Probablemente solo una prueba de concepto.
Estrechamente relacionada:
Respuesta relacionada sobre SO:
Y considere: