Cái nào hiệu quả hơn, CTE
hay Temporary Tables
?
Cái nào hiệu quả hơn, CTE
hay Temporary Tables
?
Câu trả lời:
Tôi muốn nói rằng chúng là những khái niệm khác nhau nhưng không quá khác nhau để nói "phấn và phô mai".
Một bảng tạm thời là tốt để sử dụng lại hoặc để thực hiện nhiều lần xử lý trên một tập hợp dữ liệu.
Một CTE có thể được sử dụng để tái diễn hoặc đơn giản là cải thiện khả năng đọc.
Và, như một khung nhìn hoặc hàm nội tuyến có giá trị cũng có thể được coi như một macro được mở rộng trong truy vấn chính
Một bảng tạm thời là một bảng khác với một số quy tắc xung quanh phạm vi
Tôi đã lưu trữ procs nơi tôi sử dụng cả hai (và cả biến bảng)
cte vs temporary tables
vì vậy IMHO câu trả lời này cần làm nổi bật những nhược điểm của CTE. TL; DR của câu trả lời được liên kết: không bao giờ nên sử dụng CTE cho hiệu suất. . Tôi đồng ý với trích dẫn đó vì tôi đã trải qua những nhược điểm của CTE.
Nó phụ thuộc.
Đầu tiên
Biểu thức bảng chung là gì?
CTE (không đệ quy) được xử lý rất giống với các cấu trúc khác cũng có thể được sử dụng làm biểu thức bảng nội tuyến trong SQL Server. Các bảng có nguồn gốc, dạng xem và hàm nội tuyến có giá trị. Lưu ý rằng trong khi BOL nói rằng CTE "có thể được coi là tập kết quả tạm thời" thì đây là một mô tả hoàn toàn hợp lý. Thường xuyên hơn không, nó không phải là vật chất của chính nó.
Một bảng tạm thời là gì?
Đây là một tập hợp các hàng được lưu trữ trên các trang dữ liệu trong tempdb. Các trang dữ liệu có thể nằm một phần hoặc toàn bộ trong bộ nhớ. Ngoài ra, bảng tạm thời có thể được lập chỉ mục và có số liệu thống kê cột.
Kiểm tra dữ liệu
CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);
INSERT INTO T(B)
SELECT TOP (1000000) 0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
master..spt_values v2;
ví dụ 1
WITH CTE1 AS
(
SELECT A,
ABS(B) AS Abs_B,
F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780
Lưu ý trong kế hoạch trên không có đề cập đến CTE1. Nó chỉ truy cập trực tiếp vào các bảng cơ sở và được xử lý giống như
SELECT A,
ABS(B) AS Abs_B,
F
FROM T
WHERE A = 780
Viết lại bằng cách cụ thể hóa CTE thành một bảng tạm thời trung gian ở đây sẽ phản tác dụng ồ ạt.
Cụ thể hóa định nghĩa CTE của
SELECT A,
ABS(B) AS Abs_B,
F
FROM T
Sẽ liên quan đến việc sao chép khoảng 8GB dữ liệu vào một bảng tạm thời, sau đó vẫn còn chi phí lựa chọn từ đó.
Ví dụ 2
WITH CTE2
AS (SELECT *,
ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM T
WHERE B % 100000 = 0)
SELECT *
FROM CTE2 T1
CROSS APPLY (SELECT TOP (1) *
FROM CTE2 T2
WHERE T2.A > T1.A
ORDER BY T2.A) CA
Ví dụ trên mất khoảng 4 phút trên máy của tôi.
Chỉ có 15 hàng trong số 1.000.000 giá trị được tạo ngẫu nhiên khớp với vị từ nhưng việc quét bảng đắt tiền xảy ra 16 lần để xác định vị trí này.
Đây sẽ là một ứng cử viên tốt để cụ thể hóa kết quả trung gian. Việc viết lại bảng tạm thời tương đương mất 25 giây.
INSERT INTO #T
SELECT *,
ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM T
WHERE B % 100000 = 0
SELECT *
FROM #T T1
CROSS APPLY (SELECT TOP (1) *
FROM #T T2
WHERE T2.A > T1.A
ORDER BY T2.A) CA
Việc cụ thể hóa trung gian một phần của truy vấn vào bảng tạm thời đôi khi có thể hữu ích ngay cả khi nó chỉ được đánh giá một lần - khi nó cho phép phần còn lại của truy vấn được biên dịch lại, tận dụng các số liệu thống kê về kết quả cụ thể hóa. Một ví dụ về cách tiếp cận này là trong bài viết SQL Cat Khi phá vỡ các truy vấn phức tạp .
Trong một số trường hợp, SQL Server sẽ sử dụng một bộ đệm để lưu trữ một kết quả trung gian, ví dụ như CTE và tránh phải đánh giá lại cây con đó. Điều này được thảo luận trong mục Kết nối (di chuyển) Cung cấp một gợi ý để buộc vật chất hóa trung gian của CTE hoặc các bảng dẫn xuất . Tuy nhiên, không có số liệu thống kê nào được tạo ra về điều này và ngay cả khi số lượng các hàng được lưu trữ khác biệt nhiều so với ước tính thì không thể thực hiện kế hoạch thực hiện tiến trình để thích ứng linh hoạt trong phản ứng (ít nhất là trong các phiên bản hiện tại. tương lai).
CTE có công dụng của nó - khi dữ liệu trong CTE nhỏ và có sự cải thiện khả năng đọc mạnh mẽ như với trường hợp trong các bảng đệ quy. Tuy nhiên, hiệu suất của nó chắc chắn không tốt hơn các biến của bảng và khi một người đang xử lý các bảng rất lớn, các bảng tạm thời vượt trội hơn đáng kể so với CTE. Điều này là do bạn không thể xác định các chỉ mục trên CTE và khi bạn có lượng dữ liệu lớn yêu cầu tham gia với một bảng khác (CTE đơn giản giống như một macro). Nếu bạn đang tham gia nhiều bảng với hàng triệu bản ghi trong mỗi bảng, CTE sẽ hoạt động kém hơn đáng kể so với các bảng tạm thời.
Các bảng tạm thời luôn ở trên đĩa - miễn là CTE của bạn có thể được giữ trong bộ nhớ, rất có thể nó sẽ nhanh hơn (giống như một biến bảng).
Nhưng một lần nữa, nếu tải dữ liệu của CTE (hoặc biến bảng tạm thời) của bạn quá lớn, nó cũng sẽ được lưu trữ trên đĩa, do đó không có lợi ích lớn.
Nói chung, tôi thích CTE hơn bảng tạm thời vì nó đã biến mất sau khi tôi sử dụng nó. Tôi không cần phải suy nghĩ về việc bỏ nó một cách rõ ràng hay bất cứ điều gì.
Vì vậy, cuối cùng không có câu trả lời rõ ràng, nhưng cá nhân tôi, tôi thích CTE hơn các bảng tạm thời.
Vì vậy, truy vấn tôi được chỉ định để tối ưu hóa được viết bằng hai CTE trong máy chủ SQL. Mất 28 giây.
Tôi đã dành hai phút để chuyển đổi chúng thành các bảng tạm thời và truy vấn mất 3 giây
Tôi đã thêm một chỉ mục vào bảng tạm thời trên trường mà nó đang được tham gia và giảm xuống còn 2 giây
Ba phút làm việc và bây giờ nó chạy nhanh hơn 12 lần bằng cách loại bỏ CTE. Cá nhân tôi sẽ không sử dụng CTE bao giờ họ cũng khó gỡ lỗi hơn.
Điều điên rồ là các CTE đều chỉ được sử dụng một lần và vẫn đưa ra một chỉ số cho chúng nhanh hơn 50%.
CTE sẽ không chiếm bất kỳ không gian vật lý nào. Nó chỉ là một tập kết quả chúng ta có thể sử dụng tham gia.
Bảng tạm thời là tạm thời. Chúng ta có thể tạo các chỉ mục, các ràng buộc như các bảng thông thường mà chúng ta cần xác định tất cả các biến.
Phạm vi của bảng tạm thời chỉ trong phiên. EX: Mở hai cửa sổ truy vấn SQL
create table #temp(empid int,empname varchar)
insert into #temp
select 101,'xxx'
select * from #temp
Chạy truy vấn này trong cửa sổ đầu tiên, sau đó chạy truy vấn bên dưới trong cửa sổ thứ hai, bạn có thể tìm thấy sự khác biệt.
select * from #temp
Tôi đã sử dụng cả hai nhưng trong các quy trình phức tạp lớn luôn tìm thấy các bảng tạm thời tốt hơn để làm việc và có phương pháp hơn. CTE có công dụng của chúng nhưng nhìn chung với dữ liệu nhỏ.
Ví dụ: tôi đã tạo các sprocs quay lại với kết quả tính toán lớn trong 15 giây nhưng chuyển đổi mã này để chạy trong CTE và đã thấy nó chạy quá 8 phút để đạt được kết quả tương tự.
Đến bữa tiệc muộn, nhưng ...
Môi trường tôi làm việc rất hạn chế, hỗ trợ một số sản phẩm của nhà cung cấp và cung cấp các dịch vụ "giá trị gia tăng" như báo cáo. Do các hạn chế về chính sách và hợp đồng, tôi thường không cho phép sự xa xỉ của không gian dữ liệu / bảng riêng biệt và / hoặc khả năng tạo mã vĩnh viễn [nó sẽ tốt hơn một chút, tùy thuộc vào ứng dụng].
IOW, tôi thường không thể phát triển một thủ tục được lưu trữ hoặc UDFs hoặc bảng tạm thời, v.v. Tôi gần như phải làm mọi thứ thông qua giao diện ứng dụng MY (Báo cáo Crystal - bảng thêm / liên kết, đặt các mệnh đề từ w / in CR, v.v. ). Một ân huệ tiết kiệm nhỏ là Crystal cho phép tôi sử dụng LỰA CHỌN (cũng như Biểu thức SQL). Một số điều không hiệu quả thông qua khả năng bảng add / link thông thường có thể được thực hiện bằng cách xác định Lệnh SQL. Tôi sử dụng CTE thông qua đó và đã nhận được kết quả rất tốt "từ xa". CTE cũng giúp w / báo cáo bảo trì, không yêu cầu phát triển mã, giao cho DBA để biên dịch, mã hóa, chuyển, cài đặt và sau đó yêu cầu thử nghiệm nhiều cấp độ. Tôi có thể làm CTE thông qua giao diện cục bộ.
Mặt trái của việc sử dụng CTE w / CR là, mỗi báo cáo là riêng biệt. Mỗi CTE phải được duy trì cho mỗi báo cáo. Khi tôi có thể thực hiện SP và UDF, tôi có thể phát triển thứ gì đó có thể được sử dụng bởi nhiều báo cáo, chỉ yêu cầu liên kết với SP và truyền tham số như thể bạn đang làm việc trên một bảng thông thường. CR không thực sự tốt trong việc xử lý các tham số vào các Lệnh SQL, do đó khía cạnh của khía cạnh CR / CTE có thể thiếu. Trong những trường hợp đó, tôi thường cố gắng xác định CTE để trả về đủ dữ liệu (nhưng không phải TẤT CẢ dữ liệu), sau đó sử dụng các khả năng chọn bản ghi trong CR để cắt và xúc xắc.
Vì vậy, ... phiếu bầu của tôi là dành cho CTE (cho đến khi tôi có được không gian dữ liệu của mình).
Một cách sử dụng mà tôi thấy hiệu suất vượt trội của CTE là ở chỗ tôi cần tham gia Truy vấn tương đối phức tạp vào một vài bảng có vài triệu hàng mỗi bảng.
Trước tiên, tôi đã sử dụng CTE để chọn tập hợp con dựa trên các cột được lập chỉ mục để trước tiên cắt các bảng này xuống còn vài nghìn hàng có liên quan và sau đó nối CTE vào truy vấn chính của tôi. Điều này giảm theo cấp số nhân thời gian chạy truy vấn của tôi.
Trong khi kết quả cho CTE không được lưu trong bộ nhớ cache và các biến bảng có thể là lựa chọn tốt hơn, tôi thực sự chỉ muốn dùng thử và tìm thấy sự phù hợp với kịch bản trên.
Tôi vừa thử nghiệm điều này - cả CTE và không CTE (trong đó truy vấn được gõ cho mọi trường hợp liên kết) cả hai mất ~ 31 giây. CTE làm cho mã dễ đọc hơn nhiều - cắt giảm từ 241 xuống 130 dòng rất đẹp. Mặt khác, bảng Temp đã cắt nó xuống còn 132 dòng và lấy FIVE GIÂY để chạy. Không phải trò đùa. tất cả các thử nghiệm này đã được lưu trữ - các truy vấn đều được chạy nhiều lần trước đó.
Từ kinh nghiệm của tôi trong SQL Server, tôi đã tìm thấy một trong những tình huống trong đó CTE vượt trội hơn bảng Temp
Tôi cần sử dụng Bộ dữ liệu (~ 100000) từ Truy vấn phức tạp chỉ ONCE trong Quy trình được lưu trữ của tôi.
Bảng tạm thời gây ra tình trạng quá tải trên SQL khi Quy trình của tôi hoạt động chậm (vì Bảng tạm thời là các bảng được vật chất hóa thực sự tồn tại trong tempdb và Kiên trì cho quy trình hiện tại của tôi)
Mặt khác, với CTE, CTE Chỉ tồn tại cho đến khi truy vấn sau được chạy. Vì vậy, CTE là một cấu trúc trong bộ nhớ tiện dụng với Phạm vi hạn chế. CTE không sử dụng tempdb theo mặc định.
Đây là một kịch bản trong đó các CTE thực sự có thể giúp đơn giản hóa mã của bạn và vượt trội hơn Bảng tạm thời. Tôi đã sử dụng 2 CTE, đại loại như
WITH CTE1(ID, Name, Display)
AS (SELECT ID,Name,Display from Table1 where <Some Condition>),
CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>)
SELECT CTE2.ID,CTE2.<col3>
FROM CTE2
GO