在我最喜欢的 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 *
为SELECT COUNT_BIG(*)
那里以避免使用标量 UDF 约束投影列。我还添加了一个WHERE
足够复杂的子句,可以跳过琐碎的计划阶段。由于约束,您的第二个查询(确实是并行的)可以修改为始终串行:
我将T1.SomeDate添加到投影列表中,因此查询现在使用了有问题的列。当不需要该列时,优化器会忽略对其的约束,因此可以找到并行计划。
当进程检测到会阻止并行性的条件时,可以在编译早期添加NonParallelPlanReason 显示计划属性。例如,以下查询产生原因ParallelismDisabledByTraceFlag:
非并行原因的存在并不意味着肯定会考虑并行性。优化器仅在琐碎计划阶段之后考虑并行性,可选择搜索 0,并且肯定搜索 1。有关优化器阶段的更多信息,请参阅我的Query Optimizer Deep Dive系列。
您在评论中提到您对使用并行性引用标量函数的计算列的行为感兴趣。我在Properly Persisted Computed Columns中对此进行了描述。
最后,这里不是特别关心,但如果你想测试这样的事情,你最好在真正的用户数据库而不是tempdb中进行。在tempdb中有几件事情的工作方式不同。