chr*_*ris 10 performance index sql-server t-sql filtered-index query-performance
我已经在下面创建了过滤索引,但是当我进一步运行 2 个查询时,该索引仅用于第一个示例中的查找,该示例在 JOIN 中有 END_DTTM 而不是 where 子句(这是查询中的唯一区别) . 任何人都可以解释为什么会发生这种情况?
索引创建
CREATE NONCLUSTERED INDEX [ix_PATIENT_LIST_BESPOKE_LIST_ID_includes] ON [dbo].[PATIENT_LIST_BESPOKE]
(
[LIST_ID] ASC,
[END_DTTM] ASC
)
WHERE ([END_DTTM] IS NULL)
Run Code Online (Sandbox Code Playgroud)
查询
DECLARE @LIST_ID INT = 3655
--This one seeks on the index
SELECT
PATIENT_LISTS.LIST_ID
FROM
DBO.PATIENT_LISTS
LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID
AND PATIENT_LIST_BESPOKE.END_DTTM IS NULL
WHERE
PATIENT_LISTS.LIST_ID = @LIST_ID
--This one scans on the index
SELECT
PATIENT_LISTS.LIST_ID
FROM
DBO.PATIENT_LISTS
LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID
WHERE
PATIENT_LISTS.LIST_ID = @LIST_ID AND
PATIENT_LIST_BESPOKE.END_DTTM IS NULL
Run Code Online (Sandbox Code Playgroud)
Pau*_*ite 14
为了让优化器将谓词与索引(过滤或其他方式)匹配,谓词必须出现在逻辑查询树中的 Get 操作旁边。为方便起见,通常在优化开始之前将谓词尽可能靠近逻辑树的叶子。
为了大大简化,物理索引策略实现是这样的:
Predicate + Logical Get -> Physical Get (using Index)
Run Code Online (Sandbox Code Playgroud)
您感兴趣的查询以外连接上方的谓词开头:
Predicate on T2 --+-- LOJ -- Get (T1)
|
+---- Get (T2)
Run Code Online (Sandbox Code Playgroud)
此形状与索引策略规则不匹配,因为谓词与 Get 不相邻。因此,答案的第一部分是过滤索引匹配将失败,除非可以将谓词推送到外部连接。
第二部分很简单,优化器不包含将谓词移动到保留侧的外部连接的必要探索规则,因为转换很少有效。优化器的一般特性是只实现最常用的规则。
因此,在这种情况下匹配过滤索引失败。需要明确的是,重写在您引用的非常具体的情况下(第二个查询)是有效的。
对于第一个查询形式(具有不同的语义),谓词从一开始就与连接相关联,谓词下推逻辑可以将其移动到 Get 的短距离,因为它不必移动通过外部连接作为上面解释了。
背景和更多信息:
Aar*_*and 10
这些在语义上不是相同的查询,因为一个可以在 join 之前过滤,另一个可以在 join 之后过滤。让我用一个更简单的例子来说明:
CREATE TABLE dbo.Lefty(LeftyID INT PRIMARY KEY);
CREATE TABLE dbo.Righty(LeftyID INT, SomeList INT);
INSERT dbo.Lefty(LeftyID) VALUES(1),(2),(3);
INSERT dbo.Righty(LeftyID, SomeList) VALUES(1,1),(1,NULL),(2,2);
Run Code Online (Sandbox Code Playgroud)
查询 1 返回所有三行:
SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
AND r.SomeList IS NULL;
Run Code Online (Sandbox Code Playgroud)
然而,查询 2 忽略了 LeftyID 2:
SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
WHERE r.SomeList IS NULL;
Run Code Online (Sandbox Code Playgroud)
如果您正在尝试执行反半连接,则测试列必须是 not nullable。当您仅处理 INNER 连接时,在 ON 和 WHERE 之间移动标准在逻辑上没有区别,但对于 OUTER 则存在显着差异。并且您应该更关心您的结果是否正确,而不是是否可以使用过滤索引。