【问题标题】:How can I optimize/refactor a TSQL "LIKE" clause?如何优化/重构 TSQL“LIKE”子句?
【发布时间】:2010-12-06 20:11:58
【问题描述】:

我有一个包含 117000 条左右记录的表。我需要执行一个搜索,检查给定字符串模式的 3 个单独字段。

我的where子句如下:

field1 LIKE '%' + @DESC + '%'
OR field2 LIKE '%' + @DESC + '%'
OR field3 LIKE '%' + @DESC + '%'

无论输入如何,这似乎都需要大约 24 秒...

有没有更好的方法来做到这一点?少于 10(或 5!)秒会更可取。

感谢您的帮助。

【问题讨论】:

    标签: sql sql-server tsql optimization sql-like


    【解决方案1】:

    使用全文搜索和CONTAINS。 LIKE 在字段中间搜索时无法优化,即。当 LIKE 表达式以 '%' 开头时,它总是会进行全表扫描。

    【讨论】:

    • 你可以使用索引,做一点工作,当 LIKE '%'+string 时,请参阅我其他评论中的链接。
    • @KM:有趣的把戏,倒转的专栏。与电话本也很好的类比,使案情一目了然。
    • 不错的技巧,对于类似 '%text%' 的问题没有类似的东西。
    • @KM:它肯定必须是一个'%test%' 的形式,所以反向的方法已经被淘汰了,虽然我以前见过,这是一个聪明的把戏。我将不得不尝试全文搜索,但看起来我必须说服 DBA 正确索引它。感谢您的建议。我会看看全文/包含是否会提高性能。我记得在旧数据库上使用完全相同的查询执行此操作,而且速度要快得多......不确定新数据库的不同之处
    • 在让 DBA 对表进行全文索引后,这使我的搜索时间缩短到 1-3 秒。感谢您的帮助!
    【解决方案2】:

    只要您使用通配符开始 LIKE 搜索,您就是在进行扫描。除非您可以缩小搜索条件以包括第一个字符(这可能不可行),否则您需要求助于全文搜索。

    【讨论】:

    • @Stuart Ainsworth 说任何时候你用通配符开始一个 LIKE 搜索,你都在做扫描,所以不一定是真的,看这个:stackoverflow.com/questions/1388059/…
    • 现在你只是在玩语义;您反转列并对其进行索引,然后反转like子句,使其不以通配符开头。您没有使用通配符开始搜索,因此您没有进行扫描。我承认这是一个有趣的解决方案,我必须牢记这一点。
    • 这是不正确的,即使在字段中间搜索也可以优化LIKE。 sqlservercentral.com/blogs/dwainsql/2014/03/26/…
    • 近 8 年后,我学到了一些新东西 :) 从来没有想过从通配符字符串搜索中删除 NULL 值。
    【解决方案3】:

    您真的需要以通配符开头吗?为什么?通常,您可以强制用户至少输入第一个字符。我提出这一点是因为一些开发人员只是将通配符用作一种习惯,而不是因为有要求。在大多数情况下,用户将能够键入第一个字符,除非该文件存储了长字符串(例如官方机场名称)。否则,您确实需要使用全文索引,尽管如果您最后不需要通配符,KM 的反向技巧非常酷。

    如果你能避免做那些会影响性能的事情,那就去做吧。

    【讨论】:

    • 是的,我确实需要这个查询的双面通配符。我需要在“草莓香蕉酸奶”中找到类似“香蕉”的内容。
    【解决方案4】:

    虽然我同意全文索引将是最佳解决方案这一公认答案,但我绝不提倡使用领先的通配符搜索(如果必须执行),那么可能的步骤可以用来降低它们的性能。

    Kalen Delaney 在“Microsoft SQL Server 2008 Internals”一书中说:

    排序规则可以产生巨大的影响 SQL Server 的时候几乎要看看 字符串中的所有字符。为了 例如,请看以下内容:

    SELECT COUNT(*) FROM tbl WHERE longcol LIKE '%abc%'
    

    二进制排序规则的执行速度可能比非二进制 Windows 排序规则快 10 倍或更多。使用varchar 数据,SQL 排序规则的执行速度比 Windows 排序规则快七到八倍。

    【讨论】:

    • 更多来自本书“如果你有一个 varchar 列,你可以通过如下强制排序来加速:SELECT COUNT(*) FROM tbl WHERE longcol COLLATE SQL_Latin1_General_CP_CI_AS LIKE '%abc%'; "
    • @yoelhalb 你的意思可能是_CP1_ 不是_CP_
    【解决方案5】:

    怎么样

    field1 + field2 + field3 LIKE '%' + @DESC + '%'
    

    CONTAINS(field1 + field2 + field3, @DESC)
    

    【讨论】:

      【解决方案6】:

      我尝试了一种可能的解决方案。在此解决方案之前,甚至查询都没有返回结果并导致连接超时错误。

      我的查询有日期过滤器和其他条件。所有其他条件都类似于搜索。一个列关键字在 ntext 列上搜索“%abc%”,它正在执行全表扫描。

      解决方案:

      将查询分成两部分。 1) CTE (Common Table Express) 中的第一部分 2) 在 CTE 上应用所有搜索条件。

      WITH SearchData(Column1,Column2,Column3,Column4,........)
          AS
          (
          SELECT Column1,Column2,Column3,Column4,...........
          FROM myTable1 WITH(NOLOCK) 
                  INNER JOIN MyTable2 WITH(NOLOCK) 
                      ON MyTable1.id = MyTable2.Id
          WHERE (MyTable1.CreationTime >= '2014-04-27' AND MyTable1.CreationTime <= '2014-05-01') 
       )
      
          SELECT DISTINCT top 250 Column1,Column2,Column3,Column4
          FROM SearchData
          WHERE   (ISNULL(Column1,'') LIKE @Column1  +'%' OR @Column1 IS NULL)
                  and (Column2 LIKE @Column2+ '%' OR @Column2 IS NULL)
                  ...
                  ...
                  ...
                  ...
                  AND (Column10 like '%'+@Column10+'%' or @Column10 IS NULL)
                  AND @Column1+@Column2+@Column3+........@Column10 <> ''  
                  ORDER BY [CreationTime] DESC
      

      它对我有用。

      【讨论】:

      • 我认为将TOP 子句与具有 LIKE 条件的查询一起使用将始终提高性能。所以它的 TOP 子句而不是 CTE 有助于提高性能,因为使用 TOP,只要扫描了所需数量的记录,就会缩短完整扫描。
      • 在每次连接中使用 WITH(NOLOCK) 作为题外话是非常危险的,你必须非常小心,否则你会得到重复的数据等......我看到人们将此提示视为优化查询的标准方法,它不是。在引入 nolock 提示之前,您应该始终三思而后行并查看应用程序中表的写入方式(插入/更新)。 (只是说)
      【解决方案7】:

      如果您不能使用 FullTextSearch,您可以将速度提高 10 倍。下一步:

      1 添加计算字段:

      alter table TableName
      add CalculatedColumnName as upper(Column1 + '|' + Column2...) collate Latin1_General_100_Bin2
      persisted;
      

      2 为计算字段添加索引:

      create nonclustered index IDX_TableName_CalculatedColumnName
      on TableName(CalculatedColumnName);
      

      3 更改查询文本

      select count(*)
      from TableName
      where CalculatedColumnName like '%' + upper(@ParameterValue) + '%' collate Latin1_General_100_Bin2
      

      来源:http://aboutsqlserver.com/2015/01/20/optimizing-substring-search-performance-in-sql-server

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-26
        • 1970-01-01
        • 2021-05-03
        • 2015-07-27
        • 2021-11-17
        • 2011-10-13
        • 1970-01-01
        相关资源
        最近更新 更多