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


16

ในขณะที่พยายามเขียนแบบสอบถามฉันพบ (วิธีที่ยาก) ที่ SQL Server แยกวิเคราะห์ WHEREs ในแบบสอบถามนานก่อนที่จะแยกวิเคราะห์ SELECT เมื่อดำเนินการแบบสอบถาม

เอกสาร MSDNกล่าวว่าเพื่อแยกทั่วไปตรรกะเป็นเช่นนั้น SELECT จะแยกกันเกือบที่ผ่านมา (จึงทำให้เกิดการ "ไม่มีวัตถุเช่น [นามแฝง]" ข้อผิดพลาดเมื่อพยายามที่จะใช้นามแฝงคอลัมน์ในข้ออื่น ๆ ) มีแม้กระทั่งข้อเสนอแนะเพื่อให้สามารถใช้นามแฝงได้ทุกที่ซึ่งทีมงานของ Microsoft อ้างถึงปัญหาการปฏิบัติตามมาตรฐาน ANSI (ซึ่งแสดงให้เห็นว่าพฤติกรรมนี้เป็นส่วนหนึ่งของมาตรฐาน ANSI)

ในฐานะโปรแกรมเมอร์ (ไม่ใช่ DBA) ฉันพบว่าพฤติกรรมนี้ค่อนข้างสับสนเนื่องจากดูเหมือนว่าฉันจะเอาชนะจุดประสงค์ของการมีชื่อแทนคอลัมน์เป็นส่วนใหญ่ (หรืออย่างน้อยที่สุดนามแฝงคอลัมน์อาจทำให้มีประสิทธิภาพมากขึ้นหากพวกเขาเป็น แยกวิเคราะห์ก่อนหน้านี้ในการดำเนินการค้นหา) เนื่องจากสถานที่เดียวที่คุณสามารถใช้นามแฝงที่แท้จริงคือในการสั่งซื้อ ในฐานะโปรแกรมเมอร์ดูเหมือนว่าจะไม่มีโอกาสมากสำหรับการทำแบบสอบถามที่มีประสิทธิภาพสะดวกและแห้งมากขึ้น

ดูเหมือนว่ามันเป็นปัญหาที่เห็นได้ชัดว่ามันมีเหตุผลแล้วว่ามีเหตุผลอื่น ๆ สำหรับการตัดสินใจว่าชื่อแทนคอลัมน์ไม่ควรได้รับอนุญาตในสิ่งอื่นใดนอกเหนือจาก SELECT และ ORDER BY แต่เหตุผลเหล่านั้นคืออะไร

คำตอบ:


19

สรุป

ไม่มีเหตุผลเชิงตรรกะที่ไม่สามารถทำได้ แต่ประโยชน์มีน้อยและมีข้อผิดพลาดบางอย่างที่อาจไม่ชัดเจนในทันที

ผลการวิจัย

ฉันค้นคว้าและพบข้อมูลที่ดี ต่อไปนี้เป็นคำพูดโดยตรงจากแหล่งข้อมูลหลักที่เชื่อถือได้ (ที่ประสงค์จะไม่เปิดเผยตัวตน) ที่ 2012-08-09 17:49 GMT:

เมื่อ SQL ถูกคิดค้นครั้งแรกไม่มีนามแฝงในส่วนคำสั่ง SELECT นี่เป็นข้อบกพร่องที่ร้ายแรงซึ่งแก้ไขเมื่อภาษานั้นได้มาตรฐานโดย ANSI ในปี 1986

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

select name, salary + bonus as pay
from employee
where pay > 100000

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

ฉันสนใจที่จะทำการวิจัยเพิ่มเติมเกี่ยวกับมาตรฐาน SQL-86 และทำไม DBMS สมัยใหม่จึงไม่สนับสนุนการใช้ชื่อแทน แต่ยังไม่มีเวลาที่จะไปไกล สำหรับผู้เริ่มฉันไม่รู้ว่าจะได้รับเอกสารหรือวิธีการตรวจสอบว่าใครทำขึ้นคณะกรรมการ ใครช่วยได้บ้าง ฉันต้องการทราบเพิ่มเติมเกี่ยวกับผลิตภัณฑ์ Sybase ดั้งเดิมที่ SQL Server มาจาก

