【问题标题】:LIKE work-around in SQL (Performance issues)SQL 中的 LIKE 解决方法(性能问题)
【发布时间】:2011-04-11 02:34:28
【问题描述】:

我一直在阅读,发现使用 LIKE 会大大降低查询速度。

一位同事推荐我们使用

Select Name
From mytable
a.Name IN (SELECT Name 
           FROM mytable
           WHERE Name LIKE '%' + ISNULL(@Name, N'') + '%' 
           GROUP BY Name)

代替

Select Name
From mytable
a.Name LIKE '%' + ISNULL(@Name, N'') + '%'

现在我不是 SQL 专家,我并不真正了解这些语句的内部工作原理。这是一个更好的选择,值得为每个 like 语句输入一些额外的字符吗?有没有更好(更容易输入)的替代方法?

【问题讨论】:

  • 我无法想象这有什么不同。与往常一样,您应该为您的数据库使用查询计划器,并让它准确地告诉您它将如何运行查询。但是,除非该列上有某种全文索引,否则无法优化您的 like 子句最终是正确的,即使维护起来也很昂贵。基本上,没有免费的午餐。
  • 你上面的声明中的IIQR是什么?那是一张小桌子吗?还有你用的是什么关系型数据库?
  • 你的表现的杀手锏是你进行了LIKE %(term)% 搜索。这基本上使所有索引都无用 - 你几乎可以保证获得全表扫描。如果可以的话,尝试使用类似LIKE (term)%(没有前导%)的东西——这将允许SQL Server使用索引——或者使用全文搜索,如果你真的必须搜索任意词
  • 有些查询绝对需要放置前导 %。任意术语是什么意思?

标签: sql sql-server tsql sql-like


【解决方案1】:

有几个性能问题需要解决...

如果可能,不要多次访问同一个表

不要将子查询用于无需引用同一个表的其他副本即可完成的条件。如果由于使用聚合函数(MAX、MIN 等)而需要来自表副本的数据,这是可以接受的,但分析函数(ROW_NUMBER、RANK 等)可能更适合(假设支持)。

不要比较你不需要的东西

如果您的参数为 NULL,这意味着您想要比较的列的任何值,请不要包含过滤条件。像这样的陈述:

WHERE a.Name LIKE '%' + ISNULL(@Name, N'') + '%'

...保证优化器必须比较name 列的值,无论是否使用通配符。在LIKE 的情况下更糟糕的是,在评估的左侧使用通配符可确保如果正在搜索的列中存在索引,则无法使用索引。

性能更好的方法是:

IF @Name IS NOT NULL 
BEGIN
   SELECT ...
     FROM ...
    WHERE a.name LIKE '%' + @Name + '%'
END
ELSE 
BEGIN
   SELECT ...
     FROM ...
END

执行良好的 SQL 就是根据您的需要量身定制。这就是为什么你should be considering dynamic SQL when you have queries with two or more independent criteria

使用正确的工具

当您检查文本数据中是否存在字符串时,LIKE 运算符在搜索文本时效率不高。 Full Text Search (FTS) technology 旨在解决这些缺点:

IF @Name IS NOT NULL
BEGIN
   SELECT ...
     FROM ...
    WHERE CONTAINS(a.name, @Name) 
END
ELSE
BEGIN
   SELECT ...
     FROM ...
END

始终测试和比较

我同意 LittleBobbyTables - 该解决方案最终依赖于检查所有备选方案的查询/执行计划,因为表设计和数据会影响优化器的决策和性能。在 SQL Server 中,具有最低子树成本的那个是最有效的,但如果不维护表统计信息和索引,它可能会随着时间而改变。

【讨论】:

    【解决方案2】:

    只需比较执行计划,您就会发现差异。

    我没有你的确切数据,但我对我的 SQL Server 2005 数据库运行了以下查询(是的,这很书呆子):

    SELECT     UnitName
    FROM         Units
    WHERE     (UnitName LIKE '%Space Marine%')
    
    SELECT     UnitName
    FROM         Units
    WHERE     UnitName IN (
       (SELECT UnitName FROM Units 
       WHERE UnitName LIKE '%Space Marine%' GROUP BY UnitName)
    )
    

    这是我的执行计划结果:

    您的同事建议在我的查询中添加一个嵌套循环和第二次聚集索引扫描,如上所示。您的里程可能会有所不同,但一定要检查执行计划以查看它们的比较情况。我无法想象它会如何更有效率。

    【讨论】:

    • @Jonn -- 如果我完成它,它将用于游戏。按照我的速度,不太可能:)
    • 呵呵.. 我想我明白你的意思了。希望你能完成它。
    【解决方案3】:

    除非 IIQR 是一些较小的表,它以某种方式对名称进行索引(并且不是从一开始就在此处查询的原始表),否则我根本看不出那个较长的版本有什么帮助;它做着完全相同的事情,只是增加了一个额外的步骤,即创建一组在 IN 中使用的结果。

    但即使 IIQR 是一个较小的“索引”表,我也会怀疑。我想更多地了解相关数据库以及每个查询计划的最终结果。

    LIKE 会对查询性能产生负面影响,因为它通常需要进行表扫描 - 物理加载每条记录的相关字段并搜索相关文本。即使该字段被索引,也可能是这种情况。但是,如果您需要做的是在字段内的任何可能位置搜索部分文本,则可能无法解决。

    不过,取决于相关表格的大小;这可能真的一点都不重要。

    为了,不过;我建议保持简单是最好的。除非您真的知道使查询复杂化对性能的整体影响,否则很难决定采用哪种方式来做事。

    【讨论】:

      猜你喜欢
      • 2010-09-11
      • 1970-01-01
      • 1970-01-01
      • 2021-03-25
      • 1970-01-01
      • 1970-01-01
      • 2022-09-27
      • 1970-01-01
      • 2011-05-08
      相关资源
      最近更新 更多