SQL - sử dụng bí danh trong Group By


143

Chỉ tò mò về cú pháp SQL. Vì vậy, nếu tôi có

SELECT 
 itemName as ItemName,
 substring(itemName, 1,1) as FirstLetter,
 Count(itemName)
FROM table1
GROUP BY itemName, FirstLetter

Điều này sẽ không chính xác bởi vì

GROUP BY itemName, FirstLetter 

thực sự nên

GROUP BY itemName, substring(itemName, 1,1)

Nhưng tại sao chúng ta không thể đơn giản sử dụng trước đây cho thuận tiện?


13
điều đó được cho phép trong Postgresql
Michael Buen

7
MySQL cũng cho phép nó
Kip

1
bạn đang nói về rdbms nào?
Shiwangini

Câu trả lời:


292

SQL được triển khai như thể một truy vấn được thực hiện theo thứ tự sau:

  1. TỪ mệnh đề
  2. Mệnh đề WHERE
  3. Mệnh đề NHÓM THEO
  4. Điều khoản có
  5. Mệnh đề CHỌN
  6. Mệnh đề theo thứ tự

Đối với hầu hết các hệ thống cơ sở dữ liệu quan hệ, thứ tự này giải thích tên nào (cột hoặc bí danh) là hợp lệ vì chúng phải được giới thiệu ở bước trước.

Vì vậy, trong Oracle và SQL Server, bạn không thể sử dụng một thuật ngữ trong mệnh đề GROUP BY mà bạn xác định trong mệnh đề SELECT vì GROUP BY được thực thi trước mệnh đề SELECT.

Mặc dù vẫn có ngoại lệ: MySQL và Postgres dường như có thêm sự thông minh cho phép điều đó.


3
Tôi thích lời giải thích này. Mặc dù tôi không thể suy đoán mức độ khó khi thêm nó vào động cơ dưới dạng đường cú pháp.
Haoest

11
Bất kỳ ý tưởng nào nếu DB đủ thông minh để nhận ra cùng một biểu thức trong các mệnh đề CHỌN và NHÓM THEO mà không đánh giá lại các biểu thức? tức là nếu có GROUP BY substring(itemName, 1,1), cơ sở dữ liệu có đủ thông minh để không thực hiện cú đánh hiệu năng của việc tính toán lại chuỗi con trong mệnh đề SELECT không?
Kip

10
Trong mệnh đề CHỌN của một truy vấn có nhóm, bạn chỉ có quyền truy cập vào các biểu thức GROUP BY và các giá trị tổng hợp. Vì vậy, nó không phải là thông minh; nó phải được thực hiện theo cách đó để nhóm hoạt động. (Và nó được yêu cầu bởi tiêu chuẩn SQL). Nhưng ngay cả trong các trường hợp tầm thường hơn (ví dụ: cùng một biểu thức trong mệnh đề WHERE và SELECT), các hệ thống cơ sở dữ liệu hiện đại chắc chắn sẽ chỉ tính toán một lần. Tối ưu hóa này được gọi là loại bỏ biểu thức phụ phổ biến .
Codo

6
Lệnh thực hiện có liên quan gì đến câu hỏi? Nó không giống như người hỏi đã cố gắng NHÓM THEO trên COUNT (). Trong thực tế, truy vấn theo yêu cầu chỉ hoạt động tốt trong MySQL và có khả năng PostgreSQL được chỉ ra trong các bình luận.

1
Đối với mysql, sql_modekhông bao gồm CHỈ_ULL_GROUP_BY trong bitmask, Trình tối ưu hóa có cơ hội cung cấp kết quả tốt hơn với cách sử dụng bí danh khác nhau / khác nhau trong HAVINGmệnh đề.
vẽ

28

Bạn luôn có thể sử dụng truy vấn con để bạn có thể sử dụng bí danh; Tất nhiên, kiểm tra hiệu năng (Có thể máy chủ db sẽ chạy cả hai giống nhau, nhưng không bao giờ bị tổn thương để xác minh):

SELECT ItemName, FirstLetter, COUNT(ItemName)
FROM (
    SELECT ItemName, SUBSTRING(ItemName, 1, 1) AS FirstLetter
    FROM table1
    ) ItemNames
GROUP BY ItemName, FirstLetter

2
Các truy vấn phụ nên được tránh khi có thể do hiệu suất kém. Sử dụng một bản sao của chức năng sẽ tốt hơn nhiều vì tất nhiên nó được trình tối ưu hóa cơ sở dữ liệu phát hiện và chỉ được thực hiện một lần.
Roland

1
@Roland nhưng không có sự khác biệt trong kế hoạch thực hiện trong trường hợp đó. Có bất kỳ xem xét hiệu suất khác?
Guido Mocha

@Roland, Các truy vấn phụ tương quan hoặc cú pháp khác dẫn đến các vòng lặp hoặc hành vi theo từng hàng nên tránh và có giới hạn về mức độ bạn nên đi sâu với các truy vấn con lồng nhau, nhưng nói chung không đúng khi dẫn truy vấn phụ đến hiệu suất kém. Trong trường hợp này như Chris đã nói, bạn có thể xác minh kế hoạch thực hiện (kế hoạch truy vấn AKA, kế hoạch giải thích) so sánh cả có và không có truy vấn con, và xem liệu có thực sự có sự khác biệt nào không. Khá nhiều công cụ cơ sở dữ liệu sẽ viết lại truy vấn của bạn để bạn không hoàn toàn kiểm soát những gì được thực thi. Đó là điểm của cú pháp khai báo.
Davos

