ความแตกต่างระหว่างมุมมองอินไลน์และด้วยคำสั่งย่อย?


9

มุมมองอินไลน์ช่วยให้คุณสามารถเลือกจากแบบสอบถามย่อยราวกับว่ามันเป็นตารางที่แตกต่างกัน:

SELECT
    *
FROM /* Selecting from a query instead of table */
    (
        SELECT
            c1
        FROM
            t1
        WHERE
            c1 > 0
    ) a
WHERE
    a.c1 < 50;

ฉันเคยเห็นสิ่งนี้อ้างถึงโดยใช้คำต่าง ๆ : มุมมองอินไลน์, พร้อมด้วยข้อ, CTE และตารางที่ได้รับ สำหรับฉันดูเหมือนว่าพวกเขาเป็นไวยากรณ์เฉพาะของผู้ขายที่แตกต่างกันสำหรับสิ่งเดียวกัน

นี่เป็นข้อสันนิษฐานที่ผิดหรือเปล่า? มีความแตกต่างทางเทคนิค / ประสิทธิภาพระหว่างสิ่งเหล่านี้หรือไม่?


5
ชื่อ "เป็นทางการ" จาก SQL มาตรฐานคือตาราง Derived (ซึ่ง Oracle ตั้งชื่อมุมมองแบบอินไลน์ ) และนิพจน์ตารางทั่วไป (= WITH...) คุณสามารถเขียนตารางที่ได้รับทั้งหมดเป็น CTE แต่อาจไม่ใช่วิธีอื่น ๆ ในรอบ (เช่น Recursive CTE หรือใช้ CTE ซ้ำหลายครั้ง)
dnoeth

คำตอบ:


8

มีความแตกต่างที่สำคัญระหว่างมุมมองแบบอินไลน์ (ตารางที่ได้รับ) และ WITH clause (CTE) ใน Oracle บางส่วนนั้นค่อนข้างเป็นสากลเช่นสามารถใช้กับ RDBMS อื่นได้

  1. WITH สามารถใช้ในการสร้างแบบสอบถามย่อย recursive มุมมองแบบอินไลน์ - ไม่ (เท่าที่ฉันรู้เหมือนกันสำหรับ RDBMS ทั้งหมดที่สนับสนุน CTE)
  2. แบบสอบถามย่อยในWITHประโยคมีแนวโน้มที่จะถูกดำเนินการทางกายภาพก่อน ในหลายกรณีการเลือกระหว่างWITHและมุมมองแบบอินไลน์ทำให้เครื่องมือเพิ่มประสิทธิภาพเลือกแผนการดำเนินการที่แตกต่างกัน (ฉันเดาว่าเป็นผู้ขายเฉพาะหรืออาจเป็นเวอร์ชั่นเฉพาะ)
  3. แบบสอบถามย่อยWITHสามารถปรากฏเป็นตารางชั่วคราวได้ (ฉันไม่ทราบว่าผู้ขายรายอื่น ๆ แต่ Oracle สนับสนุนคุณสมบัตินี้)
  4. แบบสอบถามย่อยWITHสามารถอ้างอิงได้หลายครั้งในแบบสอบถามย่อยอื่น ๆ และในแบบสอบถามหลัก (จริงสำหรับ RDBMS ส่วนใหญ่)

MySQL (อย่างน้อยรุ่น MariaDB ล่าสุด) สามารถสร้างตารางที่ได้รับ (และแม้แต่เพิ่มดัชนี)
ypercubeᵀᴹ

3
ฉันต้องการเพิ่มสิ่งนั้นเพื่อเป็นประโยชน์ด้านการใช้ CTE นั้นโดยทั่วไปสามารถอ่านได้สำหรับมนุษย์เช่นกัน
Joishi Bodio

@JoishiBodio: โดยส่วนตัวแล้วฉันเห็นด้วยกับคุณ แต่การอ่านเป็นเรื่องส่วนตัว ฉันควรหลีกเลี่ยงการพูดถึงมัน
a1ex07

นอกจากนี้ CTE สามารถอ้างอิง CTE ที่ประกาศก่อนหน้านี้ ตารางที่ได้รับไม่สามารถอ้างอิงตารางที่ได้รับการประกาศก่อนหน้านี้ที่ระดับเดียวกันเว้นแต่LATERALจะใช้
Lennart

