ทำไม MySQL อนุญาตให้มีการใช้นามแฝง SELECT?


14

ใน SQL เท่าที่ฉันรู้ลำดับการประมวลผลแบบสอบถามตรรกะซึ่งเป็นลำดับการตีความแนวคิดเริ่มต้นด้วย FROM ด้วยวิธีดังต่อไปนี้:

  1. จาก
  2. WHERE
  3. จัดกลุ่มตาม
  4. การมี
  5. เลือก
  6. สั่งโดย

การติดตามรายการนี้เป็นเรื่องง่ายที่จะดูว่าทำไมคุณไม่สามารถเลือกชื่อแทนในส่วนคำสั่ง WHERE เนื่องจากยังไม่ได้สร้างชื่อแทน T-SQL (SQL Server) ทำตามสิ่งนี้อย่างเคร่งครัดและคุณไม่สามารถใช้นามแฝง SELECT ได้จนกว่าคุณจะผ่าน SELECT

แต่ใน MySQL มันเป็นไปได้ที่จะใช้นามแฝง SELECT ในส่วนคำสั่ง HAVING แม้ว่ามันควรจะดำเนินการก่อน สิ่งนี้จะเป็นไปได้อย่างไร

เพื่อให้ตัวอย่าง:

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;

คำสั่งไม่ถูกต้องใน T-SQL (เนื่องจาก HAVING อ้างถึงนามแฝง SELECT Amount) ...

Msg 207, Level 16, State 1, Line 5
Invalid column name 'Amount'.

... แต่ใช้งานได้ดีใน MySQL

จากสิ่งนี้ฉันสงสัยว่า:

  • MySQL ใช้ช็อตคัตในกฎ SQL เพื่อช่วยผู้ใช้หรือไม่? อาจใช้การวิเคราะห์ล่วงหน้าบ้างไหม?
  • หรือว่า MySQL ใช้ลำดับการตีความแนวความคิดที่แตกต่างจากที่ฉันคิดว่า RDBMS ทั้งหมดกำลังติดตามอยู่?

1
ฉันเดาว่ามันเป็นสัญลักษณ์ที่สองของคุณ
a_horse_with_no_name

3
ฉันเดาว่ามันจะไม่ทำให้เกิดความกำกวมหรือความสับสนใด ๆ จนกว่าพวกเขาจะสนับสนุนฟังก์ชันการจัดอันดับ จากนั้นSELECT C, ROW_NUMBER() OVER (ORDER BY X) AS RN FROM T GROUP BY C HAVING RN = 1จะเป็นปัญหาในขณะที่ROW_NUMBERวิ่งหลังจากHAVING
มาร์ตินสมิ ธ

ฉันไม่แน่ใจว่าฟังก์ชั่นการจัดอันดับใดที่ MySQL รองรับ ถ้าคุณต้องการหมายเลขแถวคุณต้องสร้างมันด้วยวิธีนี้: SELECT @rownum:=@rownum + 1 as row .... บางทีเหตุผลที่พวกเขาสนับสนุนนามแฝง SELECT นั้นเป็นเพราะพวกเขาทำได้เพราะพวกเขาไม่สนับสนุนสิ่งที่จะทำให้มันเป็นไปไม่ได้ ... ใครจะไปรู้? :)
Ohlin

ตามที่ @MartinSmith อธิบายตราบใดที่ไม่มีฟังก์ชั่นหน้าต่าง / การจัดอันดับลำดับตรรกะของการดำเนินการสำหรับHAVINGและSELECTประโยคที่สามารถแลกเปลี่ยน SELECTดังนั้นมีความคลุมเครือในการทำเช่นนี้ไม่ได้และสามารถลดความซับซ้อนลักษณะของรหัสเมื่อมีการแสดงออกในมหึมา
ypercubeᵀᴹ

หวังว่านี่เป็นหัวข้อที่ค่อนข้างจะบอกว่าฉันตอบคำถามที่นี่ที่เพลิดเพลินกับผลลัพธ์ที่เร็วขึ้น (พร้อมdistincts) ... ด้วยการส่งออกAlias in the Havingเดียวกัน Explainดังนั้นการเปลี่ยนแปลงบางอย่างกับเครื่องมือเพิ่มประสิทธิภาพจึงเกิดขึ้น
Drew

