【问题标题】:Why is this SQL statement very slow?为什么这条 SQL 语句很慢?
【发布时间】:2012-11-20 01:42:12
【问题描述】:

我有一个包含大约 100 万条记录的表(运行 SQL Server 2008 Web)。我有一个搜索例程,它试图匹配产品代码和产品描述。 但是在某些情况下它非常慢。下面是(删减)sql语句:

WITH AllProducts AS (
  SELECT       p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber
  FROM        Product AS p 
    WHERE p.IsEnabled=1 AND
    (
      p.BaseSku = 'KPK-3020QWC-C' -- this on its own is fast
      OR
      CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"') -- and this on its own is fast, but not both
    )
) SELECT * FROM AllProducts        
  WHERE RowNumber BETWEEN 1 AND 20;

请注意,如果我只是比较 [p.BaseSku = 'KPK-3020QWC-C'] 或 [CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"')] 单独(但不是两者)它的瞬间。如果我将它们比较在一起,则需要很长时间(几分钟)-并且只返回一行。

IsEnabled 和 BaseSku 被索引,FreeTextStrings 被 FTS 索引。

我记得这之前工作得很好。

任何人都可以对此有所了解并提出一些解决方案吗?

执行计划文件可在此处获得:http://wiki.webgear.co.nz/GetFile.aspx?File=Temp%5cSearch%20Test.sqlplan.zip

【问题讨论】:

  • 您能否向我们展示一下您的 SQL Server 的执行计划?
  • SQL 2005 升级到 SQL 2008 后开始出现这些问题。

标签: sql sql-server


【解决方案1】:

or 在 SQL Server 上的速度非常慢。至少可以这么说,这很严重。

尝试使用union 将其拆分为两个查询:

WITH AllProducts AS (
  select *, Row_Number() OVER (ORDER BY ProductId) AS RowNumber
  from (
  SELECT       p.*
  FROM        Product AS p 
    WHERE p.IsEnabled=1 AND
      p.BaseSku = 'KPK-3020QWC-C' 
  UNION
  SELECT       p.*
  FROM        Product AS p 
    WHERE p.IsEnabled=1 AND
      CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"')
  )
) SELECT * FROM AllProducts        
  WHERE RowNumber BETWEEN 1 AND 20;

【讨论】:

  • 确切地说,即使 BaseSku 和 FreeTextStrings 上都有索引,“或”很可能会导致对产品表的表扫描...联合会将其转换为索引查找 + 索引扫描。 ..(假设有覆盖这两列的索引)
  • 我已经尝试过了,它确实显示出很大的改进。我将使用完整的 sql 语句尝试这种技术。
  • 是的,这已经解决了。但是,我认为相同的语句在 SQL 2005 中运行得非常快而在 SQL 2008 中运行缓慢,这很奇怪。这可能会在下一个服务包中解决吗?
  • @Muxa:他们确实对编译器做了很多改进。 or 处理不是其中之一。不过,我和你在一起。我真诚地希望这种情况尽快改变!
【解决方案2】:

这似乎运作良好:

WITH AllProducts AS (
  SELECT       p.*, Row_Number() OVER (ORDER BY ProductId) AS RowNumber
  FROM        Product AS p 
    WHERE p.IsEnabled=1 AND
    (
      CONTAINS(p.BaseSku, 'KPK-3020QWC-C') /* instead of p.BaseSku = 'KPK-3020QWC-C' */
      OR
      CONTAINS(p.FreeTextStrings, '"KPK-3020QWC*"')
    )
) SELECT * FROM AllProducts        
  WHERE RowNumber BETWEEN 1 AND 20;

(我已经有 BaseSku FTS 索引)

【讨论】:

    【解决方案3】:

    Make sure all necessary indexes are in place. 我的一个查询中的or 子句遇到了同样的问题,并创建了一个包含 INCLUDE 列的非集群索引修复了性能。

    经过进一步测试,真正解决了性能问题的是索引的INCLUDE列部分。以下是我为确定问题以及如何解决问题所做的工作:

    使用执行计划帮助您创建缺失的索引:

    如果没有索引,查询需要 2+ 分钟,而它本应在几毫秒内运行。因此,我比较了 SSMS 中带有和不带有 or 子句的查询的执行计划,我需要做什么并不明显(主要是由于我对执行计划缺乏了解)。

    但是如果你查看上面绿色文本的执行计划,SSMS 可能会告诉你创建一个非聚集索引。嗯……值得一试。所以我创建了索引并解决了问题!您可以右键单击“CREATE INDEX”查询并选择“Missing Index Details...”。这将打开一个新选项卡,其中包含完整的查询供您运行。只要给它一个名字。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多