SQL Server 標量 UDF 並行性之謎!
在我最喜歡的 SQL Server 大師 Brent Ozar 的網站上引用Erik Darling 的這篇博文:
當您單獨從該表中選擇時,它會顯示“ CouldNotGenerateValidParallelPlan ”。
但是,當您將該表連接到另一個沒有呼叫標量 UDF 的檢查約束/計算列的表時,查詢將與“找到足夠好的計劃”並行
USE tempdb; SET NOCOUNT ON; SELECT TOP 10000 ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL )) AS ID, DATEADD(MINUTE, m.message_id, SYSDATETIME()) AS SomeDate INTO dbo.constraint_test_1 FROM sys.messages AS m, sys.messages AS m2; GO SELECT TOP 10000 ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL )) AS ID, DATEADD(MINUTE, m.message_id, SYSDATETIME()) AS SomeDate INTO dbo.constraint_test_2 FROM sys.messages AS m, sys.messages AS m2; GO CREATE FUNCTION dbo.DateCheck ( @d DATETIME2(7)) RETURNS BIT WITH RETURNS NULL ON NULL INPUT AS BEGIN DECLARE @Out BIT; SELECT @Out = CASE WHEN @d < DATEADD(DAY, 30, SYSDATETIME()) THEN 1 ELSE 0 END; RETURN @Out; END; GO ALTER TABLE dbo.constraint_test_1 ADD CONSTRAINT ck_cc_dt CHECK ( dbo.DateCheck(SomeDate) = 1 ); SELECT * FROM dbo.constraint_test_1 OPTION (QUERYTRACEON 8649, MAXDOP 0, RECOMPILE); -- Does not go parallel SELECT T1.ID, T2.SomeDate FROM dbo.constraint_test_1 T1 INNER JOIN dbo.constraint_test_2 T2 ON T1.ID = T2.ID OPTION (QUERYTRACEON 8649, MAXDOP 0, RECOMPILE); -- Goes parallel
如果您想要並行性,則需要一個重要的計劃,而且優化器必須能夠為查詢找到有效的並行計劃。
例如,修改您的第一個查詢(不使用並行性):
SELECT COUNT_BIG(*) FROM dbo.constraint_test_1 WHERE ID > (SELECT 0) OPTION (QUERYTRACEON 8649, MAXDOP 0, RECOMPILE); -- Goes parallel
我將其更改
SELECT *
為SELECT COUNT_BIG(*)
那里以避免使用標量 UDF 約束投影列。我還添加了一個WHERE
足夠複雜的子句,可以跳過瑣碎的計劃階段。由於約束,您的第二個查詢(確實是並行的)可以修改為始終串列:
SELECT T1.ID, T2.SomeDate, T1.SomeDate FROM dbo.constraint_test_1 T1 INNER JOIN dbo.constraint_test_2 T2 ON T1.ID = T2.ID OPTION (QUERYTRACEON 8649, MAXDOP 0, RECOMPILE); -- No parallel
我將T1.SomeDate添加到投影列表中,因此查詢現在使用了有問題的列。當不需要該列時,優化器會忽略對其的約束,因此可以找到並行計劃。
當程序檢測到會阻止並行性的條件時,可以在編譯早期添加NonParallelPlanReason 顯示計劃屬性。例如,以下查詢產生原因ParallelismDisabledByTraceFlag:
SELECT CT.ID FROM dbo.constraint_test_1 AS CT OPTION (QUERYTRACEON 8687);
非並行原因的存在並不意味著肯定會考慮並行性。優化器僅在瑣碎計劃階段之後考慮並行性,可選擇搜尋 0,並且肯定搜尋 1。有關優化器階段的更多資訊,請參閱我的Query Optimizer Deep Dive系列。
您在評論中提到您對使用並行性引用標量函式的計算列的行為感興趣。我在Properly Persisted Computed Columns中對此進行了描述。
最後,這裡不是特別關心,但如果你想測試這樣的事情,你最好在真正的使用者數據庫而不是tempdb中進行。在tempdb中有幾件事情的工作方式不同。