Khi xử lý các yêu cầu trích xuất dữ liệu kinh doanh phức tạp, hầu như mọi Data Analyst đều từng rơi vào cái bẫy của truy vấn lồng nhau (Nested Subqueries). Khi một câu lệnh SQL có tới 3-4 tầng ngoặc đơn lồng nhau, việc đọc hiểu luồng dữ liệu, gỡ lỗi (debug) hay bàn giao mã nguồn cho đồng nghiệp trở thành một thách thức lớn. CTE (Common Table Expression) xuất hiện như một giải pháp chuẩn mực giúp chuẩn hóa tư duy truy vấn: biến mã nguồn từ dạng “xoắn ốc lồng ghép” thành luồng dữ liệu tuyến tính từ trên xuống dưới.
Bài viết này cung cấp một hướng dẫn thực chiến toàn diện: từ bản chất thực thi của CTE trong Database Engine, phân tích so sánh với Subquery và Bảng tạm, cho đến cách tổ chức nhiều CTE (Multiple CTEs) để giải quyết bài toán đo lường doanh thu đơn hàng thứ hai (Repeat Purchase Revenue).
Theo tài liệu kỹ thuật chính thức từ Microsoft Learn (T-SQL Reference), CTE (Common Table Expression) là một tập kết quả tạm thời được đặt tên (temporary named result set), được phát sinh từ một câu truy vấn đơn giản và được định nghĩa trong phạm vi thực thi của một câu lệnh duy nhất như SELECT, INSERT, UPDATE, DELETE hoặc MERGE.
Ba đặc tính cốt lõi tạo nên sức mạnh của CTE:
- Phạm vi thực thi khép kín (Scope): CTE chỉ khả dụng ngay trong câu lệnh theo sau nó. Ngay khi câu lệnh chính kết thúc, kết quả tạm thời này sẽ tự động được giải phóng khỏi ngữ cảnh thực thi.
- Tái sử dụng nội bộ (Internal Reusability): Trong phạm vi của câu lệnh chính, bạn có thể gọi tên CTE nhiều lần (ví dụ: tự JOIN với chính nó) mà không cần phải viết lại toàn bộ khối mã nguồn.
- Khả năng đệ quy (Recursive CTE): CTE có thể tự tham chiếu lại chính nó, hỗ trợ giải quyết các bài toán dữ liệu dạng phân cấp (cây danh mục sản phẩm, cơ cấu tổ chức quản lý) mà các câu lệnh SQL thông thường khó xử lý.
2. Ma trận so sánh: CTE vs Subquery vs Bảng tạm (#Temp Table)
Một câu hỏi thường gặp khi đi phỏng vấn vị trí Data Analyst là: “Khi nào nên dùng CTE, khi nào nên dùng Subquery và khi nào phải dùng Bảng tạm?”. Dưới đây là bảng đối chiếu kỹ thuật chuẩn mực:
| Tiêu chí so sánh | Subquery (Truy vấn con) | CTE (WITH … AS) | Bảng tạm (#Temp Table) |
|---|---|---|---|
| Khả năng đọc hiểu | Rất khó đọc khi lồng > 2 cấp | Tối ưu nhất (Tuyến tính từ trên xuống) | Rõ ràng theo từng khối lệnh |
| Vị trí lưu trữ | Bộ nhớ tạm của Optimizer | Bộ nhớ tạm của Optimizer (Inline) | Lưu vật lý trong cơ sở dữ liệu tempdb |
| Khả năng tạo Index | Không hỗ trợ | Không hỗ trợ | Hỗ trợ tạo Clustered/Non-clustered Index |
| Phạm vi sử dụng | Cục bộ trong câu lệnh | Cục bộ trong 1 câu lệnh duy nhất | Toàn bộ phiên làm việc (Session) |
| Kịch bản tối ưu | Lọc giá trị nhanh trong WHERE (SELECT MAX...) |
Tổ chức phân tích nghiệp vụ 2-4 bước | Dữ liệu lớn (> 1 triệu dòng), tính toán phức tạp |
3. Cú pháp chuẩn T-SQL: Từ CTE đơn đến Multiple CTEs
a. Cú pháp CTE đơn (Single CTE)
Một CTE cơ bản luôn mở đầu bằng từ khóa WITH, theo sau là tên đặt cho CTE và câu lệnh truy vấn nằm trong cặp dấu ngoặc đơn (...):
b. Cú pháp nhiều CTE nối tiếp (Multiple CTEs)
Đây là kỹ thuật mạnh mẽ nhất của CTE: bạn có thể định nghĩa liên tiếp nhiều khối xử lý. Quy tắc quan trọng cần nhớ: chỉ sử dụng duy nhất một từ khóa WITH ở đầu, các CTE tiếp theo phân tách nhau bằng dấu phẩy ,. Đặc biệt, CTE đứng sau hoàn toàn có thể tham chiếu trực tiếp đến dữ liệu của các CTE đã định nghĩa phía trước:
4. Bài toán thực chiến: Phân tích doanh thu đơn hàng thứ hai (Repeat Purchase)
4.1 Bối cảnh kinh doanh & Schema dữ liệu mẫu
Trong ngành Bán lẻ và Thương mại điện tử (E-commerce), đơn hàng thứ 2 là cột mốc sống còn (inflection point). Đây là thời điểm phân định một người dùng là “khách mua thử một lần rồi rời bỏ” hay đã chuyển hóa thành “khách hàng quay lại (Repeat Customer)”.
Giả sử chúng ta có 2 bảng dữ liệu nguồn với cấu trúc mẫu như sau:
Bảng orders (Lịch sử giao dịch):
| order_id | customer_id | order_date | total_amount | order_status |
|---|---|---|---|---|
| ORD-101 | CUST-01 | 2026-01-10 | 500,000 | Completed |
| ORD-102 | CUST-01 | 2026-02-15 | 750,000 | Completed |
| ORD-103 | CUST-02 | 2026-01-20 | 300,000 | Completed |
| ORD-104 | CUST-02 | 2026-03-01 | 450,000 | Completed |
| ORD-105 | CUST-03 | 2026-02-05 | 600,000 | Cancelled |
Bảng customers (Hồ sơ khách hàng):
| customer_id | customer_name | city |
|---|---|---|
| CUST-01 | Nguyễn Văn An | Hà Nội |
| CUST-02 | Trần Thị Bình | TP. Hồ Chí Minh |
| CUST-03 | Lê Minh Cường | Đà Nẵng |
4.2 Viết truy vấn T-SQL với cấu trúc Multiple CTEs
Toàn bộ câu lệnh SQL được chia thành 2 CTE nối tiếp nhau trước khi tổng hợp ở truy vấn chính:
4.3 Bóc tách luồng dữ liệu từng bước và bảng kết quả
Cơ chế xử lý diễn ra như thế nào qua từng khối lệnh?
- Khối
cte_ranked_orders: Hàm Window FunctionROW_NUMBER()chia cụm dữ liệu theo từngcustomer_id(nhờPARTITION BY) và sắp xếp ngày mua tăng dần. Kết quả sinh ra cộtorder_sequence(1 cho đơn đầu, 2 cho đơn tiếp theo…). Đồng thời, điều kiệnWHERE order_status = 'Completed'loại trừ ngay các đơn hàng bị hủy trước khi tính thứ hạng. - Khối
cte_second_orders: Kế thừa tập dữ liệu đã đánh số từ bước 1 và chỉ lọc đúng điều kiệnorder_sequence = 2. Lúc này, chỉ những khách hàng thực sự quay lại mua lần 2 mới được giữ lại. - Khối SELECT cuối cùng: Thực hiện
INNER JOINvới bảng danh mụccustomersđể lấy chiều thông tin địa lý (city), áp dụng hàm tổng hợpCOUNT()đếm số lượng khách mua lại vàAVG()tính doanh thu trung bình.
Kết quả báo cáo đầu ra (Final Query Result):
| customer_city | repeat_customer_count | avg_second_order_revenue (VND) |
|---|---|---|
| Hà Nội | 1 | 750,000 |
| TP. Hồ Chí Minh | 1 | 450,000 |
5. Best Practices & 5 Lỗi thường gặp cần tránh
;WITH cte_name AS (...).#Temp_Table.COUNT(*), SUM()) mà quên đặt alias, hoặc JOIN 2 bảng có chung tên cột (ví dụ cùng có cột id).SELECT ... INTO #TempTable và tạo Clustered Index trên cột khóa chính để tối ưu hóa thời gian quét.OPTION (MAXRECURSION n) ở cuối câu lệnh để giới hạn số lần lặp an toàn.6. Tài liệu tham khảo chính thống
- Microsoft Learn: WITH common_table_expression (Transact-SQL)
– Tài liệu kỹ thuật chuẩn mực từ Microsoft về cú pháp, nguyên tắc hoạt động và hướng dẫn sử dụng CTE trong SQL Server.
- Mode Analytics: Use Common Table Expressions (CTE) to Keep Your SQL Clean
– Hướng dẫn thực hành chuẩn mực từ Mode Analytics về tư duy giữ cấu trúc mã SQL sạch sẽ, dễ bảo trì cho Data Team.
|
BI & AI Analytics
Làm chủ tư duy phân tích dữ liệu, tự động hóa xử lý và xây dựng báo cáo quản trị thông minh với Power BI và AI.
|
Python Data & Automation
Từ nền tảng lập trình đến phân tích dữ liệu thực chiến, xây dựng data pipeline và ứng dụng học máy vào kinh doanh.
|