คำตอบ:


13

เมื่อคุณมีคำถามเกี่ยวกับการเรียงลำดับนี้แหล่งข้อมูลที่ดีที่สุด IMHO คือเอกสารของ MySQL ตอนนี้ถึงจุด นี่คือพฤติกรรมของส่วนขยาย MySql GROUP BYซึ่งเปิดใช้งานโดยค่าเริ่มต้น

MySQL Extensions to GROUP BY
MySQL ขยายพฤติกรรมนี้เพื่ออนุญาตให้ใช้นามแฝงในส่วนคำสั่ง HAVING สำหรับคอลัมน์รวม

หากคุณต้องการพฤติกรรมมาตรฐานคุณสามารถปิดใช้งานส่วนขยายนี้ได้ sql_mode ONLY_FULL_GROUP_BY

SET [SESSION | GLOBAL] sql_mode = ONLY_FULL_GROUP_BY;

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

มีการใช้ฟิลด์ที่ไม่จัดกลุ่ม 'จำนวน' ในส่วนคำสั่ง HAVING: SELECT YEAR (orderdate), COUNT (*) เป็นจำนวนเงินจากกลุ่มคำสั่งซื้อตามปี (จำนวนวันที่สั่ง) จำนวน HAVING> 1

นี่คือตัวอย่างของSQLFiddle

ดังนั้นขึ้นอยู่กับคุณว่าจะกำหนดค่าและใช้งานอินสแตนซ์ของ MySQL อย่างไร


คุณพูดถูกเกี่ยวกับเอกสาร ผมก็ไม่เคยคิดว่ามันอาจจะเขียนอย่างชัดเจนในขณะที่คุณยกมาข้างต้นมัน :) ขอขอบคุณสำหรับการหามัน ...
โอห์ลิน

คำตอบนี้ไม่ตอบว่า "MySQL กำลังทำการวิเคราะห์ล่วงหน้าหรือว่า MySQL ใช้การตีความแนวความคิดที่ต่างออกไป"
Pacerier

2
@Pierier MySQL คือ "กำลังทำการวิเคราะห์ล่วงหน้า" เนื่องจากเครื่องมือเพิ่มประสิทธิภาพการสืบค้นจะพิจารณาทุกแง่มุมของการสืบค้นขณะที่เลือกสิ่งที่เชื่อว่าจะเป็นแผนการสืบค้นที่ดีที่สุด แนวคิดของ "การตีความแนวความคิดที่แตกต่าง" เป็นการบิดเบือนความจริงที่ว่าเซิร์ฟเวอร์มีอิสระที่จะใช้โมเดลแนวคิดในทุก ๆ ทางที่ก่อให้เกิดผลลัพธ์ที่ถูกต้อง ORDER BYตัวอย่างเช่นอาจได้รับการจัดการที่เร็วกว่าที่เป็นจริงในทางทฤษฎีถ้าเครื่องมือเพิ่มประสิทธิภาพพบว่าแถวสามารถอ่านได้ในลำดับแรกจากดัชนีที่อยู่ในลำดับที่ต้องการ
Michael - sqlbot

4

คำถามที่ดี.

ฉันคิดว่าคุณควรเรียกใช้แบบสอบถามเหล่านี้

EXPLAIN SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;
SHOW WARNINGS;

และตรวจสอบว่าแบบสอบถามถูกเขียนใหม่อย่างไร ฉันค่อนข้างมั่นใจว่าเครื่องมือเพิ่มประสิทธิภาพการค้นหาแทนที่จำนวนด้วย COUNT (*)

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING COUNT(*)>1;

เช่นเดียวกับที่ทำ

select 
 *
from 
 test
where 
 id = 5 - 3

หลังจากเครื่องมือเพิ่มประสิทธิภาพข้อความค้นหามีลักษณะเช่นนี้

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