จะใช้หลายคำสั่ง WITH ในแบบสอบถาม PostgreSQL ได้อย่างไร?


105

ฉันต้องการ "ประกาศ" ตาราง TEMP หลายตารางที่มีประสิทธิภาพโดยใช้คำสั่ง WITH ข้อความค้นหาที่ฉันพยายามดำเนินการอยู่ในแนวของ:

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

ฉันได้อ่านเอกสาร PostgreSQLและค้นคว้าเกี่ยวกับการใช้WITHคำสั่งหลาย ๆคำสั่งและไม่สามารถหาคำตอบได้


ลองใช้เครื่องหมายจุลภาคก่อนwithคำสั่งที่สองหลังจากนั้น ไม่แน่ใจเกี่ยวกับ postgres แต่เป็นไวยากรณ์ปกติของเซิร์ฟเวอร์ Oracle และ sql
msheikh25

ฉันลองใช้เครื่องหมายจุลภาคและต่อมาเป็นอัฒภาคและยังคงมีข้อผิดพลาดทางไวยากรณ์: ERROR: syntax error at or near "WITH"สำหรับเครื่องหมายจุลภาคและERROR: syntax error at or near ";"สำหรับอัฒภาค
Greg

คำตอบ:


168

ตามความคิดเห็นอื่น ๆ Common Table Expression [CTE] ที่สองนำหน้าด้วยลูกน้ำไม่ใช่คำสั่ง WITH ดังนั้น

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

ในแง่ของการค้นหาจริงของคุณไวยากรณ์นี้ควรใช้งานได้ใน PostgreSql, Oracle และ sql-server โดยปกติแล้วคุณจะดำเนินการต่อWITHด้วยอัฒภาค ( ;WTIH) แต่นั่นเป็นเพราะโดยทั่วไปแล้ว sql-server folks (รวมตัวเองด้วย) ไม่จบ ข้อความก่อนหน้าซึ่งจำเป็นต้องสิ้นสุดก่อนที่จะมีการกำหนด CTE ...

อย่างไรก็ตามโปรดทราบว่าคุณมีปัญหาทางไวยากรณ์ที่สองเกี่ยวกับWHEREคำสั่ง ของคุณ WHERE date IN table_2ไม่ถูกต้องเนื่องจากคุณไม่เคยอ้างอิงค่า / คอลัมน์จาก table_2 ฉันชอบINNER JOINมากกว่าINหรือExistsดังนั้นนี่คือไวยากรณ์ที่ควรจะทำงานกับที่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
;

หากคุณต้องการรักษาวิธีที่คุณมีซึ่งโดยทั่วไปแล้ว EXISTS จะดีกว่า IN แต่ในการใช้ IN คุณต้องมีคำสั่ง SELECT จริงในตำแหน่งของคุณ

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

ในเป็นปัญหามากเมื่อdateอาจจะเป็นNULLดังนั้นหากคุณไม่ต้องการที่จะใช้แล้วผมจะแนะนำJOIN EXISTSดังต่อไปนี้:

SELECT * 
FROM
     table_1 t1
WHERE EXISTS (SELECT * FROM table_2 t2 WHERE t2.date = t1.date);

ยินดีที่ได้ช่วยเหลือ ฉันไม่พบบทความเกี่ยวกับการไม่ใช้ IN แต่ฉันขอแนะนำอย่างยิ่งให้ใช้ JOIN หรือ EXISTS over IN หากมีค่าว่างในผลลัพธ์ของคุณตั้งค่าสิ่งที่เกิดขึ้นคือคุณจะได้รับทุกระเบียนไม่ใช่เฉพาะรายการที่คุณต้องการ มันแปลก แต่เป็นวิธีที่ RDBM ส่วนใหญ่ทำงาน ลองตรวจสอบการค้นหาฉันรู้ว่าคำตอบที่ดีที่ฉันเห็นเกี่ยวกับเรื่องนี้ก็อยู่ในเว็บไซต์นี้เช่นกัน ... ขอให้
Matt

8

คุณยังสามารถเชื่อมโยงผลลัพธ์ของคุณโดยใช้คำสั่ง WITH เช่น:

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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.