使用 Count 而不是返回所有行并检查记录计数是正确的,但我们仍然要求数据库引擎在所有行中查找。
如果要求不是返回最大行数,而只是确定是否有超过X行,那么我要做的第一个改进是返回表中前X行的计数。
所以如果X是 1000,你的应用逻辑不需要改变,你仍然可以确定一个有 999 个日志的项目和 1000+ 个日志的区别
我们只需更改现有查询,选择TOP(X)行而不是计数,然后返回该结果集的计数,只选择主键或唯一索引列,这样我们只检查索引而不是底层表存储。
select count(Id) FROM (
SELECT TOP(1000) // limit the seek that the DB engine does to the limit
Id // Further constrain the seek to just the indexed column
FROM History
where SensorId IN ( // this is the same filter condition as before, just re-formatted
SELECT Id
FROM Sensor
WHERE DeviceId = 96)
) as trunk
将此查询更改为前 10,000 仍可提供亚秒级响应,但是在X = 100,000 时,查询所用的时间几乎与原始查询一样长
如果有问题的表具有高事务率并且执行时间的主要原因是由于锁争用导致的等待,那么对于此类问题还有另一种看似“灵丹妙药”的方法。
如果您怀疑锁定是问题所在,并且您可以接受包含未提交行的计数响应,那么您可以使用WITH(NOLOCK)表提示来允许查询在READ UNCOMMITED事务隔离级别中有效运行。
这里有一个关于NOLOCK 表提示对选择查询的影响的很好的讨论
SELECT COUNT(1) FROM History WITH (NOLOCK)
WHERE SensorId IN (SELECT Id FROM Sensor WHERE DeviceId = 96)
尽管强烈反对,但这是一个很好的例子,说明 NOLOCK 很容易被允许,它甚至是有道理的,因为您在删除之前的计数将考虑另一个用户或正在积极添加到日志计数的操作。
经过多次试验,当查询 1000 或 10K 行时,使用计数的选择解决方案仍然比使用NOLOCK表提示更快。NOLOCK然而,提供了以最小的更改执行相同查询的机会,同时仍能及时返回。
随着基础结果集中行数的增加, select with 的性能NOLOCK仍然会增加,而当超过 top 限制时,具有 top而没有 order by 子句的 select 的性能应该保持不变。