为什么没有谓词应用于索引扫描?

Zac*_*her 4 sql-server index-tuning sql-server-2012

我们在处理一些与此类似的查询时遇到问题:

SELECT COUNT('A')  FROM  [dbo].[OINV] T0  
INNER  JOIN [dbo].[OCRD] T2  ON  T2.[CardCode] = T0.[CardCode]   
WHERE T0.[CardCode] = (@P2)  OR  T2.[FatherCard] = (@P3)
Run Code Online (Sandbox Code Playgroud)

查询计划

它击中的索引定义为:

NONCLUSTERED INDEX [OCRD_FATHER] ON [dbo].[OCRD]
(
    [FatherCard] ASC
) INCLUDE (CardCode)

NONCLUSTERED INDEX [OINV_CUSTOMER] ON [dbo].[OINV]
(
    [CardCode] ASC
)
Run Code Online (Sandbox Code Playgroud)

他们目前需要 1-2 秒来运行,并返回 0 的计数(这是我们所期望的)。

我非常惊讶的是,它在进入哈希匹配之前没有过滤非聚集索引 - 它正在提供每一行。这些是供应商软件查询,因此很遗憾我们无法重写它们。

为什么会这样,有没有办法在不重写查询的情况下在进行哈希匹配之前将其更改为过滤?

示例数据设置

CREATE TABLE [dbo].[OCRD]
  (
     [FatherCard] NVARCHAR(50),
     [CardCode]   NVARCHAR(50)
  );

INSERT INTO [dbo].[OCRD]
SELECT TOP (2076000) NEWID(),
                     NEWID()
FROM   master..spt_values v1,
       master..spt_values v2,
       master..spt_values v3


CREATE TABLE [dbo].[OINV]
  (
     [CardCode] NVARCHAR(50)
  )


INSERT INTO [dbo].[OINV]
SELECT TOP (5175460) NEWID()
FROM   master..spt_values v1,
       master..spt_values v2,
       master..spt_values v3


CREATE NONCLUSTERED INDEX [OCRD_FATHER]
  ON [dbo].[OCRD] ( [FatherCard] ASC )
  INCLUDE (CardCode)

CREATE NONCLUSTERED INDEX [OINV_CUSTOMER]
  ON [dbo].[OINV] ( [CardCode] ASC ) 
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 6

有什么方法可以在不重写查询的情况下在进行哈希匹配之前将其更改为过滤?

在企业版中,可以使用索引视图。例如:

CREATE VIEW dbo.OINV_OCRD
WITH SCHEMABINDING
AS
SELECT
    INV.CardCode,
    CRD.FatherCard,
    COUNT_BIG(*) AS cnt
FROM dbo.OINV AS INV
JOIN dbo.OCRD AS CRD
    ON CRD.CardCode = INV.CardCode
GROUP BY
    INV.CardCode,
    CRD.FatherCard;
GO
CREATE UNIQUE CLUSTERED INDEX cuq ON dbo.OINV_OCRD (CardCode, FatherCard);
CREATE NONCLUSTERED INDEX i ON dbo.OINV_OCRD (FatherCard) INCLUDE (cnt);
Run Code Online (Sandbox Code Playgroud)

然后像这样的查询:

DECLARE 
    @P2 nvarchar(50) = N'D20B5DD1-C729-4B7A-A276-950CE7DCF128',
    @P3 nvarchar(50) = N'6A0DBEAB-FECB-40DB-86C5-AAFA612DA691';

SELECT COUNT_BIG(*)
FROM [dbo].[OINV] T0
JOIN [dbo].[OCRD] T2
    ON T2.[CardCode] = T0.[CardCode]
WHERE 
    T0.[CardCode] = (@P2)  
    OR T2.[FatherCard] = (@P3);
Run Code Online (Sandbox Code Playgroud)

使用索引视图上的索引交集生成执行计划:

计划

表'OINV_OCRD'。 
    扫描次数 2, 
    逻辑读 8,物理读 0,预读 0, 
    lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。

 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,经过时间 = 0 毫秒。

所有关于索引视图使用的常见警告都适用。自动索引视图匹配仅在企业版(或同等版本)中可用。UsingCOUNT('A')不会影响核心机制,尽管添加了 Compute Scalar 以转换bigintinteger.

有关索引视图匹配功能的更多详细信息,请参阅对相关问题的回答。

如果向查询添加表,索引视图匹配可能会继续工作(假设匹配有效),因为优化器能够将查询的一部分与一个或多个索引视图匹配。这个答案只能解决问题中实际呈现的细节。

如果供应商查询遵循一组模式,您可以通过计划指南强制使用索引视图计划。