如何将聚集索引扫描改进为聚集索引查找?

How to improve Clustered Index scan to Clustered Index seek?

我有两张桌子

[dbo].[Employee](
    [EmployeeID] [int] IDENTITY(1,1) NOT NULL,
    [Title] [varchar](50) NULL,
    [Role] [int] NULL,
    [Status] [varchar](1) NULL)

[dbo].[Roles](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [Role] [varchar](25) NULL,
    [Description] [varchar](25) NULL)

使用 id 主聚簇键。 我也有存储过程

SELECT
   [EmployeeID]
  ,[Title]
  ,employee.[Role]
  ,roles.Role AS RoleName
FROM [dbo].Employee AS employee
INNER JOIN [dbo].Roles AS roles ON roles.id = employee.Role
WHERE [Status] <> 'D'

执行计划向我展示了一个我想避免的 'Clustered Index Scan'。有什么方法可以将它转换为 'Clustered Index Seek'? Execution Plan screen

<db fiddle>

正如我在评论中提到的,dbo.Employees 上的 CLUSTERED INDEX 不会在这里帮助您。这样做的原因是因为您在 status 列上过滤,它只是 includedCLUSTERED INDEX 中,但没有对其进行排序。因此,RDBMS 别无选择,只能扫描整个 table 以过滤掉 Status 值为 D.

的行

您可以在 Status 列上添加 INDEX 并在其他列上添加 INCLUDE,这 可能 导致索引查找 (不是聚集索引查找),但是,由于您使用 <> D,因此 RDBMS 可能仍会选择执行扫描;是否确实取决于您的数据分布:

CREATE INDEX IX_EmployeeStatus ON dbo.Employee (Status) INCLUDE (Title, Role);

同时添加一个 FOREIGN KEY 到您的 table:

ALTER TABLE dbo.Employee ADD CONSTRAINT FK_EmployeeRole FOREIGN KEY (Role) REFERENCES dbo.Roles (id);

db<>fiddle

如果您的 Status 列上有一个索引,那么该代码应该会导致两次索引查找。一份用于 < 'D',一份用于 > 'D'。您只需要一个关于 Status 的索引。

您可以使用临时 table 将其转换为一次查找,将“不是”转换为“是”,这在查询调优中始终是个好主意:

CREATE TABLE #StatusMatch (StatusCode varchar(1) NOT NULL PRIMARY KEY CLUSTERED)
 INSERT INTO #StatusMatch WITH(TABLOCKX)
      SELECT Status 
        FROM dbo.Employee WITH(NOLOCK)
       WHERE Status <> 'D'
    GROUP BY Status
    ORDER BY Status;

      SELECT a.EmployeeID, a.Title, a.Role, b.Description AS RoleName
        FROM dbo.Employee a WITH(NOLOCK)
  INNER JOIN dbo.Roles b WITH(NOLOCK) ON a.Role = b.id
  INNER JOIN #StatusMatch c ON a.Status = c.StatusCode