2009-06-21 33 views
35

Tôi đang cố gắng để thêm tính năng để một ứng dụng từ trước và tôi đi qua một cái nhìn một cái gì đó MySQL như thế này:MySQL - Lựa chọn một cột không trong Nhóm By

SELECT 
    AVG(table_name.col1), 
    AVG(table_name.col2), 
    AVG(table_name.col3), 
    table_name.personID, 
    table_name.col4 
FROM table_name 
GROUP BY table_name.personID; 

OK vì vậy có một vài chức năng tổng hợp. Bạn có thể chọn personID vì bạn đang nhóm theo nó. Nhưng nó cũng đang chọn một cột không có trong hàm tổng hợp và không phải là một phần của mệnh đề GROUP BY. Sao có thể như thế được??? Liệu nó chỉ chọn một giá trị ngẫu nhiên bởi vì các giá trị chắc chắn không phải là duy nhất cho mỗi nhóm?

Tôi đến từ đâu (Máy chủ MSSQL), đó là lỗi. Ai đó có thể giải thích hành vi này với tôi và tại sao nó được cho phép trong MySQL?

Trả lời

40

Đúng là tính năng này cho phép một số truy vấn không rõ ràng và âm thầm trả về tập kết quả với giá trị tùy ý được chọn từ cột đó. Trong thực tế, nó có xu hướng là giá trị từ hàng trong nhóm được lưu trữ vật lý đầu tiên.

Các truy vấn này không rõ ràng nếu bạn chỉ chọn các cột phụ thuộc vào chức năng trên (các) cột trong tiêu chí GROUP BY. Nói cách khác, nếu chỉ có thể có một giá trị riêng biệt của cột "mơ hồ" cho mỗi giá trị xác định nhóm, thì không có vấn đề gì. Truy vấn này sẽ là bất hợp pháp trong Microsoft SQL Server (và ANSI SQL), mặc dù nó không thể hợp lý dẫn đến sự mơ hồ:

SELECT AVG(table1.col1), table1.personID, persons.col4 
FROM table1 JOIN persons ON (table1.personID = persons.id) 
GROUP BY table1.personID; 

Ngoài ra, MySQL có một chế độ SQL để làm cho nó cư xử theo tiêu chuẩn: ONLY_FULL_GROUP_BY

FWIW, SQLite cũng cho phép các mệnh đề GROUP BY không rõ ràng này, nhưng nó chọn giá trị từ hàng cuối cùng trong nhóm.


Ít nhất trong phiên bản tôi thử nghiệm. Ý nghĩa của việc là tùy ý là MySQL hoặc SQLite có thể thay đổi việc triển khai của chúng trong tương lai và có một số hành vi khác nhau. Do đó, bạn không nên dựa vào hành vi ở cách họ đang ở trong những trường hợp mơ hồ như thế này. Tốt hơn là viết lại các truy vấn của bạn để xác định và không mơ hồ. Đó là lý do tại sao MySQL 5.7 giờ đây cho phép ONLY_FULL_GROUP_BY theo mặc định.

+3

Tôi muốn nhận xét rằng điều này không hoàn toàn đúng. Trong số các trường được lựa chọn ANSI SQL-99 phải là tổng hợp, phụ thuộc vào hàm theo mệnh đề nhóm. Vì vậy, việc chọn user_name khi nhóm theo user_id hoàn toàn ổn. SQL Server và Oracle không tuân thủ điều này, bởi vì họ sẽ không cho phép user_name được chọn khi chỉ user_id nằm trong nhóm theo danh sách; và MySQL không tuân thủ, bởi vì nó không kiểm tra xem mỗi cột được chọn có thực sự phụ thuộc vào chức năng trên user_id hay không. –

+0

@ThorstenKettner, cảm ơn, bạn đã chính xác. MySQL 5.7 đã được cải thiện và nó thông minh hơn nhiều trong trường hợp hỗ trợ ANSI SQL. –

9

Tôi nên có Googled lâu hơn một chút ... Có vẻ như tôi đã tìm thấy my answer.

MySQL mở rộng việc sử dụng GROUP BY để mà bạn có thể sử dụng các cột nonaggregated hoặc tính toán trong danh sách SELECT không xuất hiện trong GROUP BY khoản. Bạn có thể sử dụng tính năng này để có hiệu suất tốt hơn bằng cách tránh sắp xếp cột không cần thiết và nhóm . Ví dụ, bạn không cần phải vào nhóm trên customer.name trong sau truy vấn

Trong SQL tiêu chuẩn, bạn sẽ phải thêm customer.name với mệnh đề GROUP BY. Trong MySQL, tên là dư thừa.

Tuy nhiên, điều đó dường như ... sai.

+3

Bạn nói đúng là có vẻ sai. Nó là! Trong khi tôi chắc chắn có một số trường hợp ngoại lệ, như được chỉ ra bởi Bill Karwin ở trên, tôi thường thấy các nhà phát triển, những người không biết dữ liệu đủ tốt hoặc cách tính năng này thực sự hoạt động, viết các truy vấn với nhóm không phù hợp theo mệnh đề và có được kết quả xấu. Tính năng này phải được tắt theo mặc định và được phép cố ý ghi đè bằng tùy chọn truy vấn để sử dụng trong trường hợp kỹ sư được thông báo đủ để sử dụng nó. –

+0

Nó không còn "sai" hơn là có kết quả trả về 'SELECT * FROM table1' theo thứ tự nhất quán, nhất định: đó là một tính năng, không phải là lỗi. – kmoser

-1
select * from personel where p_id IN(select 
min(dbo.personel.p_id) 
FROM 
personel 
GROUP BY dbo.personel.p_adi) 
+1

Điều này chắc chắn không trả lời câu hỏi – Ojen

+0

@Ojen Nó không, nhưng nó _kind-of_ giải thích những gì đang xảy ra. Đoạn mã trên là một ví dụ về cách hành vi không chuẩn này có thể được mô hình hoá bằng cách sử dụng SQL chuẩn. – Griddo

Các vấn đề liên quan