Bagaimana cara menggunakan beberapa pernyataan WITH dalam satu kueri PostgreSQL?

96

Saya ingin "mendeklarasikan" apa yang secara efektif merupakan beberapa tabel TEMP menggunakan pernyataan WITH. Kueri yang saya coba jalankan berada di sepanjang baris:

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

Saya telah membaca dokumentasi PostgreSQL dan meneliti menggunakan beberapa WITHpernyataan dan tidak dapat menemukan jawabannya.

Greg
sumber
Cobalah tanda koma sebelum withpernyataan kedua dan tanda koma lainnya setelahnya. Tidak yakin tentang postgres tetapi itu sintaks normal dengan Oracle dan sql server
msheikh25
Saya mencoba menggunakan koma dan kemudian titik koma dan masih ada kesalahan sintaks: ERROR: syntax error at or near "WITH"untuk koma dan ERROR: syntax error at or near ";"untuk titik koma.
Greg

Jawaban:

155

Berdasarkan komentar lainnya, Common Table Expression [CTE] kedua diawali dengan koma, bukan pernyataan WITH

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

Dalam hal kueri Anda yang sebenarnya, sintaks ini harus berfungsi di PostgreSql, Oracle, dan sql-server, nah nanti biasanya Anda akan melanjutkan WITHdengan titik koma ( ;WTIH), tetapi itu karena biasanya orang-orang sql-server (termasuk saya sendiri) tidak berakhir pernyataan sebelumnya yang harus diakhiri sebelum CTE didefinisikan ...

Namun perlu dicatat bahwa Anda memiliki masalah sintaks kedua sehubungan dengan WHEREpernyataan Anda . WHERE date IN table_2tidak valid karena Anda sebenarnya tidak pernah mereferensikan nilai / kolom dari table_2. Saya lebih suka INNER JOINlebih INatau Existslebih di sini adalah sintaks yang harus bekerja dengan 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
;

Jika Anda ingin mempertahankan cara Anda memilikinya yang biasanya EXISTS akan lebih baik daripada IN tetapi untuk menggunakan IN Anda memerlukan pernyataan SELECT yang sebenarnya di tempat Anda.

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

IN sangat bermasalah ketika dateberpotensi NULLjadi jika Anda tidak ingin menggunakan JOINmaka saya sarankan EXISTS. Sebagai berikut:

SELECT * 
FROM
     table_1 t1
WHERE EXISTS (SELECT * FROM table_2 t2 WHERE t2.date = t1.date);
Matt
sumber
2
Terima kasih atas penjelasan mendalamnya, sintaksnya bekerja :)
Greg
senang untuk membantu. Saya tidak dapat menemukan artikel tentang tidak menggunakan IN tetapi saya sangat menyarankan menggunakan JOIN atau EXISTS over IN. Jika null ada di hasil Anda, apa yang terjadi adalah Anda akan mendapatkan setiap record tidak hanya yang Anda inginkan. Ini aneh tapi ini adalah cara kerja kebanyakan RDBM. coba periksa penelusurannya, saya tahu jawaban bagus yang saya lihat tentang itu ada di situs ini juga ... bagaimanapun, selamat malam
Matt
5

Anda juga dapat merangkai hasil Anda menggunakan pernyataan WITH. Misalnya:

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
sumber