T-SQL 中是不是有 LIKE 语句的替代方法?
Posted
技术标签:
【中文标题】T-SQL 中是不是有 LIKE 语句的替代方法?【英文标题】:Is there and alternative to LIKE statement in T-SQL?T-SQL 中是否有 LIKE 语句的替代方法? 【发布时间】:2014-12-17 20:51:31 【问题描述】:我有一个场景,我需要执行以下操作:
SELECT *
FROM
[dbo].[MyTable]
WHERE
[Url] LIKE '%<some url>%';
我必须在 Url ('%<some url>%'
) 的开头和结尾使用两个 %(通配符),因为即使他键入部分文本,用户也应该能够搜索完整的 url。例如,如果 url 是 http://www.google.co.in 并且用户键入“goo”,则 url 必须出现在搜索结果中。 LIKE
运算符导致性能问题。 我需要一个替代方案,这样我就可以摆脱这个语句和通配符。换句话说,我不想在这种情况下使用 LIKE 语句。我尝试使用 T-SQL CONTAINS
,但它并没有解决我的问题。除了可以执行模式匹配并快速为我提供结果之外,还有其他替代方法吗?
【问题讨论】:
它以什么方式导致性能问题? 我已经简化了这个问题。请不要删除帖子。 您需要良好的索引。如果您在该列上没有正确的索引,则不同的运算符将无法帮助您。 @sharpcloud - 索引并没有那么大的帮助,因为它有一个前导通配符,它仍然需要扫描它。索引可以做的最好的事情是减少正在扫描的数据的宽度。 全文索引可能会有所帮助。 【参考方案1】:以 % 开始点赞将导致扫描。没有绕过它。它必须评估每个值。
如果你索引列,它应该是索引(而不是表)扫描。
您没有不会导致扫描的替代方法。 Charindex 和 patindex 是替代品,但仍会扫描而不修复性能问题。
您能否将组件拆分为单独的表格? 万维网 谷歌 合作 在
然后像“goo%”这样搜索? 这将使用索引,因为它不以 % 开头。
更好的是,您可以在“google”上搜索并获得索引搜索。
并且您希望在该表中具有唯一的字符串,并在 Int PK 上单独连接,这样它就不会返回多个 www 例如。
Suspect FullText Contains 并没有更快,因为 FullText 将 URL 保留为一个单词。
【讨论】:
这如何回答“T-SQL 中的 LIKE 语句是否有替代方案?”的问题 @MartinSmith 但是没有其他方法可以纠正性能问题。 因此,如果这是您的答案,请将其放入答案中。目前,这绝不会回答 OP 的原始问题,即要求 LIKE 的替代方案而不是 LIKE 的解释。 @Blam 不,索引前导 % 将导致 index 扫描,这仍然比表扫描快。实际结果显然取决于他的具体情况。 并非如此。或:仅当大部分数据已过时且未使用时。否则,与仅表相比,我们将降低 RAM 速度并将表和索引保留在内存中。【参考方案2】:您可以创建一个FULLTEXT 索引。
首先创建您的目录:
CREATE FULLTEXT CATALOG ft AS DEFAULT;
现在假设您的表名为 MyTable
,列是 TextColumn
,并且它有一个名为 UX_MyTable_TextColumn
的唯一索引:
CREATE FULLTEXT INDEX ON [dbo].[MyTable](TextColumn)
KEY INDEX UX_MyTable_TextColumn
现在您可以使用 CONTAINS 搜索表:
SELECT *
FROM MyTable
WHERE CONTAINS(TextColumn, 'searchterm')
【讨论】:
我刚试了下,还是不行。搜索“goo”不会产生“http://www.google.com
”。搜索“google”可以,只要您搜索的是类似单词的内容,这会有所帮助。
搜索*goo*
怎么样?
对我也不起作用。 “goo*”也没有。我应该立即承认我从未使用过全文索引,正是因为,一旦我阅读了它们,我就认为它们不能做我想做的事。
啊哈,成功了。如果我使用CONTAINS(TextColumn, '"goo*"')
,它会返回一个命中——注意双引号。但是不要太热情——CONTAINS(TextColumn, '"*oogle*"')
仍然没有任何结果。我想说全文索引选项需要大量仔细测试。
FTS 包含只能使用尾随通配符。如果它不以goo
开头,则永远不会返回。除了坚持LIKE '%goo%'
之外,似乎没有办法解决这个问题【参考方案3】:
据我所知,除了like
或contains
(全文搜索功能)之外,没有其他选择可以提供更好的性能。
您可以做的是尝试通过优化查询来提高性能。
为此,您需要了解您的用户以及他们将如何使用您的系统。
我怀疑大多数人会从地址的开头输入一个 URL(即没有协议),所以你可以这样做:
declare @searchTerm nvarchar(128) = 'goo'
set @searchTerm = coalesce(replace(@searchTerm ,'''',''''''),'')
select @searchTerm
SELECT *
FROM [dbo].[MyTable]
WHERE [Url] LIKE 'http://' + @searchTerm + '%'
or [Url] LIKE 'https://' + @searchTerm + '%'
or [Url] LIKE 'http://www.' + @searchTerm + '%'
or [Url] LIKE 'https://www.' + @searchTerm + '%'
or [Url] LIKE '%' + @searchTerm + '%'
option (fast 1); --get back the first result asap;
这会给你一些优化;即如果 url 的 http://www.google.com 可以使用 url 列上的索引,因为 http://www.goo 位于字符串的开头。
结尾处的option (fast 1)
以确保看到此好处;由于最后一个URL like %searchTerm%
不能使用索引,我们宁愿尽快返回响应,而不是等待那个缓慢的部分完成。
想想其他常见的使用模式和解决方法。
【讨论】:
如果它可以工作,那将是相关的 - 遗憾的是,我认为优化器只会进行表扫描,因为它必须无论如何 - 而不是使用多个搜索路径,然后使用 tempdb 来摆脱双打。虽然需要测试,但我认为这是一个完全不起作用的技巧。例如,将协议删除(到单独的字段中)会起作用...... @TomTom 同意删除协议会更好——也许有第二个搜索列可以去除协议并将 nvarchar 转换为 varchar;仅将第二列用于搜索。更好的是上面的 Blam 解决方案;即解析 URL 并搜索其组成部分。【参考方案4】:正如所写,您的查询无法进一步优化,也无法绕过LIKE
进行搜索。提高性能的唯一方法是减少 SELECT
以仅返回您不需要的所有列,并在 URL
上创建包含这些列的索引。 LIKE
将无法使用索引进行搜索,但减少扫描数据大小会有所帮助。如果您有支持压缩的 SQL Server 版本,那也会有所帮助。
例如,如果你真的只需要 A 列,那么写
SELECT A FROM [dbo].[MyTable] WHERE [Url] LIKE '%<some url>%';
并将索引创建为
CREATE INDEX IX_MyTable_URL
ON MyTable([Url])
INCLUDE (A) WITH (DATA_COMPRESSION = PAGE);
如果 A 已经包含在您的主键中,则不需要 INCLUDE。
【讨论】:
【参考方案5】:您的查询非常简单,我认为没有理由让它变慢。 dbms 将读取记录以获取记录并比较字符串。通常它甚至可以在并行线程中执行此操作。
您认为您的陈述如此缓慢的原因是什么?您的表中有数十亿条记录吗?您的记录包含这么多数据吗?
您最好的选择不是关心查询,而是关心数据库和您的系统。其他人已经建议在 url 列上建立索引,因此可以扫描索引而不是扫描表。最大并行度是否设置错误?你的桌子是碎片化的吗?你的硬件合适吗?这些是这里要考虑的事情。
但是:charindex('oogl', url) > 0
与 url like '%oogl%'
的作用相同,但在内部它们的工作方式有所不同。对于某些人来说,LIKE 表达式的结果更快,对于其他人来说,CHARINDEX 方法。也许这取决于查询、处理器数量、操作系统等等。可能值得一试。
【讨论】:
谢谢你们的回答。他们真的很有帮助。 :)以上是关于T-SQL 中是不是有 LIKE 语句的替代方法?的主要内容,如果未能解决你的问题,请参考以下文章
T-SQL 转义select …. like中的特殊字符(百分号)