NTILE
Bạn sẽ học được
NTILE(n)chia dòng thànhnnhóm có kích thước gần bằng nhau.- Cách phần dư được phân bổ vào các nhóm đầu.
- Dùng
NTILEđể chia tứ phân vị, thập phân vị trong phân tích khách hàng. - Khác biệt giữa
NTILEvàRANKkhi chia nhóm. - Những cạm bẫy khiến nhóm không đều như tên gọi.
Khái niệm cốt lõi
NTILE(n) chia các dòng trong cửa sổ thành n nhóm, đánh số từ 1 đến n. Kích thước các nhóm chênh nhau tối đa một dòng, và phần dư được đưa vào các nhóm đầu tiên.
Ví dụ chia 9 sản phẩm thành 4 nhóm theo giá tăng dần:
SELECT name, price,
NTILE(4) OVER (ORDER BY price) AS nhom_gia
FROM products
ORDER BY price;Kết quả:
| name | price | nhom_gia |
|---|---|---|
| Sách SQL thực chiến | 180.000 | 1 |
| Áo thun cotton | 250.000 | 1 |
| Đèn bàn LED | 290.000 | 1 |
| Bình giữ nhiệt | 320.000 | 2 |
| Giá kê laptop | 390.000 | 2 |
| Chuột không dây | 450.000 | 3 |
| Tai nghe Bluetooth | 890.000 | 3 |
| Bàn phím cơ | 1.250.000 | 4 |
| Nồi chiên không dầu | 1.690.000 | 4 |
Chín dòng chia bốn nhóm: 9 = 2 × 4 + 1. Nhóm 1 nhận thêm một dòng nên có 3, ba nhóm còn lại mỗi nhóm 2. Không có nhóm nào trống, không có nhóm nào lệch quá một dòng.
Đảo chiều sắp xếp là đảo chiều ý nghĩa:
SELECT name, price,
NTILE(4) OVER (ORDER BY price DESC) AS nhom_gia_giam_dan
FROM products
ORDER BY price DESC;Lúc này nhóm 1 là bốn sản phẩm đắt nhất — nhưng vẫn là ba dòng cho nhóm 1 (Bàn phím, Nồi chiên và một sản phẩm nữa) và hai dòng cho các nhóm sau, vì quy tắc chia dựa trên vị trí chứ không dựa trên giá trị. Nhóm 1 theo chiều giảm dần gồm Nồi chiên (1.690.000), Bàn phím (1.250.000), Tai nghe (890.000); nhóm 4 gồm Sách và Áo.
Phân biệt với RANK:
RANK | NTILE | |
|---|---|---|
| Đầu ra | Hạng của từng giá trị | Số nhóm |
| Số nhóm | Bằng số giá trị khác nhau | Đúng n nhóm |
| Đồng hạng | Cùng hạng | Có thể bị tách nhóm |
| Kích thước | Không kiểm soát | Chênh tối đa 1 |
Điểm cuối là cạm bẫy lớn nhất: hai sản phẩm cùng giá có thể rơi vào hai nhóm khác nhau nếu chúng nằm hai bên ranh giới. NTILE không biết gì về giá trị, chỉ đếm vị trí. Nếu yêu cầu là "mọi sản phẩm cùng giá phải cùng nhóm", NTILE không đáp ứng được — phải tự đặt ngưỡng.
Ví dụ chạy được
Chia khách thành bốn nhóm chi tiêu (tứ phân vị) — mẫu dùng nhiều nhất trong phân tích RFM:
WITH spend AS (
SELECT c.id, c.name,
COALESCE(SUM(oi.quantity * oi.unit_price), 0) AS total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
LEFT JOIN order_items oi ON oi.order_id = o.id
GROUP BY c.id, c.name
)
SELECT name, total,
NTILE(4) OVER (ORDER BY total DESC) AS nhom
FROM spend
ORDER BY total DESC;Mười hai khách chia đều 3 người mỗi nhóm:
| name | total | nhom |
|---|---|---|
| Bình Trần | 6.750.000 | 1 |
| Giang Đặng | 5.490.000 | 1 |
| Chi Lê | 4.270.000 | 1 |
| Minh Dương | 4.030.000 | 2 |
| An Nguyễn | 3.530.000 | 2 |
| Dũng Phạm | 2.150.000 | 2 |
| Khánh Hồ | 1.640.000 | 3 |
| Linh Ngô | 1.190.000 | 3 |
| Inh Đỗ | 960.000 | 3 |
| Em Võ | 640.000 | 4 |
| Nga Lý | 580.000 | 4 |
| Hà Bùi | 0 | 4 |
COALESCE là bắt buộc: Hà Bùi chưa có đơn, SUM trên LEFT JOIN trả NULL, và NTILE ... ORDER BY NULL sẽ xếp cô vào cuối với NULLS LAST mặc định — nhưng cột total khi đó là NULL chứ không phải 0, gây hiểu nhầm trong báo cáo. Đây là lý do phải xử lý NULL trước khi chia nhóm.
Chia đơn hàng thành ba nhóm (tam phân vị):
WITH totals AS (
SELECT o.id, SUM(oi.quantity * oi.unit_price) AS total
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
GROUP BY o.id
)
SELECT id, total,
NTILE(3) OVER (ORDER BY total DESC) AS nhom
FROM totals
ORDER BY total DESC;Mười sáu đơn chia ba nhóm: 16 = 5 × 3 + 1, nên nhóm 1 có 6 đơn (13, 7, 6, 16, 9, 1) và hai nhóm sau mỗi nhóm 5 đơn. Ranh giới nhóm 1–2 nằm giữa 2.100.000 và 2.050.000; ranh giới nhóm 2–3 nằm giữa 1.530.000 và 1.430.000. Không đơn nào bị tách khỏi nhóm của mình vì dữ liệu không có hai đơn cùng tổng.
Chia trong từng danh mục với PARTITION BY:
SELECT category, name, price,
NTILE(2) OVER (PARTITION BY category ORDER BY price DESC) AS nua
FROM products
ORDER BY category, price DESC;Gia dụng có 4 sản phẩm nên mỗi nửa 2 sản phẩm. Điện tử có 3 sản phẩm nên nửa trên 2, nửa dưới 1. Sách và Thời trang chỉ một sản phẩm nên luôn rơi vào nửa 1 — NTILE không tạo nhóm rỗng chỉ để đủ số.
Lưu ý về tham số: n phải là hằng số nguyên dương. Không viết được NTILE(COUNT(*)/4) hay NTILE(số_nhóm) lấy từ cột khác. Muốn chia theo tỷ lệ động, phải tính n trước rồi ghép vào câu SQL, hoặc dùng PERCENT_RANK/CUME_DIST thay thế.
Một ứng dụng thực tế: gắn nhãn ABC cho sản phẩm theo doanh thu.
WITH revenue AS (
SELECT p.id, p.name, SUM(oi.quantity * oi.unit_price) AS doanh_thu
FROM products p
JOIN order_items oi ON oi.product_id = p.id
GROUP BY p.id, p.name
)
SELECT name, doanh_thu,
CASE NTILE(3) OVER (ORDER BY doanh_thu DESC)
WHEN 1 THEN 'A'
WHEN 2 THEN 'B'
ELSE 'C'
END AS hang_abc
FROM revenue
ORDER BY doanh_thu DESC;Chỉ 8 sản phẩm có doanh thu (Giá kê chưa bán được lần nào nên không xuất hiện vì dùng JOIN). NTILE(3) trên 8 dòng cho kích thước 3, 3, 2. Nhóm A gồm ba sản phẩm bán chạy nhất — cách phân loại này giúp đội vận hành biết nên ưu tiên nhập hàng cho ai.
Thực hành
- Chạy
NTILE(4)trên 9 sản phẩm và kiểm tra kích thước nhóm là 3, 2, 2, 2. - Chạy tứ phân vị khách hàng và xác nhận mỗi nhóm đúng 3 người, Hà Bùi ở nhóm 4 với 0.
- Đổi
NTILE(3)thànhNTILE(5)trên 16 đơn và tính kích thước mỗi nhóm trước khi chạy — sau đó đối chiếu. - Thêm sản phẩm giả lập cùng giá với một sản phẩm có sẵn, chạy lại
NTILE(4)và quan sát hai sản phẩm cùng giá rơi vào hai nhóm khác nhau. - Viết câu dùng
NTILE(10)cho khách hàng và giải thích vì sao với 12 khách, thập phân vị không còn nhiều ý nghĩa.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Nhóm chênh nhau hơn một dòng | Hiểu sai cơ chế chia | NTILE luôn chênh tối đa 1 |
| Nhóm 1 luôn lớn nhất | Đó là quy tắc phần dư | Đảo ORDER BY nếu muốn nhóm khác nhận dư |
| Hai dòng cùng giá khác nhóm | NTILE chia theo vị trí | Tự đặt ngưỡng nếu cần cùng nhóm |
argument of ntile must be greater than zero | n bằng 0 hoặc âm | Dùng hằng số dương |
NTILE báo lỗi cú pháp với biến | n không phải hằng số | Tính n trước ở ứng dụng |
Khách chưa mua bị xếp cuối với NULL | SUM trên LEFT JOIN | COALESCE(..., 0) |
Lỗi thứ ba là lý do NTILE không thay thế được ngưỡng kinh doanh. Nếu chính sách là "khách VIP khi chi từ 5 triệu", NTILE(4) không diễn đạt được điều đó — nó luôn tạo đúng bốn nhóm bất kể giá trị tuyệt đối. Hãy chọn công cụ theo câu hỏi: chia tương đối dùng NTILE, chia theo ngưỡng tuyệt đối dùng CASE.
Tóm tắt
NTILE(n)chia thànhnnhóm gần bằng nhau, dư vào nhóm đầu.- Không đảm bảo các giá trị bằng nhau nằm cùng nhóm.
PARTITION BYchia nhóm trong từng phần; phần nhỏ hơnnsẽ có nhóm rỗng.nphải là hằng số nguyên dương.- Dùng cho tứ phân vị, thập phân vị, phân loại ABC — không dùng thay ngưỡng tuyệt đối.
Use ← → to move between lessons