Chào anh em Data và Backend! Khi làm việc với SQL, chắc hẳn ai cũng đã quá quen với GROUP BY. Dù rất mạnh mẽ, nhưng GROUP BY có một nhược điểm chí mạng: nó "gom" các dòng lại và làm mất đi chi tiết của từng dòng dữ liệu gốc.
Vậy nếu sếp yêu cầu: "Lấy ra chi tiết từng đơn hàng, kèm theo tổng doanh thu của cả tháng đó trên cùng một dòng" thì sao? Dùng Subquery hay JOIN lằng nhằng? Quên đi, đây chính là lúc Window Function (Hàm cửa sổ) tỏa sáng!
🪟 1. Window Function Là Gì?
Window Function cho phép bạn thực hiện tính toán trên một tập hợp các hàng liên quan đến hàng hiện tại (gọi là "cửa sổ" - window) mà không làm thay đổi số lượng hàng trả về.
Cấu trúc cốt lõi của một Window Function:
Để SQL biết bạn đang dùng Window Function, bạn bắt buộc phải có mệnh đề OVER(). Bên trong OVER() thường chứa:
-
PARTITION BY: Chia dữ liệu thành các nhóm nhỏ (giống GROUP BY nhưng không gộp dòng). -
ORDER BY: Sắp xếp dữ liệu bên trong từng nhóm. -
ROWS / RANGE: Định nghĩa khung cửa sổ hẹp hơn (ví dụ: chỉ tính tổng của 3 dòng gần nhất).
🛠️ 2. Các Loại Window Function "Nhẵn Mặt"
Chúng ta có 3 nhóm hàm chính hay dùng nhất:
A. Hàm Tổng Hợp (Aggregate Window Functions)
Vẫn là SUM, AVG, COUNT, MAX, MIN nhưng dùng kèm OVER().
Ví dụ: Tính tổng lũy kế doanh thu theo từng ngày trong năm:
SELECT
MaDonHang,
NgayDatHang,
TongTien,
SUM(TongTien) OVER (PARTITION BY YEAR(NgayDatHang) ORDER BY NgayDatHang) AS TongLuyKeNam
FROM
DonHang;
B. Hàm Xếp Hạng (Ranking Window Functions)
Cực kỳ hữu ích khi muốn tìm "Top N" sản phẩm, nhân viên xuất sắc.
-
ROW_NUMBER(): Đánh số thứ tự liên tục 1, 2, 3, 4 (không quan tâm giá trị trùng). -
RANK(): Nếu điểm bằng nhau thì đồng hạng, nhưng sẽ bỏ qua thứ hạng tiếp theo (VD: 1, 2, 2, 4). -
DENSE_RANK(): Đồng hạng nhưng KHÔNG bỏ qua thứ hạng tiếp theo (VD: 1, 2, 2, 3).
Ví dụ: Xếp hạng sản phẩm bán chạy nhất trong từng danh mục:
SELECT
DanhMuc,
TenSanPham,
TongSoLuongBan,
RANK() OVER (PARTITION BY DanhMuc ORDER BY TongSoLuongBan DESC) AS HangSanPham
FROM
SanPhamBanChay;
C. Hàm Giá Trị (Value Window Functions)
Giúp bạn lấy giá trị của dòng trước/dòng sau so với dòng hiện tại. Cực kỳ ngon để tính biến động, chênh lệch (MoM, YoY).
-
LAG(): Lấy giá trị của dòng phía trước. -
LEAD(): Lấy giá trị của dòng phía sau.
Ví dụ: So sánh doanh số tháng này so với tháng trước:
SELECT
Thang,
DoanhSo,
LAG(DoanhSo, 1, 0) OVER (ORDER BY Thang) AS DoanhSoThangTruoc,
DoanhSo - LAG(DoanhSo, 1, 0) OVER (ORDER BY Thang) AS ChenhLech
FROM
BaoCaoDoanhSo;
⚡ 3. Bí Kíp Tối Ưu Hiệu Suất
Window Function dùng rất sướng nhưng nếu không cẩn thận sẽ làm server "thở oxy". Anh em lưu ý:
-
Index là chân ái: Hãy đảm bảo các cột dùng trong
PARTITION BYvàORDER BYđã được đánh Index phù hợp. Nếu không, DB sẽ phải sort thủ công rất tốn tài nguyên. -
Hạn chế window frame quá rộng: Dùng
ROWShayRANGEcho đúng. Tránh dùngUNBOUNDED PRECEDING AND UNBOUNDED FOLLOWINGtrên các tập dữ liệu khổng lồ nếu không thực sự cần thiết. -
Kết hợp với CTE (
WITH ... AS): Nếu cần dùng nhiều Window Function phức tạp, hãy tách chúng ra bằng CTE để code dễ đọc và giúp DB tối ưu hóa Execution Plan tốt hơn.
🎯 Tóm Lại
Thay vì viết những câu subquery dài loằng ngoằng, chạy chậm rùa bò, Window Function mang đến một cú pháp thanh lịch và tốc độ thực thi vượt trội. Nếu anh em làm Data Analysis hay Backend chuyên xử lý báo cáo, đây là skill bắt buộc phải "nằm lòng".
Anh em thường dùng Window Function nào nhiều nhất trong project thực tế? Chia sẻ ở phần comment nhé! 👇
🔥 Khám phá thêm: Nếu muốn luyện thêm các bài toán về SQL, Tối ưu truy vấn hay Database Design, hãy ghé thăm blog ITPrep để nâng cấp bộ kỹ năng ngay hôm nay!
Nguồn tham khảo: ITPrep - Hướng Dẫn Chuyên Sâu về Window Function trong SQL
Top comments (0)