显而易见的解决方案是测试两者。
首先设置一个示例架构:
IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL DROP TABLE dbo.T;
CREATE TABLE dbo.T
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
RECONCILIATION_STATUS TINYINT NOT NULL CHECK (RECONCILIATION_STATUS IN (0, 1, 2)),
Filler CHAR(100) NULL
);
INSERT dbo.T (RECONCILIATION_STATUS)
SELECT TOP (100000) FLOOR(RAND(CHECKSUM(NEWID())) * 3)
FROM sys.all_objects a, sys.all_objects b;
然后在没有索引的情况下进行测试
SELECT COUNT(Filler)
FROM dbo.T
WHERE RECONCILIATION_STATUS != 1;
SELECT COUNT(Filler)
FROM dbo.T
WHERE RECONCILIATION_STATUS IN (0, 2);
每个人的计划是:
如您所见,这里的差异可以忽略不计,因为没有索引,所以两个查询都需要进行聚集索引扫描。
由于可能的值如此之少,非聚集索引不太可能有任何用处,除非您将定期需要的所有列都包含为非键列,或者没有太多数据。在 100,000 个样本行上使用标准非聚集索引,构建如下:
CREATE NONCLUSTERED INDEX IX_T__RECONCILIATION_STATUS
ON dbo.T (RECONCILIATION_STATUS);
执行计划与聚集索引扫描保持一致。
将其他列包含为非键索引:
CREATE NONCLUSTERED INDEX IX_T__RECONCILIATION_STATUS
ON dbo.T (RECONCILIATION_STATUS) INCLUDE (Filler);
!= 1 的计划变得相当复杂,虽然我不会过多强调它的重要性,但估计的成本是一样的:
但是,IO 统计数据表明,实际所需的读取几乎没有什么不同:
表“T”。扫描计数 2,逻辑读取 935,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
表“T”。扫描计数 2,逻辑读取 934,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
到目前为止,差别不大,但这实际上取决于您的数据分布,以及您拥有的索引和约束。
有趣的是,如果您为测试创建一个临时表并在其上定义检查约束:
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL DROP TABLE #T;
CREATE TABLE #T
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
RECONCILIATION_STATUS TINYINT NOT NULL CHECK (RECONCILIATION_STATUS IN (0, 1, 2)),
Filler CHAR(100) NULL
);
INSERT #T (RECONCILIATION_STATUS)
SELECT TOP (100000) FLOOR(RAND(CHECKSUM(NEWID())) * 3)
FROM sys.all_objects a, sys.all_objects b;
优化器实际上会重写这个查询:
SELECT COUNT(Filler)
FROM #T
WHERE RECONCILIATION_STATUS != 1;
作为
SELECT COUNT(Filler)
FROM #T
WHERE RECONCILIATION_STATUS = 0
OR RECONCILIATION_STATUS = 2;
如本执行计划所示:
不过,我无法在永久表上复制此行为。尽管如此,这让我相信最好的选择是
WHERE RECONCILIATION_STATUS IN (0, 2);
不仅在性能方面,尽管在大多数情况下它似乎是微不足道的或根本没有,但在可读性和未来对附加值的验证方面也是如此。
但是,没有比对自己的数据进行此类测试更好的方法来找出答案。这将使您更好地了解什么比我可以从一小部分数据样本集中得出的任何假设表现得更好。