จากการวิจัยนี้และความคิดเพิ่มเติมฉันสงสัยว่าการใช้นามแฝงในส่วนอื่น ๆ ในขณะที่เป็นไปได้ค่อนข้างไม่เคยมีความสำคัญสูงสำหรับผู้ผลิต DBMS เมื่อเทียบกับคุณสมบัติภาษาอื่น ๆ เนื่องจากมันไม่ได้เป็นอุปสรรคมากนักการทำงานอย่างง่าย ๆ โดยผู้เขียนแบบสอบถามการใช้ความพยายามมากกว่าความก้าวหน้าอื่น ๆ จึงไม่เหมาะสม นอกจากนี้มันจะเป็นกรรมสิทธิ์เนื่องจากเห็นได้ชัดว่าไม่ได้เป็นส่วนหนึ่งของมาตรฐาน SQL (แม้ว่าฉันจะรอดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้อย่างแน่นอน) และจะเป็นการปรับปรุงเล็กน้อย โดยการเปรียบเทียบCROSS APPLY(ซึ่งจริงๆแล้วไม่มีอะไรมากไปกว่าตารางที่ได้รับอนุญาตให้อ้างอิงจากภายนอก) เป็นการเปลี่ยนแปลงครั้งใหญ่ในขณะที่กรรมสิทธิ์มีพลังการแสดงออกที่เหลือเชื่อไม่สามารถทำได้อย่างง่ายดายในรูปแบบอื่น

ปัญหาเกี่ยวกับการใช้นามแฝงทุกที่

หากคุณอนุญาตให้เลือกรายการไว้ในส่วนคำสั่ง WHERE คุณจะไม่สามารถระเบิดความซับซ้อนของการสืบค้นได้ (และทำให้ความซับซ้อนในการค้นหาแผนการดำเนินการที่ดี) เป็นไปได้ที่จะเกิดสิ่งที่ไร้เหตุผลอย่างสมบูรณ์ ลอง:

SELECT X + 5 Y FROM MyTable WHERE Y = X

จะเกิดอะไรขึ้นถ้า MyTable มีคอลัมน์ Y อยู่แล้วส่วนไหนคือ WHERE clause ที่อ้างถึง? ทางออกคือการใช้ CTE หรือตารางที่ได้รับซึ่งโดยส่วนใหญ่แล้วจะไม่เสียค่าใช้จ่ายเพิ่มเติม แต่ได้ผลลัพธ์สุดท้ายที่เหมือนกัน CTEs และตารางที่ได้รับมาอย่างน้อยบังคับใช้การแก้ปัญหาความกำกวมโดยอนุญาตให้ใช้นามแฝงเพียงครั้งเดียว

นอกจากนี้การไม่ใช้นามแฝงในส่วนคำสั่ง FROM จะทำให้รู้สึกเด่นชัด คุณทำสิ่งนี้ไม่ได้:

SELECT
   T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
FROM
   Table1 T
   INNER JOIN Table2 T2
      ON T2.ID = CalcID
   INNER JOIN Table3 T3
      ON T2.ID = T3.ID

นั่นคือการอ้างอิงแบบวงกลม (ในแง่ที่ว่า T2 จะแอบหมายถึงค่าจาก T3 ก่อนตารางที่ได้รับการนำเสนอในเข้าร่วมรายการ) และยี้ยากที่จะเห็น แล้วอันนี้ละ:

INSERT dbo.FinalTransaction
SELECT
   newid() FinalTransactionGUID,
   'GUID is: ' + Convert(varchar(50), FinalTransactionGUID) TextGUID,
   T.*
FROM
   dbo.MyTable T

