Tại sao MySQL cho phép HAVING sử dụng bí danh CHỌN?


14

Trong SQL, theo như tôi biết, thứ tự xử lý truy vấn logic, là thứ tự diễn giải khái niệm, bắt đầu bằng TỪ theo cách sau:

  1. TỪ
  2. Ở ĐÂU
  3. NHÓM THEO
  4. ĐANG CÓ
  5. LỰA CHỌN
  6. ĐẶT BỞI

Theo dõi danh sách này, thật dễ dàng để biết lý do tại sao bạn không thể có bí danh CHỌN trong mệnh đề WHERE, vì bí danh chưa được tạo. T-SQL (SQL Server) tuân thủ nghiêm ngặt điều này và bạn không thể sử dụng bí danh CHỌN cho đến khi bạn vượt qua CHỌN.

Nhưng trong MySQL, có thể sử dụng các bí danh SELECT trong mệnh đề HAVING ngay cả khi nó phải (về mặt logic) được xử lý trước mệnh đề SELECT. Làm thế nào điều này có thể có thể?

Để đưa ra một ví dụ:

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

Câu lệnh không hợp lệ trong T-SQL (vì HAVING đang đề cập đến bí danh SELECT Amount) ...

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

... nhưng hoạt động tốt trong MySQL.

Dựa trên điều này, tôi tự hỏi:

  • Có phải MySQL đang dùng một phím tắt trong các quy tắc SQL để giúp người dùng? Có thể sử dụng một số loại phân tích trước?
  • Hoặc là MySQL sử dụng một thứ tự giải thích khái niệm khác với thứ tự tôi mặc dù tất cả RDBMS đang theo dõi?

1
Tôi đoán là, đó là điểm đạn thứ hai của bạn.
a_horse_with_no_name

3
Chà tôi đoán nó không gây ra bất kỳ sự mơ hồ hay nhầm lẫn nào cho đến khi chúng hỗ trợ các chức năng xếp hạng. Sau đó SELECT C, ROW_NUMBER() OVER (ORDER BY X) AS RN FROM T GROUP BY C HAVING RN = 1sẽ có vấn đề như ROW_NUMBERchạy sau sựHAVING
Martin Smith

Tôi không chắc những chức năng xếp hạng nào được hỗ trợ bởi MySQL. Nếu bạn muốn số hàng bạn phải tạo nó theo cách này : SELECT @rownum:=@rownum + 1 as row .... Có lẽ lý do tại sao họ hỗ trợ bí danh CHỌN đơn giản là vì họ có thể, do thực tế là họ không hỗ trợ những thứ sẽ khiến điều đó không thể ... ai biết? :)
Ohlin

Như @MartinSmith giải thích, miễn là không có chức năng cửa sổ / xếp hạng, thứ tự thực hiện logic HAVINGSELECTmệnh đề có thể được hoán đổi cho nhau. Vì vậy, không có sự mơ hồ trong việc này và có thể đơn giản hóa giao diện của mã khi có các biểu thức quái dị trong SELECT.
ypercubeᵀᴹ

Hy vọng rằng đây là một phần về chủ đề để nói rằng tôi đã trả lời một câu hỏi Ở đây đang tận hưởng kết quả nhanh hơn (với distincts) ... với Alias in the Havingmặc dù cùng một Explainđầu ra. Vì vậy, một số biến thể với Trình tối ưu hóa đang diễn ra.
vẽ

Câu trả lời:


13

Vâng, khi bạn có một câu hỏi về loại này, nguồn thông tin tốt nhất IMHO là tài liệu MySQL. Bây giờ đến điểm. Đây là hành vi của tiện ích mở rộng MySql GROUP BYđược bật theo mặc định.

MySQL Extensions để GROUP BY
MySQL kéo dài hành vi này cho phép việc sử dụng một bí danh trong mệnh đề HAVING cho cột tổng hợp

Nếu bạn muốn hành vi tiêu chuẩn, bạn có thể vô hiệu hóa tiện ích mở rộng này với sql_mode ONLY_FULL_GROUP_BY

SET [SESSION | GLOBAL] sql_mode = ONLY_FULL_GROUP_BY;

Nếu bạn cố gắng thực hiện truy vấn được đề cập ở trên trong ONLY_FULL_GROUP_BYsql_mode, bạn sẽ nhận được thông báo lỗi sau:

Trường không nhóm 'Số tiền' được sử dụng trong mệnh đề HAVING: CHỌN NĂM (orderdate), COUNT (*) dưới dạng Số tiền TỪ đơn đặt hàng NHÓM THEO NĂM (orderdate) Số lượng> 1

Đây là bản demo SQLFiddle

Do đó, tùy thuộc vào bạn cách định cấu hình và sử dụng phiên bản MySQL của bạn.


Bạn hoàn toàn đúng về tài liệu. Tôi chỉ không bao giờ nghĩ rằng nó có thể được viết như vậy rõ ràng khi bạn trích dẫn nó ở trên :) Cảm ơn cho việc tìm kiếm nó ...
Ohlin

Câu trả lời này không trả lời "MySQL đang phân tích trước hay MySQL sử dụng một cách hiểu khái niệm khác?".
Pacerier

2
@Pacerier MySQL là "thực hiện phân tích trước", bởi vì trình tối ưu hóa truy vấn xem xét tất cả các khía cạnh của truy vấn trong khi chọn những gì nó tin sẽ là kế hoạch truy vấn tốt nhất. Khái niệm "giải thích khái niệm khác nhau" phản bội sự hiểu lầm về thực tế rằng máy chủ có thể tự do thực hiện mô hình khái niệm theo bất kỳ cách nào tạo ra kết quả hợp lệ. ORDER BY, ví dụ, có thể thực sự được xử lý sớm hơn nhiều so với lý thuyết là, nếu trình tối ưu hóa thấy rằng các hàng ban đầu có thể được đọc theo thứ tự từ một chỉ mục đã theo thứ tự mong muốn.
Michael - sqlbot

4

Câu hỏi hay.

Tôi nghĩ bạn nên chạy các truy vấn này

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

và kiểm tra cách truy vấn được viết lại. Tôi khá chắc chắn rằng trình tối ưu hóa truy vấn thay thế Số tiền bằng COUNT (*)

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

Giống như nó làm với

select 
 *
from 
 test
where 
 id = 5 - 3

Sau khi tối ưu hóa truy vấn của nó một cái gì đó như thế này.

select 
 test.id as 'id'
from 
 test
where 
 test.id = 2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.