为什么多个WHERE条件会降低查询速度而不是加快速度?

时间:2010-01-29 14:43:43

标签: sql-server sql-server-2005 tsql

问题是,与使用一个或两个而不是全部三个条件运行的查询相比,有问题的查询运行速度非常慢。

现在查询。

Select Count(*)
From 
    SearchTable 
Where 
    [Date] >= '8/1/2009' 
    AND 
    [Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150))
    AND 
    FreeText([Description], 'keyword list here')  

第一个条件是自我解释。第二个使用UDF获取30348英里内150英里内的邮政编码列表。第三个使用全文索引搜索提供的单词。

只有这个条件

[Date] >= '8/1/2009' 

查询在 3秒中返回43884(表格大小不到500k行)。

仅使用此条件

[Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150))

我得到27920,也是 3秒返回。

只有全文部分

FreeText([Description], 'keyword list here')

68404以 8秒返回。

当我只使用邮政编码和全文条件时,我在 4秒内获得4919
只是日期和全文条件让我9481只是害羞 14秒 使用日期和邮政编码条件只能在 14秒中给我3238 在所有三个条件下,查询在 2分53秒中返回723。 (wtfbbq)

8 个答案:

答案 0 :(得分:17)

了解原因的唯一方法是检查执行计划。 试试SET SHOWPLAN_TEXT ON

答案 1 :(得分:10)

获取执行计划

您需要查看执行计划,以便任何希望了解响应时间变化的真正原因。特别是在这种情况下,有几个因素需要考虑:

  • 一些返回更多行的查询可能会更快,因为他们正在进行表扫描 - 每个人都有“表扫描很慢”,但是根据数据分布,执行表扫描的速度可能会更快超过50,000次行查找。没有执行扫描就无法判断它。
  • 错误的统计信息也可能阻止SQL服务器准确预测其期望返回的行数 - 如果SQL服务器期望20行但是在更复杂的查询中确实有20,000个,那么它可能最终会做事情以错误的顺序导致查询速度非常慢 - 再次说明没有执行计划就无法判断。
  • 特别是使用Freetext意味着正在使用全文搜索引擎,这可能会导致SQL服务器在预测返回的行数方面出现其他问题。

真的,得到执行计划。

<强>更新

可能的原因

如果没有执行计划,我认为执行缓慢的最可能原因是对ZipCodeDescription条件的估计不佳:

  • 很难估计ZipCode条件的匹配数,因为它的结果取决于存储过程。
  • 很难根据全文查询引擎的结果估算FreeText条件下的匹配数。

我认为发生的事情是SQL服务器低估了过滤后将保留的行数,并以错误的顺序应用查询。结果是它最终会进行数十次(可能数百次)的查找,这比仅进行表扫描要慢得多。

对于一个特别复杂的查询,我看到SQL服务器执行~3,000,000次查找尝试返回单行 - 该表甚至没有3,000,000行!

要尝试的事情 - 将ZipCodeForRadius放入临时表。

如果我是对的,那么为了帮助第一个你可以尝试将ZipCodesForRadius存储过程的结果放到临时表中,我不得不承认我没有一个很好的解释为什么这个提供帮助,但我确实有一些关于它可能帮助的理论:

  • 关于临时表的更好统计数据
  • 每次运行查询时都会导致重新编译主SELECT语句的副作用(除非邮政编码的范围非常小) - 在proc处需要几秒钟如果匹配的邮政编码有很大的变化,那就好了。如果没有,那么就有办法阻止重新编译。

在任何情况下,肯定不应该造成太大的伤害。

答案 2 :(得分:2)

因为要检查的条件越多,数据库引擎的工作就越多。对我来说似乎很合乎逻辑。

如果您在聚集索引字段上有一个条件,则此特定检查不会减慢操作的速度。您是否考虑过重新排列索引以匹配您的查询?

答案 3 :(得分:2)

  • FreeText等字符串操作很昂贵
  • ZipCodesForRadius函数也可能很昂贵,具体取决于它的编码方式以及是否存在必要的索引

如果对WHERE子句进行排序不会加快速度,那么在select中进行选择可能会有所帮助(在某些情况下使用DB2 / 400加速了,不确定SqlServer如何优化):

Select Count(*)
From
(
    Select [Description]
    From 
        SearchTable 
    Where 
        [Date] >= '8/1/2009' 
        AND 
        [Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150))

) as t1
Where FreeText([Description], 'keyword list here')  

答案 4 :(得分:2)

尝试在表中添加一些索引。特别是涵盖where子句条件的那些。很可能现在正在进行表扫描以将数据拉回来,这可能非常慢。

此外,您可能希望使用管理工作室中的“包括实际执行计划”按钮来显示如何确定您获得的记录。

<强>更新

从你的一条评论中,听起来这个查询是从临时表中提取的。在这种情况下,在创建表之后将索引应用于它。添加索引然后运行查询将比在500k行临时表上运行表扫描更快。

答案 5 :(得分:2)

如果有一个条件要count(),那么查询可以扫描覆盖计数的最窄索引。即使是完整扫描,读取的页数也远小于聚簇索引扫描的页数,这可能要宽得多。当您有多个条件时,必须连接候选行,并且查询计划可能会放弃非聚集索引扫描(或范围扫描)并进行全表扫描。

在你的情况下,可能发生的是:

  • [Date] >= '8/1/2009'对包含Date的索引感到满意,最有可能是索引ON Date,因此它是一个快速范围扫描
  • [Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150))与Date相同。即使你没有Zip索引,你也可能有一个包含Zip。
  • 的索引
  • FreeText([Description], 'keyword list here')全文搜索计数,通过内部FT索引进行快速搜索。

  • 所有三个条件。现在它变得凌乱。如果你有足够的RAM,查询可以首先制定FT搜索计划,然后HASH-JOIN然后Zip扫描然后HASH-JOIN日期。这将是快速的,大约3 + 3 + 8秒+更改(对于散列操作)。但是如果你没有足够的RAM或者优化器不喜欢做散列连接,它将不得不进行FT搜索,然后嵌套循环搜索Zip然后嵌套循环搜索Code,它可能会命中index tipping point在其决定中。所以你最有可能得到一个表扫描。这当然是我的推测,但毕竟你只发布了T-SQL文本和关于集群和非集群索引结构的零信息。

最后,你必须记住SQL不是你的C语言程序语言。在谈论SQL中的性能时,绝不是比较和布尔逻辑。它始终与数据访问和页面读取量有关。因此,即使通过窄的非聚集索引或FT索引的小的,快速的索引范围扫描可以满足每个单独的条件,组合也不能(或者在他的情况下,查询优化器没有找到方法)。

答案 6 :(得分:0)

数据传输明智,您的思路正确:数据更少,完成时间更短。但是,通常时间很短,大部分时间都花在实际的查询处理上。

以这种方式看待它:如果你在一辆车上,是否更容易挑选出所有红色的汽车,或者所有红色汽车,2006款车型,黑色内饰和橡胶地垫?

答案 7 :(得分:0)

我怀疑Date字段没有编入索引,并且没有索引依赖于在非sargable列上应用where子句之前过滤结果集,它给予它们所有相同的权重并且不会先执行快速过滤器应用其他更昂贵的条款。

当我无法使用索引等调整数据库时,我经常发现重写类似于此的查询足以指导编译器进行更有效的查询:

Select Count(*)
From (
    Select 1
    From SearchTable 
    Where [Zip] In (Select ZipCode from dbo.ZipCodesForRadius('30348', 150))
) 
Where [Date] >= '8/1/2009' 
    AND FreeText([Description], 'keyword list here')