Wie verwende ich mehrere WITH-Anweisungen in einer PostgreSQL-Abfrage?

94

Ich möchte mit der WITH-Anweisung "deklarieren", was effektiv mehrere TEMP-Tabellen sind. Die Abfrage, die ich ausführen möchte, lautet wie folgt:

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

Ich habe die PostgreSQL-Dokumentation gelesen und nach der Verwendung mehrerer WITHAnweisungen gesucht und konnte keine Antwort finden.

Greg
quelle
Versuchen Sie ein Komma vor der zweiten withAnweisung und alle anderen danach. Ich bin mir nicht sicher über Postgres, aber das ist die normale Syntax mit Oracle und SQL Server
msheikh25
Ich habe versucht, ein Komma und später ein Semikolon zu verwenden, und es gab immer noch Syntaxfehler: ERROR: syntax error at or near "WITH"für das Komma und ERROR: syntax error at or near ";"für das Semikolon.
Greg

Antworten:

154

Gemäß den anderen Kommentaren steht vor dem zweiten Common Table Expression [CTE] ein Komma und keine WITH-Anweisung

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

In Bezug auf Ihre eigentliche Abfrage sollte diese Syntax in PostgreSql, Oracle und SQL-Server funktionieren. Je später Sie normalerweise WITHmit einem Semikolon ( ;WTIH) fortfahren , dies liegt daran, dass SQL-Server-Leute (ich selbst eingeschlossen) normalerweise nicht enden frühere Aussagen, die beendet werden müssen, bevor ein CTE definiert wird ...

Beachten Sie jedoch, dass Sie in Bezug auf Ihre WHEREAnweisung ein zweites Syntaxproblem hatten . WHERE date IN table_2ist ungültig, da Sie niemals auf einen Wert / eine Spalte aus Tabelle_2 verweisen. Ich ziehe es INNER JOINüber INoder Existsso ist hier eine Syntax , die mit einem funktionieren sollte 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
;

Wenn Sie so bleiben möchten, wie Sie es hatten, was normalerweise EXISTS besser ist als IN, aber um IN verwenden zu können, benötigen Sie eine tatsächliche SELECT-Anweisung in Ihrem Where.

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

IN ist sehr problematisch, wenn datedies möglicherweise der Fall NULLsein könnte. Wenn Sie kein verwenden möchten, JOINwürde ich vorschlagen EXISTS. Wie folgt:

SELECT * 
FROM
     table_1 t1
WHERE EXISTS (SELECT * FROM table_2 t2 WHERE t2.date = t1.date);
Matt
quelle
2
Vielen Dank für die ausführliche Erklärung, die Syntax hat funktioniert :)
Greg
froh, dass ich Helfen kann. Ich kann den Artikel über die Nichtverwendung von IN nicht finden, würde jedoch dringend empfehlen, JOIN oder EXISTS over IN zu verwenden. Wenn in Ihrer Ergebnismenge eine Null vorhanden ist, erhalten Sie jeden Datensatz, nicht nur die gewünschten. Es ist komisch, aber so funktionieren die meisten RDBMs. Versuchen Sie eine Suche zu überprüfen, ich weiß, dass die gute Antwort, die ich darüber gesehen habe, auch auf dieser Seite war ... trotzdem, gute Nacht
Matt
5

Sie können Ihre Ergebnisse auch mit der WITH-Anweisung verketten. Z.B:

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
quelle