Como usar várias instruções WITH em uma consulta PostgreSQL?

105

Eu gostaria de "declarar" o que são efetivamente várias tabelas TEMP usando a instrução WITH. A consulta que estou tentando executar segue as linhas 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

Eu li a documentação do PostgreSQL e pesquisei o uso de várias WITHinstruções e não consegui encontrar uma resposta.

Greg
fonte
Experimente uma vírgula antes da segunda withafirmação, qualquer outra depois. Não tenho certeza sobre o postgres, mas essa é a sintaxe normal com Oracle e sql server
msheikh25 01 de
Tentei usar uma vírgula e depois um ponto e vírgula e ainda havia erros de sintaxe: ERROR: syntax error at or near "WITH"para a vírgula e ERROR: syntax error at or near ";"para o ponto e vírgula.
Greg

Respostas:

167

De acordo com os outros comentários, a segunda Expressão de Tabela Comum [CTE] é precedida por uma vírgula, não por uma instrução WITH, portanto

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

Em termos de sua consulta real, esta sintaxe deve funcionar em PostgreSql, Oracle e sql-server, bem o último, normalmente, você continuará WITHcom um ponto-e-vírgula ( ;WTIH), mas isso é porque normalmente o pessoal do sql-server (eu incluído) não termina declarações anteriores que precisam ser encerradas antes de um CTE ser definido ...

Observe, entretanto, que você teve um segundo problema de sintaxe em relação à sua WHEREinstrução. WHERE date IN table_2não é válido porque você nunca faz referência a um valor / coluna da tabela_2. Eu prefiro INNER JOINmais INou Existsmenos aqui está uma sintaxe que deve funcionar com um 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
;

Se você quiser manter a forma que você tinha, que normalmente EXISTS seria melhor do que IN, mas para usar IN você precisa de uma instrução SELECT real em seu where.

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

IN é muito problemático quando datepotencialmente poderia ser NULLse você não quiser usar um, JOINentão eu sugiro EXISTS. Do seguinte modo:

SELECT * 
FROM
     table_1 t1
WHERE EXISTS (SELECT * FROM table_2 t2 WHERE t2.date = t1.date);
Matt
fonte
feliz em ajudar. Não consigo encontrar o artigo sobre não usar IN, mas sugiro fortemente o uso de JOIN ou EXISTS em vez de IN. Se existir um nulo em seu conjunto de resultados, o que acontece é que você obterá todos os registros, não apenas os que deseja. É estranho, mas é a forma como a maioria dos RDBMs funciona. tente verificar uma pesquisa sobre ele, eu sei que a boa resposta que vi sobre isso também estava neste site ... de qualquer maneira, tenha uma boa noite
Matt
8

Você também pode encadear seus resultados usando a instrução WITH. Por exemplo:

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
fonte