我可以优化包含WHERE值<>的请求吗? 2,索引为

时间:2017-09-05 14:32:54

标签: sql sql-server query-optimization

这是我的要求:

SELECT TOP 10 * FROM BigTable WHERE Value <> 2;

BigTable有点特别,因为Value列包含每行的相同值:2。在实际情况中,可能有一些行具有不同的值,但不是很多。我确实需要找到这些流氓行。但是,我不知道在设计时哪个值(2只是一个例子)(但我知道在查询时)。

请求很慢(大约5分钟); BigTable包含1000万行。

所以我在Value列上添加了一个索引,该列的类型为smallint。 10分钟后,索引建成了,我再次运行了请求。它仍然很慢。

这个问题可以在这里复制:http://sqlfiddle.com/#!6/6ce0f/1

此时,我的猜测是SQL Server无法使用索引进行<>运算符的查询,但我不确定为什么?例如,此其他查询只需2秒:SELECT TOP 10 Value FROM BigTable GROUP BY Value(并返回单行,其值为2,如预期的那样)。

我正在考虑拆分成多个查询:一个用于获取不同值的列表,另一个用于获取所有流氓行,例如SELECT TOP 10 * FROM BigTable WHERE Value = x等(所有值都不是2),但是有更好的解决方案吗?

编辑:

此查询的想法是查找在更新大多数行的进程之后尚未更新的行。基本上,我正在与另一个数据源同步。每次运行此过程时,我都会增加该值,并使用新值(以及更新的数据)更新每一行。在该过程结束时,我可以检查哪些行具有旧值,并删除它们。这个过程有点长,这就是为什么我不想先截断表并插入后,因为我需要先前的数据在这个流程执行期间保持可用。

索引是使用此请求创建的(由Entity Framework Core生成,但我手动进行查询测试):

CREATE NONCLUSTERED INDEX [IX_Value] ON [dbo].[BigTable]
(
    [Value] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

编辑2:

以下是来自SSMS的估计查询计划(没有什么是最高机密,但由于我没有问我是否可以透露项目的内容,我已经模糊了数据库名称;在这些屏幕截图中还有表格和列名称是真实的)

Value <> 2的查询计划 enter image description here

您可以看到它根本不使用IX_Establishments_UpdateTag(索引扫描位于主键上)。执行时间:5分18秒(在这次运行中,我有一行Value/UpdateTag不是2)

Value < 2的查询计划 enter image description here

此处使用IX_Establishments_UpdateTag索引。执行时间小于1秒(SSMS报告0秒)。

3 个答案:

答案 0 :(得分:1)

SQL可以使用<>查询的索引。 “可以”并不意味着它会,它只意味着优化器会考虑它们。

OR存在时,SQL通常会出现问题 - 这些通常(可能总是?)会导致全表扫描。 Value IN (1,2,3)必须“翻译”为“值= 1或值= 2或值= 3”。

Value < 2 OR Value > 2对你我来说很明显,但优化器可能不够聪明,意识到这相当于Value <> 2 ...所以它将它留作OR,并且chugs和桌面扫描。

至于为什么Value <> 2无法快速运行,它取决于您的数据。做一些猜测,而不是在这里进行必要的细节:

  • 你有很多数据(1000万行,没问题,但是每行有多少字节?...会导致讨论Pages和Extents。)
  • 你说很少有&lt;&gt; 2(或任何你的目标值)
  • 查询优化器使用索引统计信息来确定是否使用给定索引
  • 通过对数据进行采样来构建统计数据。如果数据很少,那么在构建统计信息时可能没有采样非2值,因此优化器认为所有值都是2并且将索引标记为很大程度上无用。

(可以通过运行

查看统计信息
dbcc show_statistics (<TableName>, <IndexName>)
然而,

理解统计数据完全是另一回事。如果你想走到这一步,请在网上查看相关文章和讨论。)

以上是很多“为什么”。如果没有深入研究数据,分析统计数据,并且这样做,我就没有现成的解决办法。作为一个虽然实验,如果我们将您的< OR >查询转换为AND查询怎么办?尝试

where not (Value >= 2 and Value <= 2)

看起来很傻,可能行不通,但值得一试,看看会发生什么。

答案 1 :(得分:0)

实际上有一个简单的解决方案似乎能够快速运行:

SELECT TOP 10 * FROM BigTable WHERE Value < 2
SELECT TOP 10 * FROM BigTable WHERE Value > 2

但请注意,在我的测试中,此查询速度很慢:

SELECT TOP 10 * FROM BigTable WHERE Value < 2 OR Value > 2

所以我需要执行两个不同的查询并在之后连接结果。我不知道是否有合理的解释。

答案 2 :(得分:0)

SQL Server可以使用索引进行<>查询。

如下所示的简单查询

select top 100 empid,custid 
 from orderstest where orderid<>230

以下索引

create index nci on orderstest
(orderid)
include(custid,empid)

使sql server以下面的格式使用谓词

  

[1]寻找键[1]:结束:[PerformanceV3]。[dbo]。[orderstest]。 orderid&lt;标量算子((230))
   [2]寻找键[1]:开始:[PerformanceV3]。[dbo]。[orderstest]。 orderid&gt;标量运算符((230))

并且对于实际读取以检索行的行数

,估计也很好
  

RunTimeCountersPerThread线程=&#34; 0&#34; ActualRows =&#34; 100&#34; ActualRowsRead =&#34; 100&#34;

所以我猜你的查询可能运行缓慢的原因可能是

  1. 您正在阅读表格中的所有行,如果您的索引没有所有列,则会扫描整个表格
  2. 2.我认为网络可能是一个瓶颈,取决于你如何呈现所有行

    例如,

    下面的查询返回所有行并在SSMS上显示

    select empid,custid 
     from orderstest where orderid<>230  
    

    需要4秒,但在使用discard results set after execution执行时会立即返回

    其他一些原因可能是锁定,阻塞......您可以启用实际执行计划并在show plan xml中查找等待统计信息。以下我的查询是等待统计信息

     <WaitStats>
                  <Wait WaitType="SOS_SCHEDULER_YIELD" WaitTimeMs="1" WaitCount="3" />
                  <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="4942" WaitCount="4389" />
                </WaitStats>