คุณต้องการวางเดิมพันเท่าไหร่ที่ฟังก์ชัน newid () จะถูกนำไปใส่ในแผนปฏิบัติการสองครั้งโดยสิ้นเชิงทำให้คอลัมน์ทั้งสองแสดงค่าต่างกันโดยไม่คาดคิด? สิ่งที่เกี่ยวกับเมื่อแบบสอบถามข้างต้นจะใช้ระดับ N ลึกใน CTEs หรือตารางที่ได้รับ ฉันรับประกันได้ว่าปัญหาจะเลวร้ายยิ่งกว่าที่คุณสามารถจินตนาการ นอกจากนี้แล้วปัญหาความไม่สอดคล้องกันอย่างจริงจังเกี่ยวกับเมื่อสิ่งที่ได้รับการประเมินเพียงครั้งเดียวหรือสิ่งที่จุดในแผนแบบสอบถามและไมโครซอฟท์ได้กล่าวว่าจะไม่แก้ไขบางส่วนของพวกเขาเพราะพวกเขาจะแสดงพีชคณิตแบบสอบถามอย่างถูกต้อง - หากได้รับผลลัพธ์ที่ไม่คาดคิดแบ่งแบบสอบถามออกเป็นส่วน การอนุญาตให้มีการอ้างอิงแบบโยงโซ่การตรวจสอบการอ้างอิงแบบวนผ่านโซ่ที่อาจยาวมากซึ่งเป็นปัญหาที่ค่อนข้างยุ่งยาก แนะนำการขนานและคุณมีฝันร้ายในการสร้าง

หมายเหตุ: การใช้นามแฝงใน WHERE หรือ GROUP BY จะไม่สร้างความแตกต่างให้กับปัญหาของฟังก์ชั่นเช่น newid () หรือ rand ()

วิธี SQL Server เพื่อสร้างนิพจน์ที่ใช้ซ้ำได้

CROSS APPLY / OUTER APPLY เป็นวิธีหนึ่งใน SQL Server ในการสร้างนิพจน์ที่สามารถใช้ที่ใดก็ได้ในแบบสอบถาม (ไม่ใช่ก่อนหน้าใน FROM clause):

SELECT
   X.CalcID
FROM
   Table1 T
   INNER JOIN Table3 T3
      ON T.ID = T3.ID
   CROSS APPLY (
      SELECT
         T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
   ) X
   INNER JOIN Table2 T2
      ON T2.ID = X.CalcID

สิ่งนี้ทำสองสิ่ง:

  1. ทำให้นิพจน์ทั้งหมดใน CROSS ใช้รับ "namespace" (นามแฝงของตารางที่นี่ X) และไม่ซ้ำกันภายในเนมสเปซนั้น
  2. ทำให้ชัดเจนทุกที่ไม่เพียง แต่ที่ CalcID มาจาก X แต่ยังทำให้เห็นได้ชัดว่าทำไมคุณไม่สามารถใช้อะไรจาก X เมื่อเข้าร่วมตารางที่ T1 และ T3 เพราะ X ยังไม่ได้เปิดตัว

จริง ๆ แล้วฉันชอบ CROSS มากกว่า มันได้กลายเป็นเพื่อนที่ซื่อสัตย์ของฉันและฉันใช้มันตลอดเวลา ต้องการ UNPIVOT บางส่วน (ซึ่งต้องใช้ PIVOT / UNPIVOT หรือ UNPIVOT / PIVOT โดยใช้ไวยากรณ์ดั้งเดิม) เสร็จสิ้นด้วย CROSS APPLY ต้องการค่าที่คำนวณได้ซึ่งจะนำมาใช้ซ้ำหลายครั้งหรือไม่ เสร็จสิ้น ต้องการบังคับใช้คำสั่งดำเนินการอย่างเข้มงวดสำหรับการโทรผ่านเซิร์ฟเวอร์ที่เชื่อมโยงหรือไม่ เสร็จแล้วด้วยการปรับปรุงความเร็วกรีดร้อง ต้องการแบ่งแถวประเภทเดียวเป็น 2 แถวหรือมีเงื่อนไขเพิ่มเติมหรือไม่ เสร็จสิ้น