8

คำตอบอื่น ๆ ครอบคลุมถึงความแตกต่างของไวยากรณ์ค่อนข้างดีดังนั้นฉันจะไม่พูดเรื่องนั้น คำตอบนี้จะครอบคลุมถึงประสิทธิภาพใน Oracle

เครื่องมือเพิ่มประสิทธิภาพ Oracle อาจเลือกให้ผลลัพธ์ของ CTE เป็นจริงในตารางชั่วคราวภายใน มันใช้ฮิวริสติกในการทำเช่นนี้แทนการปรับให้เหมาะสมกับต้นทุน ฮิวริสติกคือ "การทำให้เป็นรูปธรรมของ CTE หากไม่ใช่นิพจน์ที่ไม่สำคัญและ CTE นั้นถูกอ้างอิงมากกว่าหนึ่งครั้งในการสืบค้น" มีข้อซักถามบางประการที่การทำให้เป็นรูปธรรมจะปรับปรุงประสิทธิภาพเป็น มีคำถามบางอย่างที่ทำให้เป็นรูปเป็นร่างจะลดประสิทธิภาพลงอย่างมาก ตัวอย่างต่อไปนี้เป็นบิตที่วางแผนไว้ แต่มันแสดงให้เห็นถึงจุดที่ดี:

ขั้นแรกสร้างตารางด้วยคีย์หลักที่มีจำนวนเต็มตั้งแต่ 1 ถึง 10,000:

CREATE TABLE N_10000 (NUM_ID INTEGER NOT NULL, PRIMARY KEY (NUM_ID));

INSERT /*+APPEND */ INTO N_10000
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 10000
ORDER BY LEVEL;

COMMIT;

พิจารณาแบบสอบถามต่อไปนี้ที่ใช้สองตารางที่ได้รับ:

SELECT t1.NUM_ID
FROM 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t1
LEFT OUTER JOIN 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

เราสามารถดูคำถามนี้และกำหนดได้อย่างรวดเร็วว่าจะไม่ส่งคืนแถวใด ๆ Oracle ควรจะสามารถใช้ดัชนีเพื่อพิจารณาเช่นกัน ในเครื่องของฉันแบบสอบถามเกือบเสร็จในทันทีด้วยแผนต่อไปนี้:

แผนการที่ดี

ฉันไม่ชอบทำซ้ำตัวเองดังนั้นลองทำแบบสอบถามเดียวกันกับ CTE:

