SQL Server 标量 UDF 并行性之谜!

Ani*_*van 6 sql-server parallelism check-constraints computed-column

引用Erik Darling 在我最喜欢的 SQL Server 大师 Brent Ozar 的网站上发表的这篇博客文章:

当您单独从该表中进行选择时,它会显示“ CouldNotGenerateValidParallelPlan ”。

但是,当您将该表连接到另一个没有调用标量 UDF 的检查约束/计算列的表时,查询与“ Good Enough Plan Found ”并行

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
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 8

如果您想要并行性,则需要一个重要的计划,而且优化器必须能够为查询找到有效的并行计划。

例如,修改您的第一个查询(未使用并行性):

SELECT  COUNT_BIG(*)
FROM    dbo.constraint_test_1
WHERE   ID > (SELECT 0)
OPTION  (QUERYTRACEON 8649, MAXDOP 0, RECOMPILE); -- Goes parallel
Run Code Online (Sandbox Code Playgroud)

平行线

我将 更改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
Run Code Online (Sandbox Code Playgroud)

不平行

我将T1.SomeDate添加到投影列表中,因此查询现在使用有问题的列。当不需要该列时,优化器将忽略对其的约束,因此它可以找到并行计划。

当进程检测到阻止并行性的条件时 ,可以在编译早期添加NonParallelPlanReason显示计划属性。例如,以下查询生成原因ParallelismDisabledByTraceFlag

SELECT CT.ID 
FROM dbo.constraint_test_1 AS CT 
OPTION (QUERYTRACEON 8687);
Run Code Online (Sandbox Code Playgroud)

非并行原因的存在并不意味着肯定会以其他方式考虑并行性。优化器仅在简单计划阶段之后考虑并行性,可选搜索 0,并且肯定搜索 1。有关优化器阶段的更多信息,请参阅我的查询优化器深入研究系列。

您在评论中提到您对引用并行标量函数的计算列的行为感兴趣。我在正确保留计算列中对此进行了描述。

最后,这里不是特别关心的问题,但如果您想测试这样的事情,最好在真实的用户数据库而不是tempdb中进行。tempdb中有几处工作方式不同。