加入 table 和临时 table 时执行计划中的常量扫描和计算标量

Constant scans and compute scalars in execution plan when joining table and temp table

我正在检查这条语句的执行时间是否可以缩短。执行计划绝对令人困惑。查询和计划如下。

临时 table 和正常 table 之间有一个连接,在 Date 列上有聚簇索引。 Table 包含约 1000 行,临时 table 超过 100 万。

在执行计划中,此连接反映在两个嵌套循环中,其中有许多奇怪的事情,例如参与该过程的常量扫描和计算标量。

这里的聚集索引查找占用了 74%,那里的估计行数是 1。实际行数很大。

    select t.StartDateTime
        , t.EndDateTime
        , t.StartDateTime_BusinessHours
        , t.EndDateTime_BusinessHours
        , count (c.Date) as DaysOffCount
    --into #DaysOff
    from #CorrectedStartEndDateTime as t
    left join util.Calendar as c on c. Date >= t.StartDateTime_BusinessHours and c. Date <= t.EndDateTime_BusinessHours and c.IsDayOff = 1
    group by t.StartDateTime
        , t.EndDateTime
        , t.StartDateTime_BusinessHours
        , t.EndDateTime_BusinessHours

这里是一个link执行计划:https://www.brentozar.com/pastetheplan/?id=r1crVr6rB

临时 table 是 'select into' 来自另一个查询的转储,没有任何索引。 table 看起来是这样的:

CREATE TABLE [util].[Calendar](
    [Date] [date] NOT NULL,
    [IsDayOff] [bit] NOT NULL,
    [DaysTillNextBusinessDay] [int] NOT NULL,
 CONSTRAINT [PK_Calendar] PRIMARY KEY CLUSTERED ([Date] ASC)
)

我希望这是一个简单的嵌套循环,介于临时 table 扫描和聚簇索引查找或任何类似的东西之间,你能帮我理解发生了什么以及为什么吗?

我不知道计划中这些额外运营商的所有细节,但它们非常便宜。它们很可能被添加到计划中,因为 Calendar.dateStartDateTime_BusinessHoursEndDateTime_BusinessHours 的类型不匹配。

您可以看到对内部函数的调用

GetRangeWithMismatchedTypes([tempdb].[dbo].[#CorrectedStartEndDateTime].[StartDateTime_BusinessHours] as [t].[StartDateTime_BusinessHours],NULL,(22))

在计划中。引擎必须以某种方式将 datedatetime 进行比较,并且引入了这些看起来很奇怪的运算符以避免将 date 转换为 datetime,以便仍然可以使用索引。

计划本身看起来不错。我怀疑您能否使 运行 显着加快。

有关 GetRangeWithMismatchedTypes 的更多详细信息,请参阅 https://dba.stackexchange.com/questions/128526/having-hard-time-understanding-expressions-in-below-query-plan


Calendar table 的索引查找中估计行数和实际行数存在很大差异,我认为这不是这个特定查询的问题。我认为这种错误估计不会使查询 运行 变慢。


您可以尝试使用 OUTER APPLY 重写查询以避免 GROUP BY。可能会快一点。

select 
    t.StartDateTime
    , t.EndDateTime
    , t.StartDateTime_BusinessHours
    , t.EndDateTime_BusinessHours
    , A.DaysOffCount
--into #DaysOff
from 
    #CorrectedStartEndDateTime as t
    OUTER APPLY
    (
        SELECT
            COUNT(*) AS DaysOffCount
        FROM
            util.Calendar as c
        WHERE
            c.Date >= t.StartDateTime_BusinessHours 
            and c.Date <= t.EndDateTime_BusinessHours 
            and c.IsDayOff = 1
    ) AS A
;