WITH N_10000_CTE AS (
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

นี่คือแผน:

แผนไม่ดี

นั่นเป็นแผนที่เลวร้ายจริงๆ แทนที่จะใช้ดัชนี Oracle จะกำหนดขนาดของแถวที่ 10,000 X 10,000 = 100000000 ลงในตารางชั่วคราวเท่านั้นในที่สุดก็จะกลับมาเป็น 0 แถว ค่าใช้จ่ายของแผนนี้ประมาณ 6 M ซึ่งสูงกว่าแบบสอบถามอื่น ๆ มาก การสืบค้นใช้เวลา 68 วินาทีในการทำให้เครื่องของฉันเสร็จ

โปรดทราบว่าแบบสอบถามอาจล้มเหลวหากมีหน่วยความจำไม่เพียงพอหรือพื้นที่ว่างในพื้นที่ตารางชั่วคราว

ฉันสามารถใช้INLINEคำใบ้ที่ไม่มีเอกสารเพื่อไม่อนุญาตให้เครื่องมือเพิ่มประสิทธิภาพจากการทำให้ CTE เป็นจริง:

WITH N_10000_CTE AS (
  SELECT /*+ INLINE */ n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

แบบสอบถามนั้นสามารถใช้ดัชนีและเสร็จสิ้นเกือบจะทันที ค่าใช้จ่ายของการสืบค้นเป็นเหมือนก่อนหน้า 11 ดังนั้นสำหรับการสืบค้นครั้งที่สองการวิเคราะห์พฤติกรรมที่ใช้โดย Oracle ส่งผลให้เกิดการเลือกการสืบค้นด้วยค่าใช้จ่ายโดยประมาณที่ 6 M แทนที่จะเป็นแบบสอบถามที่มีค่าใช้จ่ายประมาณ 11


1

สำหรับ SQL Server ให้WITH CTEระบุชุดผลลัพธ์ชั่วคราวที่มีชื่อ แต่จำเป็นสำหรับชุดแรกCTEเท่านั้น กล่าวคือ

WITH CTE AS (SELECT .... FROM), 
CTE2 AS (SELECT .... FROM)

SELECT CTE.Column, CTE2.Column
FROM CTE
INNER JOIN CTE2 on CTE.Column = CTE2.Column

แต่นี่ไม่ใช่แบบสอบถามย่อยหรือแบบสอบถามย่อยที่เกี่ยวข้อง มีสิ่งที่คุณสามารถทำกับ CTE สิ่งที่คุณไม่สามารถทำกับแบบสอบถามย่อยใน SQL Server เช่นปรับปรุงตารางอ้างอิงใน CTE นี่คือตัวอย่างของการอัพเดตตารางด้วย CTE

แบบสอบถามย่อยจะเป็นอย่างไร

SELECT
   C1,
   (SELECT C2 FROM SomeTable) as C2
FROM Table

หรือแบบสอบถามย่อยที่สัมพันธ์กันคือสิ่งที่คุณได้ให้ไว้ใน OP ของคุณหากคุณต้องการอ้างอิง / เข้าร่วม / จำกัด ผลลัพธ์ของคุณโดยอ้างอิงจาก a1

ดังนั้นพวกเขาจะไม่เหมือนกันอย่างแน่นอน แต่ในหลายกรณีคุณสามารถใช้วิธีการเหล่านี้อย่างน้อยหนึ่งวิธีเพื่อให้ได้ผลลัพธ์เดียวกัน มันขึ้นอยู่กับผลลัพธ์ที่ได้นั้นคืออะไร


1

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

ข้อมูลเพิ่มเติมสามารถพบได้ที่นี่และที่นี่


โดยทั่วไปคำแนะนำที่เป็นรูปธรรมไม่จำเป็นต้องใช้ โดยค่าเริ่มต้นของออราเคิลเพิ่มประสิทธิภาพการตัดสินใจว่าจะทำให้ความรู้สึกที่เป็นรูปธรรม CTE หรือไม่ - แต่คุณอาจเขียนทับการประเมินผลการเพิ่มประสิทธิภาพกับคำใบ้MATERIALIZEรับผิดชอบ INLINEสำหรับสิ่งที่ตรงกันข้าม
Wernfried Domscheit

@WernfriedDomscheit ที่เป็นจริง แต่บางครั้งเครื่องมือเพิ่มประสิทธิภาพไม่เลือกที่จะทำให้ CTE เป็นจริงและในกรณีนั้นการใช้materializeคำใบ้เป็นตัวเลือกที่ถูกต้อง บางครั้งฉันจำเป็นต้องระบุเมื่อเพิ่มประสิทธิภาพการสืบค้นที่ซับซ้อนมากซึ่งฉันรู้ว่าการทำให้ CTE เป็นจริงจะเป็นประโยชน์ต่อแผนการดำเนินการ
Marko Vodopija

0

คุณต้องระวังด้วย CTE ในเซิร์ฟเวอร์ SQL ไม่ใช่แค่ oracle แต่มีหลายกรณีที่การสืบค้นมีประสิทธิภาพที่แย่กว่ามากเมื่อใช้ CTE เปรียบเทียบกับเคียวรีย่อย, cross Apply และอื่น ๆ

เช่นเคยมันเป็นสิ่งสำคัญในการทดสอบแบบสอบถามใด ๆ ภายใต้เงื่อนไขการโหลดที่แตกต่างกันเพื่อตรวจสอบที่ดีที่สุด

คล้ายกับ @scsimon with oracle บางครั้งเซิร์ฟเวอร์ MS SQL จะไม่ทำสิ่งที่คุณคาดหวังเกี่ยวกับการใช้ดัชนี

หากคุณจะใช้ข้อมูลเดียวกันมากกว่าหนึ่งครั้ง CTE จะมีประโยชน์มากขึ้นหากคุณใช้เพียงครั้งเดียวบ่อยครั้งที่แบบสอบถามย่อยจะเร็วกว่าในชุดข้อมูลขนาดใหญ่

เช่น select * from (แบบสอบถามย่อยของฉัน) เข้าร่วมอย่างอื่น ...

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