2

我想计算所有不紧急且订单状态 = 1(已发货)的订单。

这应该是一个非常简单的优化查询。我想在 Orders 表上放置一个简单的过滤索引来覆盖这个查询,使其成为一个恒定的时间/O(1) 操作。但是,当我查看查询计划时,它似乎使用了没有意义的索引扫描。理想情况下,此查询应该只返回索引中的项目数。

该表如下所示(简化为本质):

CREATE TABLE [dbo].[Orders](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [IsUrgent] [bit] NOT NULL,
    [Status] [tinyint] NOT NULL
 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED ( [Id] ASC )

我创建了这个过滤索引:

CREATE INDEX IX_Orders_ShippedNonUrgent ON Orders(Id) WHERE IsUrgent = 0 AND Status = 1;

现在,当我执行此查询时:

SELECT COUNT(*) FROM Orders WHERE IsUrgent = 0 AND Status = 1

我看到查询计划正在使用 IX_Orders_ShippedNonUrgent,但它正在执行索引扫描并在约 150,000 行订单中执行大约 200 次读取。

假设过滤的索引保持最新,是否可以始终让此查询在恒定时间内运行?理想情况下,它应该只执行 1 次读取来获取索引的大小。

如果我切换到这样的非过滤索引:

CREATE INDEX IX_Orders_IsUrgentStatus ON Orders(IsUrgent, Status);

查询计划使用 Index Seek,但仍然执行比回答这个简单查询所需的更多的读取。

更新

我能够做到这一点

SELECT TOP 1 rows FROM sys.partitions p
INNER JOIN sys.indexes i
ON i.name = 'IX_Orders_ShippedNonUrgent'
AND i.object_id = p.object_id
AND i.index_id = p.index_id

并在 9 次读取中获得结果,但似乎应该有一种更简单、更简单的方式来使用简单的 COUNT(*) 查询。

4

1 回答 1

1

我想要的似乎是不可能的。Nikola Markovinović在评论中留下了最好的答案,即忘记过滤索引并改用索引视图:

CREATE VIEW [dbo].vw_Orders_TotalShippedNonUrgent WITH SCHEMABINDING 
AS 
SELECT COUNT_BIG(*) AS TotalOrders 
  FROM dbo.Orders WHERE IsUrgent = 0 AND Status = 1;

CREATE UNIQUE CLUSTERED INDEX IX_vw_Orders_TotalShippedNonUrgent ON vw_Orders_TotalShippedNonUrgent(TotalOrders);

这会强制为我想要的每个摘要统计信息创建视图及其索引,并重写查询以询问视图而不是简单的方法,但它在只有 2 次读取时速度很快。

我会把这个问题留一段时间,以防有人有一个同样快的更简单的方法。

于 2012-09-03T22:44:34.233 回答