Sql-Server

SQL Server 標量 UDF 並行性之謎!

  • February 3, 2022

在我最喜歡的 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中有幾件事情的工作方式不同。

引用自:https://dba.stackexchange.com/questions/306925