未选择过滤索引,并被提示拒绝

Ada*_*kis 5 sql-server index-tuning sql-server-2012 nonclustered-index filtered-index

我试图理解为什么非聚集索引在过滤时不适用于给定的查询。我的(大)查询的相关部分是这样的:

) results
JOIN BE_Insurance ins
ON results.PayorId = ins.Id
Run Code Online (Sandbox Code Playgroud)

在选择中,我只抓取 ins.name。我最初创建的索引是这样的:

CREATE INDEX IX_BE_Insurance_PayorId_PayorName
ON      BE_Insurance (Id) INCLUDE (Name)
WHERE ParentId IS NULL
Run Code Online (Sandbox Code Playgroud)

我的查询是这样的,只有带有 NULL parentId 的 payorIds 才会被选择,但我理解优化器不愿意选择它。但是当我添加一个提示来尝试强制索引时,整个事情就出错了。

由于此查询中定义的提示,查询处理器无法生成查询计划。在不指定任何提示且不使用 SET FORCEPLAN 的情况下重新提交查询。

我认为它会遵循我的提示,如果我发现一些错误的数据并且索引缺少一些所需的值,则可能会在执行时出错。从索引中删除过滤器会导致查询成功选择它(即使没有提示)。

带有 WHERE 子句的索引不适合这样的查询吗?只有当优化器可以保证过滤器有效并且不会产生任何缺失值时,它们才符合条件吗?


根据要求,这是整个查询,仍在进行中。

SELECT results.*
FROM (
        SELECT auths.*,
               worked.WorkedHours,
               worked.WorkedUnits,
               worked.WorkedAmount,
               worked.ActiveClients
        FROM (
                SELECT PayorId,
                       SUM(AuthHours) AuthHours, 
                       SUM(AuthUnits) AuthUnits, 
                       SUM(AuthAmount) AuthAmount
                FROM (
                        --DYNAMIC TEMPLATE ----------------------------------------------------------------------------------
                        SELECT PayorId,
                               PayorName,
                               AuthHours AuthHours, 
                               AuthUnits AuthUnits, 
                               AuthAmount AuthAmount
                        FROM PayorAuthorizations_Level1Data_Authorizations auths WITH(NOEXPAND)
                        WHERE OrganizationId = @organizationId AND StartDate <= @endDate AND @startDate <= EndDate
                        --/DYNAMIC TEMPLATE----------------------------------------------------------------------------------

                        --INTERSECT / EXCEPT dynamically generated queries 
                ) q
                GROUP BY PayorId
        ) auths
        JOIN (
                SELECT PayorId,
                       SUM(ISNULL(TotalWorkedHours, 0)) WorkedHours,
                       SUM(ISNULL(TotalWorkedUnits, 0)) WorkedUnits,
                       SUM(ISNULL(TotalWorkedAmount, 0)) WorkedAmount,
                       COUNT(DISTINCT clientId) ActiveClients
                FROM (
                        --DYNAMIC TEMPLATE ----------------------------------------------------------------------------------
                        SELECT PayorId,                   
                               TotalWorkedHours,
                               TotalWorkedUnits,
                               TotalWorkedAmount,
                               clientId clientId
                        FROM PayorAuthorizations_Level1Data_CurrentlyWorked worked WITH(NOEXPAND)
                        WHERE OrganizationId = @organizationId AND StartDate <= @endDate AND @startDate <= EndDate
                        --/DYNAMIC TEMPLATE----------------------------------------------------------------------------------

                        --INTERSECT / EXCEPT other dynamically generated queries 
                ) q
                GROUP BY PayorId
        ) worked
        ON auths.payorId = worked.payorId
) results
JOIN BE_Insurance ins WITH (INDEX(IX_BE_Insurance_PayorId_PayorName))
ON results.PayorId = ins.Id
OPTION(FORCE ORDER, MERGE JOIN)
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 8

我认为它会遵循我的提示,如果我发现一些错误的数据并且索引缺少一些所需的值,则可能会在执行时出错。

如果查询优化器可以保证(在其推理框架内)可以从索引中提供所有可能的匹配项,则查询优化器将仅在查询计划中使用过滤索引。这是设计使然,以避免您描述的那种运行时错误。

未能从我的非聚集索引针对聚集索引键查找导致嵌套循环联接,大概是为了获取 parentId。INCLUDING parent ID 消除了这一点,并给我留下了一个很好的非聚集索引扫描。

这是一个已知的电流限制。将过滤的列添加到键或包含列表是标准的解决方法,并且是针对各种半相关原因的当前最佳实践。

虽然肯定需要 FORCE ORDER, MERGE JOIN。

除非您完全理解所有后果,否则使用这样的提示(指令)要非常小心。FORCE ORDER特别是一个非常强大和广泛的提示,具有许多不明显的副作用,包括聚合运算符的放置,以及子查询和公共表表达式的计算顺序。

在大多数情况下,您应该尝试编写为查询优化器提供足够高质量信息的查询,以便在没有提示的情况下做出正确的决策。暗示的计划今天可能是“最佳”的,但随着数据量和/或分布随时间的推移而变化,它可能不会保持不变。