Jak używać wielu instrukcji WITH w jednym zapytaniu PostgreSQL?

105

Chciałbym „zadeklarować”, czym jest efektywnie wiele tabel TEMP przy użyciu instrukcji WITH. Zapytanie, które próbuję wykonać, wygląda następująco:

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

Przeczytałem dokumentację PostgreSQL i zbadałem użycie wielu WITHinstrukcji i nie mogłem znaleźć odpowiedzi.

Greg
źródło
Spróbuj umieścić przecinek przed drugą withinstrukcją, a inne po niej. Nie jestem pewien co do postgres, ale to normalna składnia z serwerem Oracle i sql
msheikh25
Próbowałem użyć przecinka, a później średnika i nadal były błędy składniowe: ERROR: syntax error at or near "WITH"dla przecinka i ERROR: syntax error at or near ";"dla średnika.
Greg

Odpowiedzi:

167

Zgodnie z innymi komentarzami drugie wyrażenie Common Table Expression [CTE] jest poprzedzone przecinkiem, a nie instrukcją WITH, więc

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

Jeśli chodzi o twoje rzeczywiste zapytanie, ta składnia powinna działać w PostgreSql, Oracle i sql-server, cóż, później zwykle będziesz postępować WITHze średnikiem ( ;WTIH), ale to dlatego, że zazwyczaj ludzie z serwera sql (włącznie ze mną) nie kończą poprzednie oświadczenia, które należy zakończyć przed zdefiniowaniem CTE ...

Zwróć jednak uwagę, że wystąpił drugi problem ze składnią w odniesieniu do WHEREinstrukcji. WHERE date IN table_2jest nieprawidłowa, ponieważ w rzeczywistości nigdy nie odwołujesz się do wartości / kolumny z tabeli_2. Wolę INNER JOINnad INlub Existstak tutaj jest składnia, który powinien działać z 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
;

Jeśli chcesz zachować to, co miałeś, co zwykle EXISTS byłoby lepsze niż IN, ale aby użyć IN, potrzebujesz rzeczywistej instrukcji SELECT w miejscu gdzie.

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

IN jest bardzo problematyczne, kiedy datepotencjalnie może być, NULLwięc jeśli nie chcesz używać JOIN, to proponuję EXISTS. Następująco:

SELECT * 
FROM
     table_1 t1
WHERE EXISTS (SELECT * FROM table_2 t2 WHERE t2.date = t1.date);
Matt
źródło
miło, że mogłem pomóc. Nie mogę znaleźć artykułu o nieużywaniu IN, ale zdecydowanie sugeruję użycie JOIN lub EXISTS over IN. Jeśli w Twoim zbiorze wyników istnieje wartość null, to co się stanie, otrzymasz każdy rekord, a nie tylko te, które chcesz. To dziwne, ale tak działa większość RDBM. spróbuj sprawdzić wyszukiwanie, wiem, że dobra odpowiedź, którą widziałem, była również na tej stronie ... w każdym razie życzę dobrej nocy
Matt
8

Możesz również połączyć swoje wyniki za pomocą instrukcji WITH. Na przykład:

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
źródło