Khi SQL Không Còn Scale: Câu Chuyện Query Vỡ Trận và Bài Học Cho Team BI

1. Mở đầu: Một buổi sáng và cú query “vỡ trận”

Có một buổi sáng như mọi ngày.
Bạn mở laptop, định chỉnh nhẹ vài dòng SQL cho dashboard “Doanh thu theo nền tảng”.
Chỉ vài dòng thôi — thêm TikTok vào logic tính phí.

CASE
  WHEN platform = 'Shopee' THEN commission_fee * 0.9
  WHEN platform = 'Lazada' THEN commission_fee * 0.85
  WHEN platform = 'TikTok' THEN commission_fee * 0.88
END AS adjusted_fee

Chạy thử. Dashboard lỗi.

Bạn lùi lại kiểm tra: query dài 400 dòng, mỗi phần một kiểu CASE WHEN.
Bảng config thì thêm 3 cột mới.
Cả pipeline Airflow phía sau nổ tung vì schema không khớp.

Cảm giác quen không?
Đó chính là khoảnh khắc mọi đội BI đều gặp một lần trong đời:

“Query chạy được” không đồng nghĩa với “Query có thể scale”.

2. Cội nguồn của vấn đề: Khi business logic bị nhúng vào SQL

SQL vốn sinh ra để truy vấn dữ liệu, không phải lưu trữ business rule.
Thế nhưng, trong thực tế, gần như mọi dự án data ở giai đoạn đầu đều bắt đầu bằng:

“Cứ viết CASE trong query đi, chạy được đã!”

Và thế là:

  • Fee khác nhau giữa Shopee và Lazada → CASE WHEN platform = ...
  • Chính sách rebate mỗi tháng khác nhau → CASE WHEN month = ...
  • Cập nhật đặc biệt cho 1 brand → thêm AND brand_name != 'Test'

Mỗi logic mới là một lớp “băng keo” dán thêm vào query.
Khi có 10 nền tảng, 20 chương trình, 50 sản phẩm, query ban đầu 100 dòng bỗng thành 1.000 dòng.
Không ai dám động vào nữa.
Cập nhật 1 dòng có thể làm dashboard chết hàng loạt.

3. “Tôi đã tách config ra bảng riêng, sao vẫn vỡ?”

Sau vài lần “toang”, đội bạn quyết định “chuẩn hóa”:

Tạo một bảng config riêng để lưu các rule.

Ví dụ:

SELECT o.order_id, o.platform, o.total_amount * c.rate AS commission_fee
FROM orders o
LEFT JOIN config_commission c ON o.platform = c.platform;

Nghe có vẻ chuyên nghiệp.
Nhưng rồi vài tháng sau, bảng config bắt đầu phình ra:

platform rate effective_date note override version
Shopee 0.05 2025-01-01 null v1
Lazada 0.04 2025-01-01 test true v2
TikTok 0.03 2025-02-01 new null v3

Cột mới, version mới, cấu trúc mới.
Query join theo platform không còn chạy đúng, vì:

  • Cột key đổi tên (platformchannel_code)
  • Có nhiều record cho cùng 1 platform → join ra nhiều dòng
  • Cần filter thêm effective_date → lại thêm WHERE

Kết quả:
Query ban đầu tưởng tách config để dễ quản lý, nhưng thực tế lại vỡ cấu trúc.
Vì SQL gốc vẫn đang phụ thuộc chặt vào schema của bảng config.

4. Khi hệ thống bắt đầu “không thể scale”

Hãy tưởng tượng bạn phải nhân đôi hệ thống:

  • 10 nền tảng thương mại điện tử.
  • 5 warehouse khác nhau.
  • 3 nhóm phí (commission, logistic, rebate).
  • 2 version bảng config (v1, v2 – mỗi team 1 bản).

Tổng cộng: 10 × 5 × 3 × 2 = 300 biến thể logic.

Với cách làm hiện tại, bạn sẽ cần:

  • 300 dòng CASE WHEN hoặc
  • 300 biến thể JOIN khác nhau.

Mỗi lần có logic mới, bạn phải:

  • Chỉnh query.
  • Test lại toàn bộ DAG.
  • Re-deploy dashboard.

Đó là lúc hệ thống không còn khả năng scale — không phải vì dữ liệu lớn, mà vì logic không tách lớp.

5. Dấu hiệu nhận biết “query sắp vỡ”

Một số dấu hiệu kinh điển:

Dấu hiệu Mô tả thực tế
CASE WHEN xuất hiện ở hơn 3 nơi Bạn đang hard-code logic lặp lại
Bảng config có hơn 10 cột, mỗi cột là một rule Config không còn là data, mà là logic disguised
Cần chỉnh query mỗi khi có brand mới Rule không còn tách biệt với business dimension
Không ai dám đụng vào file SQL gốc Query đã thành “black box”

6. Cốt lõi của vấn đề: SQL không có “semantic layer”



Cách bạn viết query cho thấy bạn đang làm việc ở tầng nào:

Tầng Mục tiêu Công cụ đúng
Raw Data Lưu trữ Tables
Transformation Chuẩn hoá, tính toán SQL / dbt / ETL
Semantic Layer Mô tả business rule View / Model / Config
Visualization Hiển thị Power BI / Looker / Tableau

Phần lớn team BI mắc lỗi:

Gộp cả 3 tầng đầu vào 1 query duy nhất.

Hậu quả: query vừa lấy dữ liệu, vừa tính toán, vừa chứa logic dẫn đến không thể maintain, không thể scale, không thể audit.

7. Một ví dụ thực tế từ dự án BI

Trong dự án ERP reconciliation, có logic:

  • Nếu order Shopee, fee = NMV * 5%
  • Nếu Lazada, fee = NMV * 4%
  • Nếu TikTok, fee = NMV * 3%
  • Nếu RTV (return-to-vendor), fee = 0

Query ban đầu:

SELECT order_id,
CASE
  WHEN platform = 'Shopee' THEN nmv * 0.05
  WHEN platform = 'Lazada' THEN nmv * 0.04
  WHEN platform = 'TikTok' THEN nmv * 0.03
  WHEN order_type = 'RTV' THEN 0
END AS platform_fee
FROM reconcile_data;

Sau 3 tháng, team thêm 2 nền tảng mới.
Thêm vài chương trình rebate.
Bảng query 200 dòng tăng thành 900 dòng.
Power BI mất 5 phút refresh.

Đến lúc đó, mọi người nhận ra:

Chúng ta không bị “chậm” vì dữ liệu, mà vì cách viết SQL của chính mình.

 Bạn đã từng gặp “query vỡ trận” tương tự chưa?Chia sẻ câu chuyện của bạn hoặc cách bạn tổ chức logic trong SQL tại phần bình luận nhé! 

Đăng nhận xét

1 Nhận xét

  1. Các dự án mình làm đều tách query cho 3 tầng, đối với team BI họ chỉ được lấy data từ view đã được define business rule sẵn. Không biết bạn lấy số liệu từ đầu mà khẳng định phần lơn team BI đều gộp cả 3 tầng vào 1 query nhỉ

    Trả lờiXóa