这是我在使用基于T-SQL的Stack Exchange Data Explorer时反复遇到的问题:
当字符串作为其他字符串的子字符串出现时,如何搜索除之外的字符串?
例如,如何选择表MyTable
中MyCol
列包含字符串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帖子。)
答案 0 :(得分:5)
目前为止所提供的方式都不能保证按宣传的方式工作,只对行的子集执行REPLACE
。
SQL Server does not guarantee short circuiting of predicates和can 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)
三级过滤器应该有效:
收集所有符合'%foo%'的行;
用非发生的字符串替换'foobar'的所有实例(例如''或许);
再次检查匹配'%foo%'
在这里,您只对可能匹配的行执行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'