16

Ít nhất trong PostgreSQL, bạn có thể sử dụng số cột trong tập kết quả trong mệnh đề GROUP BY của bạn:

SELECT 
 itemName as ItemName,
 substring(itemName, 1,1) as FirstLetter,
 Count(itemName)
FROM table1
GROUP BY 1, 2

Tất nhiên điều này bắt đầu là một nỗi đau nếu bạn đang thực hiện điều này một cách tương tác và bạn chỉnh sửa truy vấn để thay đổi số lượng hoặc thứ tự các cột trong kết quả. Nhưng vẫn.


GROUP BY FirstLetterđược cho phép trong Postgresql. Để dí dỏm, hãy thử chạy cái này trong Postgresql: chọn chuỗi con (tên_bảng, 1,2) làm tên từ nhóm information_schema.tables theo tname
Michael Buen

1
@MichaelBuen Có vẻ như có vấn đề với tôi. Từ một bài kiểm tra nhanh, có vẻ như nếu có một bí danh và một cột bảng cơ sở có cùng tên thì cái sau có được ưu tiên không? Câu đố SQL . Vì vậy, nếu dựa vào nhóm này bằng bí danh, một thay đổi lược đồ sau này có thể âm thầm phá vỡ truy vấn của bạn và thay đổi ngữ nghĩa.
Martin Smith

@MartinSmith bây giờ chỉ biết đó là một gotcha, sẽ không sử dụng nó, cảm ơn. Vì PostgreSQL cho phép lối tắt đó, họ nên ưu tiên bí danh, nếu không họ không nên cho phép lối tắt đó.
Michael Buen

Đây là một ý tưởng khủng khiếp của các nhà thiết kế PostgreSQL. Thật khó hiểu ngay khi bạn cố gắng thực GROUP BYhiện bất kỳ biểu thức nào có chứa hàm tổng hợp hoặc hàm cửa sổ, điều "rõ ràng" không hoạt động.
Lukas Eder

13

SQL Server không cho phép bạn tham chiếu bí danh trong mệnh đề GROUP BY vì thứ tự xử lý hợp lý. Mệnh đề GROUP BY được xử lý trước mệnh đề SELECT, vì vậy bí danh không được biết khi mệnh đề GROUP BY được ước tính. Điều này cũng giải thích tại sao bạn có thể sử dụng bí danh trong mệnh đề ORDER BY.

Đây là một nguồn thông tin về các giai đoạn xử lý logic của Máy chủ SQL .


8

Tôi không trả lời tại sao lại như vậy, nhưng chỉ muốn chỉ ra cách giải quyết giới hạn đó trong SQL Server bằng cách sử dụng CROSS APPLYđể tạo bí danh. Sau đó, bạn sử dụng nó trong GROUP BYmệnh đề, như vậy:

SELECT 
 itemName as ItemName,
 FirstLetter,
 Count(itemName)
FROM table1
CROSS APPLY (SELECT substring(itemName, 1,1) as FirstLetter) Alias
GROUP BY itemName, FirstLetter

4

Lưu ý rằng việc sử dụng bí danh trong Nhóm theo (đối với các dịch vụ hỗ trợ nó, chẳng hạn như postgres) có thể có kết quả ngoài ý muốn. Ví dụ: nếu bạn tạo một bí danh đã tồn tại trong câu lệnh bên trong, Nhóm By sẽ chọn tên trường bên trong.

-- Working example in postgres
select col1 as col1_1, avg(col3) as col2_1
from
    (select gender as col1, maritalstatus as col2, 
    yearlyincome as col3 from customer) as layer_1
group by col1_1;

-- Failing example in postgres
select col2 as col1, avg(col3)
from
    (select gender as col1, maritalstatus as col2,
    yearlyincome as col3 from customer) as layer_1
group by col1;

3

Một số DBMS sẽ cho phép bạn sử dụng bí danh thay vì phải lặp lại toàn bộ biểu thức.
Teradata là một ví dụ như vậy.

Tôi tránh ký hiệu vị trí thứ tự theo khuyến nghị của Bill vì những lý do được ghi trong câu hỏi SO này .

Cách thay thế dễ dàng và mạnh mẽ là luôn lặp lại biểu thức trong mệnh đề GROUP BY.
DRY KHÔNG áp dụng cho SQL.


1

Cảnh giác với việc sử dụng các bí danh khi nhóm các kết quả từ một khung nhìn trong SQLite. Bạn sẽ nhận được kết quả bất ngờ nếu tên bí danh giống với tên cột của bất kỳ bảng bên dưới nào (với các khung nhìn.)


0

Trước đây, tôi thấy rằng Rdb, sản phẩm cũ của DEC hiện được Oracle hỗ trợ cho phép sử dụng bí danh cột trong NHÓM THEO. Dòng chính Oracle qua phiên bản 11 không cho phép sử dụng bí danh cột trong NHÓM THEO. Không chắc chắn những gì Postgresql, SQL Server, MySQL, v.v sẽ hoặc không cho phép. YMMV.

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.