Bảng tạm thời là một cách cực hay để lưu kết quả trung gian, sau đó thao tác tiếp với chúng. Kiểu như trong lập trình ấy: thay vì copy-paste một đoạn code dài ngoằng, bạn lưu nó vào biến rồi dùng lại cho tiện. Trong thế giới SQL, mấy cái "biến" này thường chính là bảng tạm thời.
Nhưng CTE còn làm mọi thứ dễ hơn nữa — nó cho phép tạo bảng tạm thời mà không cần lằng nhằng:
Không cần lo xóa thủ công.
CTE chỉ tồn tại trong lúc query chạy thôi. SQL chạy xong là CTE biến mất luôn, kiểu như trợ lý tàng hình làm xong việc rồi lặn mất tăm, không để lại dấu vết gì.
Code dễ đọc hơn hẳn.
Tạo CTE dễ hơn nhiều so với việc tạo rồi xóa bảng tạm vật lý. Mọi thứ rõ ràng, nhìn phát hiểu ngay.
Quá hợp cho mấy task "một lần rồi thôi".
Nếu chỉ cần xử lý dữ liệu ở một bước — CTE là best choice. Nhanh, gọn, không side effect.
Cú pháp CTE như bảng tạm thời
Làm mới lại cú pháp CTE nhé:
WITH cte_name AS (
SELECT ...
)
SELECT ...
FROM cte_name;
Ở đây WITH tạo ra một "biểu thức bảng" tạm thời, dùng được trong toàn bộ query mà nó được định nghĩa. Nhìn thì như bảng thật, nhưng nó chỉ sống tới khi query chạy xong thôi.
Ví dụ: Tính danh sách sinh viên xuất sắc
Cùng tạo bảng tạm bằng CTE để tìm sinh viên có điểm trung bình (grade) trên 90. Sau đó in ra danh sách họ nhé.
WITH high_achievers AS (
SELECT student_id, AVG(grade) AS avg_grade
FROM grades
GROUP BY student_id
HAVING AVG(grade) > 90
)
SELECT *
FROM high_achievers;
Ở đây mình:
- dùng
WITHđể tạo bảng tạmhigh_achievers. - trong CTE, gom nhóm điểm (
grades) theo từng sinh viên (student_id) rồi tính điểm trung bình. - ở query
SELECT *chỉ cần gọi bảng tạmhigh_achieversnhư bảng thường thôi.
So sánh CTE và bảng tạm thời
Đôi khi sẽ có câu hỏi: khác gì giữa dùng CTE và bảng tạm tạo bằng CREATE TEMP TABLE?
Đây là ví dụ bảng tạm truyền thống (CREATE TEMP TABLE) cho cùng dữ liệu:
CREATE TEMP TABLE high_achievers_temp AS
SELECT student_id, AVG(grade) AS avg_grade
FROM grades
GROUP BY student_id
HAVING AVG(grade) > 90;
SELECT *
FROM high_achievers_temp;
DROP TABLE high_achievers_temp; -- Đừng quên xóa bảng nhé!
Và query tương tự dùng CTE:
WITH high_achievers AS (
SELECT student_id, AVG(grade) AS avg_grade
FROM grades
GROUP BY student_id
HAVING AVG(grade) > 90
)
SELECT *
FROM high_achievers;
Khi nào dùng CTE, khi nào dùng bảng tạm
CTE cực hợp khi bạn muốn chuẩn bị dữ liệu trung gian rồi thao tác luôn — khỏi lằng nhằng. Không cần lo xóa: CTE tự biến mất khi query xong. Nó hiện rõ trong cấu trúc SQL, không bị giấu như bảng tạm. Nhờ vậy query gọn, dễ hiểu, nhất là khi bạn xử lý nhiều bước liên tiếp. Ngoài ra, CTE còn có thể lồng nhau, kết hợp nhiều cái để xây logic phức tạp — phần này sẽ nói sau.
Cấu trúc này rất ổn khi query chỉ chạy một lần, dữ liệu chỉ cần trong phạm vi query đó. Nếu bạn muốn dùng kết quả nhiều lần ở các phần khác nhau của hệ thống, hoặc muốn giữ dữ liệu trung gian suốt phiên làm việc — bảng tạm sẽ đáng tin hơn. Đặc biệt nếu dữ liệu lớn và cần hiệu năng cao: lúc này bảng tạm vật lý chạy ổn định và nhanh hơn.
Tóm lại, tùy bài toán: CTE là công cụ nhanh, gọn cho xử lý dữ liệu cục bộ. Bảng tạm là "ngựa thồ" cho mấy kịch bản phức tạp, lâu dài.
Ví dụ: Tổng hợp dữ liệu
Giả sử có bảng enrollments lưu thông tin sinh viên đăng ký khóa học nào. Mình muốn biết mỗi khóa có bao nhiêu sinh viên, chỉ lấy khóa có hơn 5 sinh viên thôi nhé.
Dùng CTE làm như này:
WITH course_counts AS (
SELECT course_id, COUNT(student_id) AS student_count
FROM enrollments
GROUP BY course_id
HAVING COUNT(student_id) > 5
)
SELECT *
FROM course_counts
ORDER BY student_count DESC;
Nhiều CTE: Xây dựng từng bước
Nếu cần chia task thành nhiều bước thì sao? Ví dụ, đầu tiên chọn sinh viên có điểm trung bình cao, sau đó tìm các khóa học của họ? Dễ thôi!
WITH high_achievers AS (
SELECT student_id, AVG(grade) AS avg_grade
FROM grades
GROUP BY student_id
HAVING AVG(grade) > 90
),
student_courses AS (
SELECT e.student_id, c.course_name
FROM enrollments e
JOIN courses c ON e.course_id = c.course_id
)
SELECT ha.student_id, ha.avg_grade, sc.course_name
FROM high_achievers ha
JOIN student_courses sc ON ha.student_id = sc.student_id;
Bây giờ query có hai CTE:
high_achieverstìm sinh viên xuất sắc.student_coursesghép sinh viên với khóa học.
Kết quả là danh sách sinh viên điểm cao và các khóa học của họ.
Lỗi thường gặp khi dùng CTE
Dữ liệu quá lớn. CTE được lưu trong bộ nhớ PostgreSQL. Nếu tạo CTE với kết quả khổng lồ, query sẽ chậm hoặc thậm chí hết RAM.
Lạm dụng. Dùng CTE ở chỗ subquery thường là đủ sẽ làm mọi thứ rối rắm hơn.
Quên index. Nếu dữ liệu trong CTE lấy từ bảng lớn mà không có index, query sẽ rất chậm.
GO TO FULL VERSION