T-SQL模式匹配异常

时间:2016-02-01 11:47:01

标签: sql-server tsql dataexplorer

这是我在使用基于T-SQL的Stack Exchange Data Explorer时反复遇到的问题:

当字符串作为其他字符串的子字符串出现时,如何搜索除之外的字符串?

例如,如何选择表MyTableMyCol列包含字符串foo但忽略属于foo的任何foobar的所有记录。字符串SELECT * FROM MyTable WHERE MyCol LIKE '%foo%' AND MyCol NOT LIKE '%foobar%'

快速而肮脏的尝试将是:

MyCol = 'not all foos are foobars'

但显然这将无法匹配,例如foobar,我确实希望与之匹配。

我提出的一个解决方案是用一些虚拟标记(不是foo的子字符串)替换所有出现的foo,然后检查剩余的SELECT * FROM MyTable WHERE REPLACE(MyCol, 'foobar', 'X') LIKE '%foo%' s,如:

REPLACE()

这有效,但我怀疑它效率不高,因为它必须在表中的每条记录上运行Posts。 (对于SEDE,这通常是http://表,目前有大约3000万行。)有更好的方法吗?

(FWIW,the real use case提示此问题是搜索包含使用i.stack.imgur.com方案前缀但未指向主机{{1}}的图片网址的SO帖子。)

4 个答案:

答案 0 :(得分:5)

目前为止所提供的方式都不能保证按宣传的方式工作,只对行的子集执行REPLACE

SQL Server does not guarantee short circuiting of predicatescan move compute scalars up into the underlying query for derived tables and CTEs

唯一可以保证(mostly)工作的是CASE语句。下面我使用扩展到IIF

CASE的句法糖种类
SELECT *
FROM   MyTable
WHERE  1 = IIF(MyCol LIKE '%foo%', 
               IIF(REPLACE(MyCol, 'foobar', 'X') LIKE '%foo%', 1, 0), 
               0);

答案 1 :(得分:1)

三级过滤器应该有效:

  1. 收集所有符合'%foo%'的行;

  2. 用非发生的字符串替换'foobar'的所有实例(例如''或许);

  3. 再次检查匹配'%foo%'

  4. 在这里,您只对可能匹配的行执行REPLACE,而不是对所有行执行REPLACE。如果你只期望一小部分比赛,这应该会更有效率。

    SQL看起来像这样:

    ;with data as (
        select * 
        from MyTable 
        where MyCol like '%foo%'      
    )
    select *
    from data
    where replace(MyCol, 'foobar', 'X') like '%foo%'
    

    请注意,需要子查询,因为SQL中没有表达式快捷方式;引擎可以根据需要自由重新排序布尔项,以便在单个查询级别内进行有效处理。

答案 2 :(得分:1)

这将比您当前的查询更快:

SELECT * 
FROM MyTable 
WHERE 
  MyCol like '%foo%' AND
  REPLACE(MyCol, 'foobar', 'X') LIKE '%foo%'

在应用MyCol之后计算REPLACE,因此这比仅仅更快:

REPLACE(MyCol, 'foobar', 'X') LIKE '%foo%'

答案 3 :(得分:0)

假设您只想找到foo的实例,并在其周围留出空格

 SELECT * 
 FROM MyTable 
 WHERE MyCol LIKE 'foo %' OR MyCol LIKE '% foo %' OR MyCol LIKE '% foo'