将在 where 子句的操作中使用的两列的 SQL Server 索引?

问题描述 投票:0回答:1

在 SQL Server 中,我有一个表

order_lines
,其中包含
ordered
served
列。

如何才能让下面的查询不扫描全表?

SELECT *
FROM order_lines
WHERE ordered - served > 0

选项 1: 为两列建立索引。这不应该起作用,因为 SQL Server 仍然需要扫描所有索引来执行减法并检查该行对于查询是否有效,对吗?

选项 2:创建一个“待处理”列和索引

选项 3:还有其他选项吗?

sql-server indexing composite-index
1个回答
0
投票

ordered - served > 0
通常不可进行搜索。

也不能通过将其表示为

ordered > served
来将其重新排列为可搜索。列与列的比较不可控制。

如果查找中要使用的值包含在行本身中,则无法明智地使用索引查找来查找行。

然而,

ordered - served > 0
可以通过使用计算列来made 进行优化控制。

假设你原来的桌子是这样的

CREATE TABLE order_lines
(
order_line_id INT NOT NULL PRIMARY KEY,
order_id INT NOT NULL,
product_id INT NOT NULL,
/*Other columns*/
ordered  INT NOT NULL,
served  INT NOT NULL
)

您可以创建计算列...

ALTER TABLE order_lines ADD pending AS ordered - served;

然后索引该计算列

CREATE INDEX IX_pending ON order_lines(pending);

为了“覆盖”问题中给出的查询,索引需要一个包含表中每一列的

INCLUDE
子句,这不太可能通过此处的成本/收益分析来证明是合理的。

因此,如果查询使用上述索引,它还需要查找基表以获取丢失的列,并且只有在谓词具有相当选择性的情况下才会选择此查询计划。下面的计划中缺少索引警告是在抱怨未包含的列。

execution plan showing filtered index seek, key lookups, and missing index warning

您真的需要在此处使用

*
,还是可以选择更少的列,然后将它们包含在索引中?

假设未履行的订单行仅占表格的一小部分,并且您主要对找到这些订单感兴趣,那么潜在的中间立场可能是

CREATE INDEX IX_pending ON order_lines(pending)
INCLUDE (order_line_id,order_id,product_id, ordered, served)
WHERE pending > 0; 

⚠️ 但这不是有效的索引创建,因为过滤谓词不能是计算列(或像

ordered > served
这样的表达式)

索引视图可以让你做同样的事情

CREATE VIEW dbo.pending_order_lines
WITH SCHEMABINDING
AS
SELECT order_line_id, order_id, product_id, ordered, served
FROM   dbo.order_lines 
WHERE ordered > served; 

GO

CREATE UNIQUE CLUSTERED INDEX UCIX ON dbo.pending_order_lines(order_line_id)

然后是示例用法

SELECT *
FROM dbo.pending_order_lines WITH(NOEXPAND)

根据您使用的引擎版本,可能需要也可能不需要

NOEXPAND
提示才能有效地使用索引视图。

© www.soinside.com 2019 - 2024. All rights reserved.