Các câu trả lời khác bao gồm các khác biệt về cú pháp khá tốt vì vậy tôi sẽ không đi sâu vào vấn đề đó. Thay vào đó, câu trả lời này sẽ chỉ bao gồm hiệu suất trong Oracle.
Trình tối ưu hóa Oracle có thể chọn cụ thể hóa kết quả của CTE thành một bảng tạm thời nội bộ. Nó sử dụng một heuristic để làm điều này thay vì tối ưu hóa dựa trên chi phí. Các heuristic là một cái gì đó như "Vật chất hóa CTE nếu nó không phải là một biểu thức tầm thường và CTE được tham chiếu nhiều lần trong truy vấn". Có một số truy vấn mà việc cụ thể hóa sẽ cải thiện hiệu suất. Có một số truy vấn mà việc cụ thể hóa sẽ làm giảm đáng kể hiệu suất. Ví dụ sau đây là một chút giả định nhưng nó minh họa điểm rất rõ:
Đầu tiên tạo một bảng có khóa chính chứa các số nguyên từ 1 đến 10000:
CREATE TABLE N_10000 (NUM_ID INTEGER NOT NULL, PRIMARY KEY (NUM_ID));
INSERT /*+APPEND */ INTO N_10000
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 10000
ORDER BY LEVEL;
COMMIT;
Hãy xem xét các truy vấn sau sử dụng hai bảng dẫn xuất:
SELECT t1.NUM_ID
FROM
(
SELECT n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
) t1
LEFT OUTER JOIN
(
SELECT n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
) t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;
Chúng tôi có thể xem truy vấn này và nhanh chóng xác định rằng nó sẽ không trả về bất kỳ hàng nào. Oracle cũng có thể sử dụng chỉ mục để xác định điều đó. Trên máy của tôi, truy vấn kết thúc gần như ngay lập tức với gói sau:
Tôi không muốn lặp lại chính mình, vì vậy hãy thử truy vấn tương tự với CTE:
WITH N_10000_CTE AS (
SELECT n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;
Đây là kế hoạch:
Đó là một kế hoạch thực sự tồi tệ. Thay vì sử dụng chỉ mục, Oracle cụ thể hóa 10000 X 10000 = 100000000 hàng thành một bảng tạm thời để cuối cùng trả về 0 hàng. Chi phí của kế hoạch này là khoảng 6 M, cao hơn nhiều so với truy vấn khác. Truy vấn mất 68 giây để hoàn thành trên máy của tôi.
Lưu ý rằng truy vấn có thể đã thất bại nếu không có đủ bộ nhớ hoặc không gian trống trong không gian bảng tạm thời.
Tôi có thể sử dụng INLINE
gợi ý không có giấy tờ để không cho phép trình tối ưu hóa thực hiện CTE:
WITH N_10000_CTE AS (
SELECT /*+ INLINE */ n1.NUM_ID
FROM N_10000 n1
CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;
Truy vấn đó có thể sử dụng chỉ mục và kết thúc gần như ngay lập tức. Chi phí của truy vấn vẫn giống như trước, 11. Vì vậy, đối với truy vấn thứ hai, heuristic được sử dụng bởi Oracle đã dẫn đến việc nó chọn một truy vấn có chi phí ước tính là 6 M thay vì truy vấn có chi phí ước tính là 11.
WITH...
). Bạn có thể viết lại mọi Bảng có nguồn gốc dưới dạng CTE, nhưng có thể không phải là cách khác (ví dụ: CTE đệ quy hoặc sử dụng CTE nhiều lần)