Kế hoạch được biên soạn trên phiên bản SQL Server 2008 R2 RTM (bản dựng 10.50.1600). Bạn nên cài đặt Gói dịch vụ 3 (bản dựng 10.50.6000), tiếp theo là các bản vá mới nhất để đưa nó lên bản dựng mới nhất (hiện tại) 10.50,6542. Điều này rất quan trọng vì một số lý do, bao gồm bảo mật, sửa lỗi và các tính năng mới.
Tối ưu hóa nhúng tham số
Liên quan đến câu hỏi hiện tại, SQL Server 2008 R2 RTM không hỗ trợ Tối ưu hóa nhúng tham số (PEO) cho OPTION (RECOMPILE)
. Ngay bây giờ, bạn đang trả chi phí biên dịch lại mà không nhận ra một trong những lợi ích chính.
Khi PEO khả dụng, SQL Server có thể sử dụng các giá trị bằng chữ được lưu trữ trong các biến và tham số cục bộ trực tiếp trong kế hoạch truy vấn. Điều này có thể dẫn đến đơn giản hóa đáng kể và tăng hiệu suất. Có nhiều thông tin hơn về điều đó trong bài viết của tôi, Thông số đánh hơi, nhúng và các tùy chọn RECOMPILE .
Hash, sắp xếp và trao đổi tràn
Chúng chỉ được hiển thị trong các kế hoạch thực hiện khi truy vấn được biên dịch trên SQL Server 2012 trở lên. Trong các phiên bản trước, chúng tôi phải theo dõi sự cố tràn trong khi truy vấn đang thực thi bằng Profiler hoặc Sự kiện mở rộng. Sự cố tràn luôn dẫn đến I / O vật lý đến (và từ) tempdb lưu trữ liên tục , điều này có thể gây ra hậu quả hiệu suất quan trọng, đặc biệt là nếu sự cố tràn lớn hoặc đường dẫn I / O chịu áp lực.
Trong kế hoạch thực hiện của bạn, có hai toán tử Hash Match (Uẩn). Bộ nhớ dành riêng cho bảng băm dựa trên ước tính cho các hàng đầu ra (nói cách khác, nó tỷ lệ thuận với số lượng nhóm được tìm thấy trong thời gian chạy). Bộ nhớ được cấp sẽ được sửa ngay trước khi bắt đầu thực thi và không thể phát triển trong khi thực thi, bất kể dung lượng bộ nhớ còn trống là bao nhiêu. Trong gói được cung cấp, cả hai toán tử Hash Match (Tổng hợp) tạo ra nhiều hàng hơn trình tối ưu hóa dự kiến và do đó có thể gặp phải sự cố tràn sang tempdb khi chạy.
Ngoài ra còn có một toán tử Hash Match (Internal Join) trong kế hoạch. Bộ nhớ dành riêng cho bảng băm dựa trên ước tính cho các hàng đầu vào phía đầu dò . Đầu vào đầu dò ước tính 847.399 hàng, nhưng gặp phải 1.223.636 khi chạy. Sự dư thừa này cũng có thể gây ra sự cố tràn băm.
Tổng hợp dự phòng
Kết hợp băm (Tổng hợp) tại nút 8 thực hiện thao tác nhóm trên (Assortment_Id, CustomAttrID)
, nhưng các hàng đầu vào bằng với các hàng đầu ra:

