¿Cómo usar varias declaraciones WITH en una consulta de PostgreSQL?

96

Me gustaría "declarar" cuáles son efectivamente múltiples tablas TEMP usando la instrucción WITH. La consulta que estoy tratando de ejecutar está en las líneas de:

WITH table_1 AS (
SELECT GENERATE_SERIES('2012-06-29', '2012-07-03', '1 day'::INTERVAL) AS date
)

WITH table_2 AS (
SELECT GENERATE_SERIES('2012-06-30', '2012-07-13', '1 day'::INTERVAL) AS date
)

SELECT * FROM table_1
WHERE date IN table_2

Leí la documentación de PostgreSQL e investigué sobre el uso de varias WITHdeclaraciones y no pude encontrar una respuesta.

Greg
fuente
Pruebe con una coma antes de la segunda withdeclaración y cualquier otra después. No estoy seguro acerca de Postgres, pero esa es la sintaxis normal con Oracle y SQL Server
msheikh25
Intenté usar una coma y luego un punto y coma y todavía había errores de sintaxis: ERROR: syntax error at or near "WITH"para la coma y ERROR: syntax error at or near ";"para el punto y coma.
Greg

Respuestas:

155

Según los otros comentarios, la segunda expresión de tabla común [CTE] está precedida por una coma, no una declaración WITH, por lo que

WITH cte1 AS (SELECT...)
, cte2 AS (SELECT...)
SELECT *
FROM
    cte1 c1
    INNER JOIN cte2 c2
    ON ........

En términos de su consulta real, esta sintaxis debería funcionar en PostgreSql, Oracle y sql-server, bueno, lo más tarde, por lo general, procederá WITHcon un punto y coma ( ;WTIH), pero eso se debe a que, por lo general, la gente del servidor sql (incluido yo mismo) no termina declaraciones anteriores que deben finalizar antes de que se defina un CTE ...

Sin embargo, tenga en cuenta que tuvo un segundo problema de sintaxis con respecto a su WHEREdeclaración. WHERE date IN table_2no es válido porque nunca hace referencia a un valor / columna de table_2. Prefiero INNER JOINmás INo Existsmenos aquí hay una sintaxis que debería funcionar con JOIN:

WITH table_1 AS (
SELECT GENERATE_SERIES('2012-06-29', '2012-07-03', '1 day'::INTERVAL) AS date
)

, table_2 AS (
SELECT GENERATE_SERIES('2012-06-30', '2012-07-13', '1 day'::INTERVAL) AS date
)

SELECT * 
FROM
     table_1 t1
     INNER JOIN 
     table_2 t2
     ON t1.date = t2.date
;

Si desea mantener la forma en que lo tenía, que normalmente EXISTS sería mejor que IN, pero para usar IN, necesita una declaración SELECT real en su where.

SELECT * 
FROM
     table_1 t1
WHERE t1.date IN (SELECT date FROM table_2);

IN es muy problemático cuando datepodría serlo, NULLasí que si no quieres usar un JOIN, te sugiero EXISTS. Como sigue:

SELECT * 
FROM
     table_1 t1
WHERE EXISTS (SELECT * FROM table_2 t2 WHERE t2.date = t1.date);
Mate
fuente
2
Gracias por la explicación detallada, la sintaxis funcionó :)
Greg
encantado de ayudar. No puedo encontrar el artículo sobre no usar IN, pero sugiero encarecidamente usar JOIN o EXISTS sobre IN. Si existe un nulo en su conjunto de resultados, lo que sucede es que obtendrá todos los registros, no solo los que desea. Es extraño, pero es la forma en que funcionan la mayoría de RDBM. intente verificar una búsqueda, sé que la buena respuesta que vi sobre eso también estaba en este sitio ... de todos modos, que tenga una buena noche
Matt
5

También puede encadenar sus resultados utilizando la declaración WITH. P.ej:

WITH tab1 as (Your SQL statement),
tab2 as ( SELECT ... FROM tab1 WHERE your filter),
tab3 as ( SELECT ... FROM tab2 WHERE your filter)
SELECT * FROM tab3;
Suz'l Shrestha
fuente