ดังนั้นอย่างน้อยที่สุดใน DBMS SQL Server 2005 และสูงกว่าคุณไม่มีสาเหตุของการร้องเรียนอีกต่อไป: CROSS APPLY เป็นวิธีที่คุณแห้งในแบบที่คุณต้องการ


14

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

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

อย่างที่ทุกคนบอกว่าฉันจะทำซ้ำส่วนหนึ่งของคำตอบของฉันจากคำถามนี้:

/dba/19762/why-is-the-select-clause-listed-first


นี่คือคำอธิบายของ Joe Celko เกี่ยวกับวิธีการประมวลผลแบบสอบถามตามมาตรฐาน (ฉันขโมยมันจากบทความ aspfaq.com ของฉันเองซึ่งขโมยคำพูดอาจมาจากโพสต์กลุ่มข่าวสารโดย Celko):

นี่คือวิธีที่ SELECT ทำงานใน SQL ... อย่างน้อยก็ในทางทฤษฎี ผลิตภัณฑ์จริงจะปรับสิ่งต่าง ๆ ให้เหมาะสมเมื่อทำได้

เริ่มต้นในส่วนคำสั่ง FROM และสร้างตารางการทำงานจากตัวเชื่อมสหภาพแรงงานจุดตัดและเครื่องมือสร้างตารางอื่น ๆ ตัวเลือก AS อนุญาตให้คุณตั้งชื่อตารางการทำงานนี้ซึ่งคุณจะต้องใช้สำหรับแบบสอบถามที่เหลือ

ไปที่ส่วนคำสั่ง WHERE และลบแถวที่ไม่ผ่านเกณฑ์; นั่นคือที่ไม่ได้ทดสอบกับ TRUE (ปฏิเสธ UNKNOWN และ FALSE) ส่วนคำสั่ง WHERE จะใช้กับการทำงานในส่วนคำสั่ง FROM

ไปที่ข้อ GROUP GROUP ทางเลือกสร้างกลุ่มและลดแต่ละกลุ่มเป็นแถวเดียวแทนที่ตารางการทำงานเดิมด้วยตารางที่จัดกลุ่มใหม่ แถวของตารางที่จัดกลุ่มจะต้องมีลักษณะกลุ่ม: (1) คอลัมน์การจัดกลุ่ม (2) สถิติเกี่ยวกับกลุ่ม (เช่นฟังก์ชันรวม) (3) ฟังก์ชั่นหรือ (4) นิพจน์ที่สร้างขึ้นจากสามรายการเหล่านั้น

ไปที่ส่วนคำสั่ง HAVING ที่เป็นทางเลือกและใช้กับตารางการทำงานที่จัดกลุ่ม หากไม่มีข้อ GROUP BY ให้ถือว่าทั้งตารางเป็นกลุ่มเดียว

ไปที่ส่วนคำสั่ง SELECT และสร้างนิพจน์ในรายการ ซึ่งหมายความว่าเคียวรีย่อยสเกลาร์การเรียกใช้ฟังก์ชันและนิพจน์ใน SELECT นั้นเสร็จสิ้นหลังจากที่มีการทำส่วนคำสั่งอื่นทั้งหมด ตัวดำเนินการ AS สามารถตั้งชื่อให้กับนิพจน์ในรายการ SELECT ได้เช่นกัน ชื่อใหม่เหล่านี้มีอยู่ทั้งหมดในครั้งเดียว แต่หลังจากที่ประโยคถูกดำเนินการ; คุณไม่สามารถใช้พวกมันในรายการ SELECT หรือกลุ่ม WHERE ได้ด้วยเหตุผลนั้น

นิพจน์แบบสอบถามแบบซ้อนทำตามกฎการกำหนดขอบเขตปกติที่คุณคาดหวังจากภาษาที่มีโครงสร้างแบบบล็อกเช่น C, Pascal, Algol เป็นต้นโดยทั่วไปแล้วแบบสอบถามด้านในสุดสามารถอ้างอิงคอลัมน์และตารางในแบบสอบถามที่มีอยู่