Điều này cho thấy sự kết hợp cột là một khóa (vì vậy việc phân nhóm là không cần thiết về mặt ngữ nghĩa). Chi phí thực hiện tổng hợp dự phòng được tăng lên do cần phải vượt qua 1,4 triệu hàng hai lần trên các trao đổi phân vùng băm (toán tử Parallelism ở hai bên).
Do các cột liên quan đến từ các bảng khác nhau, việc truyền thông tin duy nhất này đến trình tối ưu hóa trở nên khó khăn hơn bình thường, do đó nó có thể tránh được hoạt động nhóm dự phòng và trao đổi không cần thiết.
Phân phối luồng không hiệu quả
Như đã lưu ý trong câu trả lời của Joe Obbish , việc trao đổi tại nút 14 sử dụng phân vùng băm để phân phối các hàng giữa các luồng. Thật không may, số lượng hàng nhỏ và bộ lập lịch có sẵn có nghĩa là cả ba hàng kết thúc trên một chuỗi. Kế hoạch rõ ràng song song chạy ser seri (với chi phí song song) cho đến khi trao đổi tại nút 9.
Bạn có thể giải quyết vấn đề này (để có được phân vùng vòng tròn hoặc phân vùng phát sóng) bằng cách loại bỏ Phân loại riêng biệt tại nút 13. Cách dễ nhất để làm điều đó là tạo một khóa chính được nhóm trên #temp
bảng và thực hiện thao tác riêng biệt khi tải bảng:
CREATE TABLE #Temp
(
id integer NOT NULL PRIMARY KEY CLUSTERED
);
INSERT #Temp
(
id
)
SELECT DISTINCT
CAV.id
FROM @customAttrValIds AS CAV
WHERE
CAV.id IS NOT NULL;
Bảng tạm thời thống kê bộ nhớ đệm
Mặc dù sử dụng OPTION (RECOMPILE)
, SQL Server vẫn có thể lưu trữ đối tượng bảng tạm thời và các thống kê liên quan của nó giữa các lệnh gọi thủ tục. Đây thường là tối ưu hóa hiệu suất đáng hoan nghênh, nhưng nếu bảng tạm thời được điền với một lượng dữ liệu tương tự trên các cuộc gọi thủ tục liền kề, kế hoạch được biên dịch lại có thể dựa trên số liệu thống kê không chính xác (được lưu trong bộ thực thi trước đó). Điều này được trình bày chi tiết trong các bài viết của tôi, Các bảng tạm thời trong thủ tục lưu trữ và giải thích bộ đệm tạm thời của bảng tạm thời .
Để tránh điều này, hãy sử dụng OPTION (RECOMPILE)
cùng với một tường minh UPDATE STATISTICS #TempTable
sau khi bảng tạm thời được điền và trước khi nó được tham chiếu trong một truy vấn.
Truy vấn viết lại
Phần này giả định những thay đổi để tạo #Temp
bảng đã được thực hiện.
Với chi phí của sự cố tràn băm có thể và tổng hợp dự phòng (và các trao đổi xung quanh), có thể trả tiền để cụ thể hóa tập hợp tại nút 10:
CREATE TABLE #Temp2
(
CustomAttrID integer NOT NULL,
Assortment_Id integer NOT NULL,
);
INSERT #Temp2
(
Assortment_Id,
CustomAttrID
)
SELECT
ACAV.Assortment_Id,
CAV.CustomAttrID
FROM #temp AS T
JOIN dbo.CustomAttributeValues AS CAV
ON CAV.Id = T.id
JOIN dbo.AssortmentCustomAttributeValues AS ACAV
ON T.id = ACAV.CustomAttributeValue_Id;
ALTER TABLE #Temp2
ADD CONSTRAINT PK_#Temp2_Assortment_Id_CustomAttrID
PRIMARY KEY CLUSTERED (Assortment_Id, CustomAttrID);
Việc PRIMARY KEY
này được thêm vào trong một bước riêng biệt để đảm bảo việc xây dựng chỉ mục có thông tin chính xác về thẻ và để tránh sự cố bộ đệm thống kê bảng tạm thời.
Sự cụ thể hóa này hoàn toàn có khả năng xảy ra trong bộ nhớ (tránh I / O tempdb ) nếu thể hiện có đủ bộ nhớ khả dụng. Điều này thậm chí còn có khả năng hơn khi bạn nâng cấp lên SQL Server 2012 (SP1 CU10 / SP2 CU1 trở lên), đã cải thiện hành vi Eager Write .
Hành động này cung cấp cho trình tối ưu hóa thông tin chính xác của bộ tối ưu hóa trên tập trung gian, cho phép nó tạo số liệu thống kê và cho phép chúng tôi khai báo (Assortment_Id, CustomAttrID)
dưới dạng khóa.
Kế hoạch cho dân số #Temp2
sẽ giống như thế này (lưu ý quét chỉ mục theo cụm #Temp
, không có Phân loại riêng biệt và trao đổi hiện sử dụng phân vùng hàng vòng tròn):

Với bộ có sẵn, truy vấn cuối cùng trở thành:
SELECT
A.Id,
A.AssortmentId
FROM
(
SELECT
T.Assortment_Id
FROM #Temp2 AS T
GROUP BY
T.Assortment_Id
HAVING
COUNT_BIG(DISTINCT T.CustomAttrID) = @dist_ca_id
) AS DT
JOIN dbo.Assortments AS A
ON A.Id = DT.Assortment_Id
WHERE
A.AssortmentType = @asType
OPTION (RECOMPILE);
Chúng tôi có thể tự viết lại COUNT_BIG(DISTINCT...
một cách đơn giản COUNT_BIG(*)
, nhưng với thông tin khóa mới, trình tối ưu hóa thực hiện điều đó cho chúng tôi:

Gói cuối cùng có thể sử dụng phép nối / băm / hợp nhất tùy thuộc vào thông tin thống kê về dữ liệu mà tôi không có quyền truy cập. Một lưu ý nhỏ khác: Tôi giả sử rằng một chỉ mục như CREATE [UNIQUE?] NONCLUSTERED INDEX IX_ ON dbo.Assortments (AssortmentType, Id, AssortmentId);
tồn tại.
Dù sao, điều quan trọng về các kế hoạch cuối cùng là các ước tính sẽ tốt hơn nhiều và chuỗi hoạt động nhóm phức tạp đã được giảm xuống thành một Tập hợp luồng duy nhất (không yêu cầu bộ nhớ và do đó không thể tràn vào đĩa).
Thật khó để nói rằng hiệu suất thực sự sẽ tốt hơn trong trường hợp này với bảng tạm thời bổ sung, nhưng các ước tính và lựa chọn kế hoạch sẽ linh hoạt hơn nhiều đối với các thay đổi về khối lượng và phân phối dữ liệu theo thời gian. Điều đó có thể có giá trị hơn trong dài hạn so với mức tăng hiệu suất nhỏ hiện nay. Trong mọi trường hợp, bây giờ bạn có nhiều thông tin hơn để dựa vào quyết định cuối cùng của bạn.
#temp
sáng tạo và sử dụng sẽ là một vấn đề đối với hiệu suất, không tăng. Bạn đang lưu vào một bảng không được lập chỉ mục để được sử dụng một lần. Hãy thử loại bỏ nó hoàn toàn (và có thể thay đổi điều đóin (select id from #temp)
thànhexists
truy vấn phụ.