CASE WHEN – rẽ nhánh đúng, query sống; rẽ sai, pipeline gãy.
“Query này chỉ thêm 1 CASE thôi mà…” Hai tháng sau, bạn ngồi fix bug trong đống CASE WHEN chồng chéo 600 dòng.
Câu chuyện quen thuộc của bất kỳ ai từng vận hành hệ thống BI, reconcile hay KPI dashboard.
1. Câu chuyện bắt đầu: từ một CASE nhỏ
Tháng 5/2024, team BI được yêu cầu cập nhật rule commission mới:
Shopee: 3% với đơn < 500k, 6% với đơn ≥ 500k
Lazada: vẫn giữ 4%
TikTok: 5%, nhưng nếu seller tier A thì giảm 1%
Bạn mở SQL lên, và bắt đầu:
CASE
WHEN platform = 'Shopee' AND booking_value < 500000 THEN booking_value * 0.03
WHEN platform = 'Shopee' AND booking_value >= 500000 THEN booking_value * 0.06
WHEN platform = 'Lazada' THEN booking_value * 0.04
WHEN platform = 'TikTok' AND seller_tier = 'A' THEN booking_value * 0.04
WHEN platform = 'TikTok' THEN booking_value * 0.05
END AS commission_fee
Chạy thử — ra kết quả đúng.
Push vào Airflow — chạy ổn.
Dashboard cập nhật, ai cũng vui.
Cho đến tháng sau:
- Shopee thêm loại đơn “voucher subsidy” cần cộng thêm 0.5%.
- Lazada có chương trình đặc biệt “free shipping weekend”.
- TikTok đổi cấu trúc seller tier từ A/B/C thành numeric.
Và bạn bắt đầu thấy mình
copy-paste thêm 10 dòng CASE WHEN mới.
Query 100 dòng biến thành 500 dòng.
Và pipeline lại fail khi một ELSE quên đóng ngoặc.
2. Khi CASE WHEN trở thành cái bẫy
CASE WHEN là công cụ tuyệt vời – giúp bạn xử lý logic ngay trong SQL.
Nhưng nó không sinh ra để chứa business rule phức tạp.
Dấu hiệu “CASE bị lạm dụng”:
- Query > 300 dòng, trong đó 70% là CASE WHEN.
- Rule thay đổi → phải sửa ở nhiều nơi khác nhau.
- Developer mới đọc vào… không dám đụng.
- Không thể test từng rule riêng biệt.
CASE WHEN là procedural logic viết trong declarative language
—
SQL được thiết kế để “nói bạn muốn gì”, không phải “nói làm sao để làm nó”.
3. Cách viết CASE hiệu quả hơn (và sạch hơn)
3.1. Viết theo hướng "fail fast" – cái dễ, rõ, loại trừ trước
Thay vì lồng CASE WHEN hoặc viết chồng lên nhau, hãy xử
lý điều kiện dễ check nhất trước.
CASE
WHEN order_status IS NULL THEN 'Invalid'
WHEN payment_status = 'Failed' THEN 'Mismatch'
WHEN commission_fee IS NULL THEN 'Missing Fee'
ELSE 'Valid'
END AS flag_status
- Dễ đọc hơn.
- Tư duy “điều gì sai nhất” luôn đứng trước — rất hợp với reconcile logic.
3.2. Gom nhóm CASE WHEN bằng bảng trung gian
Nếu bạn thấy CASE của mình bắt đầu lặp logic,
hãy đưa rule ra một bảng riêng để join.
| platform | fee_type | rate |
|---|---|---|
| Shopee | normal | 0.05 |
| Shopee | promo | 0.03 |
| Lazada | normal | 0.04 |
| TikTok | normal | 0.05 |
Thay vì viết CASE:
CASE
WHEN platform = 'Shopee' AND is_promo THEN 0.03
WHEN platform = 'Shopee' THEN 0.05
WHEN platform = 'Lazada' THEN 0.04
WHEN platform = 'TikTok' THEN 0.05
END AS rate
Bạn chỉ cần:
SELECT o.*, r.rate
FROM orders o
JOIN config_rate r
ON o.platform = r.platform
AND o.fee_type = r.fee_type;
Giờ thay đổi rule chỉ cần update bảng config,
không cần deploy lại code.
3.3. Chia CASE lớn thành nhiều CASE nhỏ
Nếu logic gồm nhiều phần (commission, shipping, subsidy...),
hãy tách ra từng cột riêng.
CASE
WHEN platform = 'Shopee' THEN nmv * 0.05
WHEN platform = 'Lazada' THEN nmv * 0.04
END AS commission_fee,
CASE
WHEN platform = 'Shopee' THEN -15000
WHEN platform = 'Lazada' THEN -12000
END AS shipping_subsidy
Đừng gộp tất cả trong một CASE “đa năng” chỉ vì muốn gọn —
đến khi debug, bạn sẽ hối hận.
3.4. Sử dụng function khi logic lặp đi lặp lại
Giả sử trong 5 query khác nhau, bạn đều phải check:
CASE
WHEN flag_platform_order != 'Matched' THEN 'Mismatch'
WHEN flag_wallet != 'Matched' THEN 'Mismatch'
WHEN flag_nmv != 'Matched' THEN 'Mismatch'
ELSE 'Matched'
END AS flag_all
Thì hãy viết function:
CREATE OR REPLACE FUNCTION f_flag_all(
flag_platform_order TEXT,
flag_wallet TEXT,
flag_nmv TEXT
)
RETURNS TEXT AS $$
BEGIN
IF flag_platform_order != 'Matched' THEN
RETURN 'Mismatch';
ELSIF flag_wallet != 'Matched' THEN
RETURN 'Mismatch';
ELSIF flag_nmv != 'Matched' THEN
RETURN 'Mismatch';
END IF;
RETURN 'Matched';
END;
$$ LANGUAGE plpgsql;
Và gọi gọn lại:
SELECT f_flag_all(flag_platform_order, flag_wallet, flag_nmv);
4. Một case thực tế – Reconcile rule Shopee
Trước khi tối ưu:
CASE
WHEN platform = 'Shopee' AND flag_platform_order != 'Matched' THEN 'Mismatch'
WHEN platform = 'Shopee' AND flag_nmv != 'Matched' THEN 'Mismatch'
WHEN platform = 'Shopee' AND flag_wallet != 'Matched' THEN 'Mismatch'
ELSE 'Matched'
END AS flag_all
Sau khi tối ưu:
Tách điều kiện platform ra ngoài, và dùng function cho phần flag.
CASE
WHEN platform = 'Shopee'
THEN f_flag_all(flag_platform_order, flag_wallet, flag_nmv)
ELSE flag_nmv
END AS flag_all
Cấu trúc SQL trở nên rõ ràng, có tầng, và dễ kiểm soát:
- Query chỉ chứa logic join + select.
- Function chứa rule xử lý flag.
- Config chứa rule platform-specific.
5. CASE WHEN không xấu – chỉ là nó đang làm việc sai chỗ
CASE WHEN tuyệt vời nếu bạn:
- cần mapping nhanh (ví dụ region code, channel, flag đơn giản),
- cần tính toán tạm thời trong view,
- cần derive cột nhỏ (ví dụ phân nhóm giá trị, bucket hóa).
Nhưng nó sai chỗ nếu:
- bạn dùng để định nghĩa rule tài chính,
- hoặc chứa logic nghiệp vụ thay đổi thường xuyên.
Nếu rule có thể thay đổi, hãy tách nó ra khỏi query.
CASE chỉ nên phục vụ cho việc đọc dữ liệu, không phải mô phỏng hệ thống.
6. Cách viết CASE “sạch” – checklist cho data team
| Mục tiêu | Cách làm |
|---|---|
| Query ngắn | Chia CASE theo cột, không gộp nhiều tầng |
| Dễ bảo trì | Function hóa logic phức tạp |
| Rule thay đổi | Dùng bảng config hoặc file YAML/CSV |
| Debug nhanh | Check điều kiện sai trước, return sớm |
| Hiệu năng | Tránh dùng CASE trong JOIN condition, thay bằng mapping table |
0 Nhận xét