使用CTE和标量函数进行查询的错误查询计划

时间:2018-08-15 14:37:06

标签: sql-server tsql user-defined-functions common-table-expression

我对使用CTE来缩小结果的非常大的表的查询效果不佳。 CTE最多可产生三行,它是通过从源表中获取最大合计值,然后使用CROSS APPLY和标量函数来产生所需的列而产生的。

有趣的是,如果我将CTE中的数据加载到一个表变量中并将该表变量连接到源表中,那么查询的性能将大大提高。

查询计划非常不同:

  • CTE版本具有一个嵌套循环连接,该连接指定“警告:无连接谓词”。然后,它具有一个过滤器,该过滤器随后使用我认为应该是联接谓词的方式。
  • 表变量版本具有两个嵌套循环联接,并且没有筛选操作。

解决此问题的方法是什么,以便我可以只使用CTE-可能是某种查询或联接提示?

-- table variable for alt version of query
-- DECLARE @PeriodsTbl TABLE (PeriodCode varchar(10), PeriodDate date, PeriodDateKey int);

WITH PeriodsSource AS (
    SELECT forPeriods.*, forPeriodDateKeys.*
    FROM (SELECT dbo.DateKeyToDate(MAX(EndDateKey)) MaxDate FROM BigTable) forMaxDate
    CROSS APPLY (
        SELECT x.*, dbo.DateToDatekey(x.PeriodDate) PeriodDateKey
        FROM ( VALUES
            ( 'cur', CAST(MaxDate AS DATE) )
            , ( 'pw', CAST(DATEADD(day, -7, MaxDate) AS DATE) )
            , ( 'py', CAST(DATEADD(day, -364, MaxDate) AS DATE) )
        ) x ( PeriodCode, PeriodDate )
    ) forPeriods
)
--INSERT INTO @PeriodsTbl 
--SELECT PeriodCode, PeriodDate, PeriodDateKey 
--FROM PeriodsSource ps;
SELECT *
FROM PeriodsSource ps -- Use @PeriodsTbl For Table Version
INNER JOIN BigTable bt ON bt.StartDateKey <= ps.PeriodDateKey 
    AND bt.EndDateKey >= ps.PeriodDateKey

1 个答案:

答案 0 :(得分:1)

我的猜测是性能问题是由于标量函数的自由使用。从性能角度来看,它们通常很糟糕。将这些转换为内联表值函数将是我的第一步。但是请确保它们不是多语句表值函数,否则您实际上可能会倒退以提高性能。或者,如果函数非常简单,则可以内联逻辑,这会更好。

编辑

在联接中使用标量函数导致计划从更有效的联接变为一个联接,而无需谓词和之后的过滤。 PeriodDateKey是在CTE中由函数DateToDatekey生成的,这就是BigTable所要连接的内容。将其更改为功能上等效的表达式会导致计划发生更改,并产生了很大的改进。

尝试将函数调用移至CROSS APPLY之外并没有带来任何改善,并且尽管删除了对标量函数的所有引用都产生了性能提升,但它不如对PeriodDateKey进行一次更改所实现的增益重要。 / p>