2012-01-04 27 views
13

Entiendo cómo usar la cláusula WITH para consultas recursivas (!!), pero tengo problemas para entender su uso/poder general.Guía sobre el uso de la cláusula WITH en SQL

Por ejemplo, la siguiente consulta actualiza un registro cuyo número se determina mediante el uso de una subconsulta devolver el identificador del primer disco de marca de tiempo:

update global.prospect psp 
set status=status||'*' 
where psp.psp_id=(
      select p2.psp_id 
      from global.prospect p2 
      where p2.status='new' or p2.status='reset' 
      order by p2.request_ts 
      limit 1) 
returning psp.*; 

¿Sería ésta una buena candidata para el uso de un contenedor en lugar de WITH ¿la subconsulta relativamente fea? Si es así, ¿por qué?

+0

De acuerdo con la documentación, el uso de 'CON [RECURSIVE]' encima de las instrucciones 'INSERT' y' UPDATE' se agregó en PostgreSQL 9.1. –

+0

@JoeyAdams - usando con dml - otra capa de la cebolla para entender –

Respuesta

18

Si puede haber acceso de escritura simultánea a tablas involucradas, hay condiciones de carrera en las siguientes consultas anteriormente. Considere:


Su ejemplo puede utilizar un CTE (expresión de tabla común), pero le dará nada una subconsulta no podía hacer:

WITH x AS (
    SELECT psp_id 
    FROM global.prospect 
    WHERE status IN ('new', 'reset') 
    ORDER BY request_ts 
    LIMIT 1 
    ) 
UPDATE global.prospect psp 
SET status = status || '*' 
FROM x 
WHERE psp.psp_id = x.psp_id 
RETURNING psp.*; 

BTW, la fila devuelta será la versión actualizada.


Si se desea introducir la fila devuelta en otra tabla, que es donde una cláusula WITH se convierte en esencial:

WITH x AS (
    SELECT psp_id 
    FROM global.prospect 
    WHERE status IN ('new', 'reset') 
    ORDER BY request_ts 
    LIMIT 1 
    ), y AS (
    UPDATE global.prospect psp 
    SET status = status || '*' 
    FROM x 
    WHERE psp.psp_id = x.psp_id 
    RETURNING psp.* 
    ) 
INSERT INTO z 
SELECT * 
FROM y 

datos de modificación de consultas utilizando CTE son posibles con PostgreSQL 9.1 o posterior.
Lee more in the excellent manual.

+0

guau - realmente bien - gracias. estoy de acuerdo con la calidad del documento pg, pero hasta la fecha los CTE han sido una de esas cosas que suenan bien cuando leo acerca de esto, pero en la práctica nunca he tenido un control sobre ello. tus dos ejemplos (¡creo!) han ayudado mucho –

9

WITH le permite definir "tablas temporales" para su uso en una consulta SELECT. Por ejemplo, hace poco escribí una consulta como esta, para calcular los cambios entre dos conjuntos:

-- Let o be the set of old things, and n be the set of new things. 
WITH o AS (SELECT * FROM things(OLD)), 
    n AS (SELECT * FROM things(NEW)) 

-- Select both the set of things whose value changed, 
-- and the set of things in the old set but not in the new set. 
SELECT o.key, n.value 
    FROM o 
    LEFT JOIN n ON o.key = n.key 
    WHERE o.value IS DISTINCT FROM n.value 

UNION ALL 

-- Select the set of things in the new set but not in the old set. 
SELECT n.key, n.value 
    FROM o 
    RIGHT JOIN n ON o.key = n.key 
    WHERE o.key IS NULL; 

Al definir las "tablas" o y n en la parte superior, que era capaz de evitar la repetición de las expresiones things(OLD) y things(NEW).

Claro, probablemente podríamos eliminar el UNION ALL usando un FULL JOIN, pero no pude hacer eso en mi caso particular.


Si entiendo su pregunta correctamente, hace esto:

  • buscar la fila más antigua de global.prospect cuyo estado es 'nuevo' o 'Reset'.

  • Marcos añadiendo un asterisco a su estado

  • devolver la fila (incluyendo nuestro pellizco a status).

No creo que WITH simplifique nada en su caso. Puede que sea un poco más elegante de utilizar una cláusula FROM, sin embargo:

update global.prospect psp 
set status = status || '*' 
from (select psp_id 
     from global.prospect 
     where status = 'new' or status = 'reset' 
     order by request_ts 
     limit 1 
     ) p2 
where psp.psp_id = p2.psp_id 
returning psp.*; 

No probado. Déjame saber si funciona.

Es más o menos exactamente lo que ya tiene, excepto:

  • Esto se puede ampliar fácilmente para actualizar varias filas. En su versión, que usa una expresión de subconsulta, la consulta fallaría si la subconsulta se cambiara para generar varias filas.

  • No alias global.prospect en la subconsulta, por lo que es un poco más fácil de leer. Dado que esto utiliza una cláusula FROM, obtendrá un error si hace referencia accidentalmente a la tabla que se está actualizando.

  • En su versión, la expresión de subconsulta se encuentra para cada elemento individual. Aunque PostgreSQL debería optimizar esto y solo evaluar la expresión una vez, esta optimización desaparecerá si accidentalmente hace referencia a una columna en psp o agrega una expresión volátil.

+0

Para actualizar varias filas, no se necesitaría ninguna forma de subconsulta o CTE, simplemente: 'UPDATE global.prospect SET status = status || '*' WHERE estado IN ('nuevo', 'restablecer') DEVOLUCIÓN *; ' –

Cuestiones relacionadas