CodeGym /Các khóa học /SQL SELF /Sử dụng PARTITION BY để chia dữ liệu thành ...

Sử dụng PARTITION BY để chia dữ liệu thành nhóm

SQL SELF
Mức độ , Bài học
Có sẵn

Hãy tưởng tượng bạn đang làm phục vụ (hoặc barista nếu bạn thích cà phê) ở một nhà hàng lớn. Mỗi ngày bạn tổng kết tiền tip kiếm được. Nhưng có một điểm: nhà hàng được chia thành các khu vực, và bạn muốn biết mỗi khu vực kiếm được bao nhiêu tip riêng biệt. PARTITION BY — chính là thứ mà SQL dùng để "chia nhà hàng thành các khu vực".

Nói một cách formal hơn, PARTITION BY được dùng trong các hàm cửa sổ để chia tất cả các dòng trong bảng thành các nhóm riêng biệt (hoặc "partition"). Bên trong mỗi nhóm, hàm cửa sổ sẽ chạy lại từ đầu. Kiểu như bạn áp dụng hàm riêng biệt cho từng "partition" vậy.

Ví dụ: nó hoạt động như thế nào

Giả sử mình có bảng sales chứa dữ liệu bán hàng:

region salesperson amount
North Alice 100
North Bob 200
South Alice 150
South Charlie 250

Nếu muốn tính xem mỗi người bán hàng kiếm được bao nhiêu tiền, nhưng tách riêng cho từng khu vực, PARTITION BY — chính là thứ bạn cần.

Cú pháp PARTITION BY

Cú pháp khá đơn giản:

window_function() OVER (PARTITION BY column_or_columns)
  • window_function() — ví dụ như SUM(), AVG(), ROW_NUMBER() v.v.
  • PARTITION BY column — chỉ định cột nào để chia nhóm các dòng.
  • OVER() — là toán tử bảo SQL: "Hãy làm gì đó trong phạm vi cửa sổ này".

Ví dụ: tính tổng theo nhóm

Hãy tính tổng doanh số cho từng khu vực:

SELECT
    region,
    salesperson,
    amount,
    SUM(amount) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales;

Kết quả sẽ như này:

region salesperson amount total_sales_by_region
North Alice 100 300
North Bob 200 300
South Alice 150 400
South Charlie 250 400

Chuyện gì xảy ra ở đây? SQL chia các dòng thành nhóm theo giá trị cột region (North và South), rồi áp dụng hàm SUM() riêng cho từng nhóm. Kết quả là các dòng trong nhóm "North" đều nhận cùng một giá trị tổng, còn nhóm "South" cũng vậy.

Ví dụ sử dụng PARTITION BY

Cùng xem PARTITION BY hữu ích thế nào trong các bài toán thực tế nhé.

Ví dụ 1: Xếp hạng trong nhóm

Giả sử bạn muốn xếp hạng các nhân viên bán hàng trong từng khu vực theo số lượng bán được. Có thể dùng kết hợp PARTITION BY và hàm RANK():

SELECT
    region,
    salesperson,
    amount,
    RANK() OVER (PARTITION BY region ORDER BY amount DESC) AS rank_in_region
FROM sales;

Kết quả:

region salesperson amount rank_in_region
North Bob 200 1
North Alice 100 2
South Charlie 250 1
South Alice 150 2

Hàm RANK() sẽ gán thứ hạng trong từng nhóm region, bắt đầu từ 1. Lưu ý là mỗi nhóm thứ hạng đều bắt đầu từ số một nhé.

Ví dụ 2: So sánh từng giá trị với trung bình nhóm

Giả sử bạn muốn xem mỗi nhân viên bán hàng kiếm được bao nhiêu so với trung bình của khu vực mình. Dùng AVG() nhé:

SELECT
    region,
    salesperson,
    amount,
    AVG(amount) OVER (PARTITION BY region) AS avg_sales_by_region,
    amount - AVG(amount) OVER (PARTITION BY region) AS diff_from_avg
FROM sales;

Kết quả:

region salesperson amount avg_sales_by_region diff_from_avg
North Alice 100 150 -50
North Bob 200 150 50
South Alice 150 200 -50
South Charlie 250 200 50

Đầu tiên SQL chia các dòng thành nhóm theo region. Sau đó tính giá trị trung bình AVG(amount) cho từng nhóm. Cuối cùng, với mỗi dòng, nó tính hiệu giữa giá trị của dòng đó và trung bình nhóm.

Ví dụ 3: Đánh số dòng trong nhóm

Giả sử bạn muốn đánh số tất cả giao dịch trong từng nhóm khu vực. Dùng ROW_NUMBER() nhé:

SELECT
    region,
    salesperson,
    amount,
    ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount DESC) AS row_number
FROM sales;

Kết quả:

region salesperson amount row_number
North Bob 200 1
North Alice 100 2
South Charlie 250 1
South Alice 150 2

So sánh với GROUP BY

Nhiều bạn hay nhầm giữa PARTITION BYGROUP BY. So sánh thử nhé:

GROUP BY

GROUP BY sẽ thay đổi cấu trúc kết quả — nó biến các dòng thành các aggregate. Ví dụ:

SELECT
    region,
    SUM(amount) AS total_sales
FROM sales
GROUP BY region;

Kết quả:

region total_sales
North 300
South 400

Ở đây mình mất thông tin về từng nhân viên bán hàng, vì dữ liệu đã được tổng hợp lại rồi.

PARTITION BY

PARTITION BY thì ngược lại, không thay đổi cấu trúc. Bạn vẫn thấy từng dòng, nhưng có thêm các giá trị tính toán theo nhóm. Tức là, PARTITION BY cho phép aggregate mà không mất chi tiết.

Lỗi thường gặp khi dùng PARTITION BY

Lỗi 1: Quên PARTITION BY

Đôi khi bạn muốn nhóm dữ liệu nhưng lại quên dùng PARTITION BY. Ví dụ:

SELECT
    region,
    salesperson,
    amount,
    SUM(amount) OVER () AS total_sales
FROM sales;

Kết quả:

region salesperson amount total_sales
North Alice 100 700
North Bob 200 700
South Alice 150 700
South Charlie 250 700

Ở đây SUM(amount) được tính cho toàn bộ bảng, chứ không phải từng khu vực. Nếu muốn tính theo khu vực, nhớ thêm PARTITION BY region nhé.

Lỗi 2: Sai thứ tự trong ORDER BY

Thứ tự dòng trong cửa sổ rất quan trọng với các hàm như RANK() hoặc ROW_NUMBER(). Hãy cẩn thận khi dùng ORDER BY bên trong OVER() nhé.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION