Trino SQL Bài học 17: Gom nhóm dữ liệu với GROUP BY
Mục tiêu bài học:
-
Hiểu cơ chế chia dữ liệu thành các nhóm nhỏ để tính toán (Split - Apply - Combine).
-
Áp dụng mệnh đề
GROUP BYkết hợp với các hàm tổng hợp để phân tích dữ liệu đa chiều. -
Khắc phục triệt để lỗi logic khi kết hợp cột vô hướng và hàm tổng hợp.
-
Nắm được "mẹo" sử dụng chỉ mục cột (Column Index) để viết SQL nhanh hơn trong Trino.
1. Cơ chế hoạt động của GROUP BY
Nếu các hàm tổng hợp ở Bài 16 (như SUM, COUNT) lấy toàn bộ dữ liệu trong bảng và nén lại thành 1 dòng duy nhất, thì GROUP BY sẽ đóng vai trò làm bước đệm.
GROUP BY hoạt động theo mô hình Split - Apply - Combine:
-
Split (Chia nhỏ): Trino sẽ gom tất cả các dòng có chung một (hoặc nhiều) giá trị thành từng thùng (Bucket / Group) riêng biệt.
-
Apply (Áp dụng tính toán): Trino áp dụng các hàm tổng hợp (
SUM,AVG,COUNT) độc lập cho từng cái thùng đó. -
Combine (Gộp kết quả): Trả về kết quả, mỗi cái thùng (nhóm) sẽ hiển thị thành 1 dòng.
2. Giải quyết lỗi "Cột bơ vơ" từ Bài 16
Ở Bài 16, chúng ta đã nhắc đến Luật thép: Không thể để một cột vô hướng đứng chung với hàm tổng hợp trong mệnh đề SELECT.
Bây giờ, với GROUP BY, bạn sẽ khai báo chính cái cột vô hướng đó là tiêu chí để chia nhóm. Khi đó, câu lệnh sẽ trở nên hợp lệ.
SQL
-- ĐÚNG CÚ PHÁP VÀ LOGIC:
-- Tính tổng doanh thu của từng trạng thái đơn hàng (orderstatus)
SELECT
orderstatus, -- Cột dùng để gom nhóm
SUM(totalprice) AS revenue -- Hàm tổng hợp áp dụng cho từng nhóm
FROM tpch.tiny.orders
GROUP BY orderstatus;
Kết quả trả về sẽ có 3 dòng (đại diện cho 3 trạng thái O, F, P), kèm theo tổng tiền của mỗi trạng thái.
3. Gom nhóm theo nhiều chiều (Multi-dimensional Grouping)
Bạn có thể truyền nhiều cột vào GROUP BY để phân tích dữ liệu chi tiết hơn. Trino sẽ tạo ra các nhóm dựa trên sự kết hợp (Tổ hợp) của các cột đó.
SQL
-- Phân tích chi tiết: Khách hàng ở từng phân khúc thị trường,
-- và theo từng trạng thái đơn hàng thì đóng góp bao nhiêu tiền?
SELECT
c.mktsegment,
o.orderstatus,
COUNT(o.orderkey) AS total_orders,
SUM(o.totalprice) AS total_revenue
FROM tpch.tiny.orders AS o
JOIN tpch.tiny.customer AS c
ON o.custkey = c.custkey
GROUP BY
c.mktsegment,
o.orderstatus;
(Ghi chú: Lệnh JOIN sẽ được học sâu ở Bài 21, nhưng bạn có thể hiểu tạm ở đây là nối 2 bảng lại bằng mã khách hàng).
4. Thứ tự thực thi (Rất quan trọng)
Để không viết sai logic, bạn phải nhớ thứ tự mà Trino engine thực thi một câu lệnh SQL chứa GROUP BY:
-
FROM/JOIN: Lấy và ghép nối dữ liệu gốc. -
WHERE: Lọc bỏ các dòng không thỏa mãn điều kiện thô. -
GROUP BY: Gom các dòng còn sót lại thành từng nhóm. -
Hàm tổng hợp (
SUM,COUNT): Tính toán cho từng nhóm. -
SELECT: Quyết định hiển thị cột nào. -
ORDER BY: Sắp xếp kết quả cuối cùng. -
LIMIT: Cắt kết quả phân trang.
Ứng dụng thứ tự thực thi:
SQL
-- Chỉ tính doanh thu của các đơn hàng trong năm 1995 (Lọc WHERE TRƯỚC),
-- sau đó mới gom nhóm theo trạng thái (GROUP BY SAU).
SELECT
orderstatus,
SUM(totalprice) AS revenue
FROM tpch.tiny.orders
WHERE orderdate BETWEEN DATE '1995-01-01' AND DATE '1995-12-31'
GROUP BY orderstatus
ORDER BY revenue DESC;
5. Kỹ thuật viết tắt bằng Chỉ số cột (Column Index)
Khi phân tích dữ liệu hàng ngày, các Analyst thường phải thêm bớt cột liên tục vào SELECT. Việc phải copy/paste từng tên cột dài dằng dặc xuống mệnh đề GROUP BY rất tốn thời gian.
Trino (và nhiều chuẩn SQL hiện đại) hỗ trợ khai báo GROUP BY bằng vị trí (số thứ tự) của cột trong mệnh đề SELECT.
SQL
SELECT
EXTRACT(YEAR FROM orderdate) AS order_year, -- Cột 1
orderstatus, -- Cột 2
orderpriority, -- Cột 3
SUM(totalprice) AS revenue -- Cột 4 (Không được group by cột này)
FROM tpch.tiny.orders
GROUP BY 1, 2, 3; -- Gom nhóm theo Cột 1, Cột 2 và Cột 3
Lời khuyên từ chuyên gia: Kỹ thuật này (GROUP BY 1, 2) cực kỳ tuyệt vời cho việc phân tích gõ nhanh (ad-hoc queries) trên DBeaver hoặc CLI. Tuy nhiên, nếu bạn viết code dể nhúng vào hệ thống tự động (Data Pipeline), bạn bắt buộc phải ghi rõ tên cột. Việc dùng số thứ tự sẽ làm code khó bảo trì (nếu ai đó chèn thêm cột vào giữa mệnh đề SELECT, toàn bộ logic nhóm sẽ bị sai lệch).
All rights reserved