5

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

如何搜索字符串,除非它作为其他字符串的子字符串出现?

例如,如何选择MyTableMyCol包含 string的表中的所有记录foo,但忽略foo属于 string 的任何 s foobar

一个快速而肮脏的尝试是这样的:

SELECT * 
FROM MyTable 
WHERE MyCol LIKE '%foo%' 
  AND MyCol NOT LIKE '%foobar%'

但显然这将无法匹配 eg MyCol = 'not all foos are foobars',我确实想匹配。

我想出的一种解决方案是foobar用一些虚拟标记(不是 的子字符串foo)替换所有出现的 s,然后检查任何剩余foo的 s,如:

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

这可行,但我怀疑它不是很有效,因为它必须REPLACE()在表中的每条记录上运行。(对于 SEDE,这通常是Posts表,目前大约有 3000 万行。)有没有更好的方法来做到这一点?

(FWIW,提示这个问题的真正用例http://是搜索带有使用方案前缀但不指向主机的图像 URL 的 SO 帖子i.stack.imgur.com。)

4

4 回答 4

5

到目前为止给出的任何一种方式都不能保证像宣传的那样工作,并且只能REPLACE在行的子集上执行。

SQL Server不保证谓词短路,并且可以将计算标量向上移动到派生表和 CTE 的基础查询中

唯一(大部分)保证工作的是CASE声明。下面我使用IIF扩展为CASE

SELECT *
FROM   MyTable
WHERE  1 = IIF(MyCol LIKE '%foo%', 
               IIF(REPLACE(MyCol, 'foobar', 'X') LIKE '%foo%', 1, 0), 
               0);
于 2016-02-01T20:28:56.543 回答
1

三级过滤器应该可以工作:

  1. 收集所有匹配 '%foo%' 的行;

  2. 用未出现的字符串替换所有 'foobar' 实例(例如 '' 也许);

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

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

SQL 看起来像这样:

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

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

于 2016-02-01T11:58:16.183 回答
1

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

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

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

REPLACE(MyCol, 'foobar', 'X') LIKE '%foo%'
于 2016-02-01T13:28:46.633 回答
0

假设您只对查找foo周围有空格的实例感兴趣

 SELECT * 
 FROM MyTable 
 WHERE MyCol LIKE 'foo %' OR MyCol LIKE '% foo %' OR MyCol LIKE '% foo'
于 2016-02-01T11:58:30.170 回答