ซึ่งหมายความว่า SELECT ไม่สามารถมีคอลัมน์ได้มากกว่า GROUP BY แต่มันสามารถมีคอลัมน์ได้น้อยลง

ตอนนี้ Celko เป็นหนึ่งในผู้สนับสนุนหลักของมาตรฐานรุ่นก่อนหน้านี้ ฉันไม่รู้ว่าคุณจะได้รับคำตอบที่ชัดเจนWHY?หรือไม่ยกเว้นการเก็งกำไร ฉันเดาว่าการแสดงรายการการดำเนินการจริงก่อนทำให้ง่ายต่อการแยกวิเคราะห์ที่จะรู้ว่าสิ่งที่ประเภทของการดำเนินการเป็นไปได้ ลองนึกภาพการเข้าร่วม 20- โต๊ะที่สามารถจบลงด้วยการเป็นSELECTหรือUPDATEหรือDELETEและจำไว้ว่ารหัสสำหรับเอ็นจิ้นเหล่านี้ถูกเขียนย้อนกลับไปในสมัยที่การแยกสตริงนั้นมีราคาค่อนข้างสูง

โปรดทราบว่าหากมาตรฐาน SQL กำหนดFROMให้มาก่อนผู้ขายอาจตัดสินใจแยกไวยากรณ์ในลำดับที่แตกต่างกันดังนั้นจึงอาจไม่เหมาะสมที่จะคาดเดาคำสั่งตามที่เขียนเพื่อปฏิบัติตามคำสั่งของการประมวลผล 100% ของ เวลา.

CASEเดียวกันเป็นจริงสำหรับสิ่งที่ต้องการ เราได้เห็นสถานการณ์ต่าง ๆ ที่นี่ในเว็บไซต์นี้ตัวอย่างเช่นตำนานที่เชื่อกันมาก่อนหน้านี้ซึ่งCASEดำเนินการตามลำดับและวงจรสั้นมักเป็นเท็จ และสิ่งนี้ขยายไปถึงความเชื่อทั่วไปอื่น ๆ เช่น SQL Server กำลังประเมินการรวมตามลำดับที่ถูกเขียนส่วนคำสั่งลัดวงจรสั้น ๆWHEREจากซ้ายไปขวาหรือประมวลผล CTE เพียงครั้งเดียวหรือตามลำดับที่แน่นอนแม้ว่าพวกเขาจะอ้างอิงหลายครั้ง ผลิตภัณฑ์มีอิสระที่จะปรับให้เหมาะกับวิธีที่พวกเขาเห็นว่าเหมาะสมแม้ว่ามันจะไม่สะท้อนถึงวิธีการที่คุณระบุว่าแบบสอบถามควรจะทำงานได้อย่างชัดเจน


2
โปรดทราบด้วยว่าความสามารถในการใช้หรือไม่ใช้นามแฝงในส่วนต่างๆของแบบสอบถามนั้นมีการบังคับใช้โดยตัวแยกวิเคราะห์ไม่ใช่โดยโปรแกรมเพิ่มประสิทธิภาพหรือตัวประมวลผล วิธีที่เอ็นจินเรียกใช้คิวรีจริงไม่จำเป็นต้องสะท้อนถึงข้อ จำกัด ที่มีผลต่อไวยากรณ์
Aaron Bertrand

2

ในEntity SQLคุณสามารถใช้นามแฝงจากนิพจน์ที่อื่นในแบบสอบถามในบางสถานการณ์:

select k1, count(t.a), sum(t.a)
from T as t
group by t.b + t.c as k1

โปรดทราบว่าที่นี่คุณต้องกำหนดนิพจน์ในส่วนGROUP BYคำสั่งเพื่อใช้ในส่วนSELECTคำสั่ง

เห็นได้ชัดว่าเป็นไปได้ที่จะอนุญาตให้มีการแสดงออกในนามแฝง - as-reuse-expression บางอย่างในแบบสอบถาม SQL

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