SQL“赞”与“ =”性能


82

这个问题 绕着我想知道的地方,但是答案并没有完全解决。

使用通配符时,通常看起来'='比'like'更快。这似乎是传统观念。但是,假设我有一列包含有限数量的不同固定,硬编码,varchar标识符,并且我想选择与其中之一匹配的所有行:

select * from table where value like 'abc%'

select * from table where value = 'abcdefghijklmn'

“赞”只需要测试前三个字符即可找到匹配项,而“ =”必须比较整个字符串。在这种情况下,在所有其他条件相同的情况下,“喜欢”似乎会占优势。

这是一个一般性的学术问题,因此与哪个数据库无关,但它是使用SQL Server 2005产生的。


23
您遗漏的一件事是是否建立value索引。如果是的话,那么这=是一个简单的查找,不需要进行表扫描,并且可以消除LIKE您对它的任何声明。
丹尼尔·迪保罗

7
@Daniel我认为这是不正确的。LIKE末尾带有通配符的A是SARGable,因此将对索引执行范围查找,看不到表扫描。该范围查找可以轻松地与一条=语句竞争,并且在许多情况下(例如,如果所有令人满意的行都在一页上,这是不太可能的情况)可能具有完全相同的性能,需要读取的次数相同。
ErikE 2011年

我的“其他所有条件都相等”旨在解决“已编入索引或未编入索引”的问题,但是根据我对其他答案的评论,似乎至少会有一些争议,这将产生多大的差异。
MickeyfAgain_BeforeExitOfSO 2011年

看我的答案。最初,我测试了未索引并且性能相同(两个表扫描都完全相同)。在我的测试场景中,我假设它会被索引,否则您为什么还要关心性能?
JNK

5
在这个问题上所有关于“喜欢”的话题和答案使我们听起来像一群高中女生。完全一样
JulianR 2011年

Answers:


64

参见https://web.archive.org/web/20150209022016/http://myitforum.com/cs2/blogs/jnelson/archive/2007/11/16/108354.aspx

从那里引用:

LIKE使用索引的规则大致如下:

  • 如果您的过滤条件使用equals =且该字段已建立索引,则很可能它将使用INDEX / CLUSTERED INDEX SEEK

  • 如果您的过滤条件使用的是LIKE(没有通配符)(例如,如果您在Web报表中有一个参数可以包含%,但您使用的是完整字符串),则使用索引的可能性大约是#1。增加的成本几乎没有。

  • 如果您的过滤条件使用LIKE,但是在开头使用通配符(如Name0 LIKE'%UTER'),则使用索引的可能性要小得多,但它仍然至少可以对全部或部分范围执行INDEX SCAN索引。

  • 但是,如果您的过滤条件使用LIKE,但以STRING FIRST开头,并且之后有通配符(例如Name0 LIKE'COMP%ER'),则SQL可能仅使用INDEX SEEK快速查找具有相同首行的行起始字符,然后在这些行中查找完全匹配的字符。

(还请记住,SQL引擎可能仍未按照您期望的方式使用索引,具体取决于查询中正在发生的事情以及您要加入的表。SQL引擎保留重写您的索引的权利。进行一些查询,以一种认为最有效的方式获取数据,其中可能包括INDEX SCAN而不是INDEX SEEK)


1
该链接已失效
baxx

2
@baxx链接的副本可在返回机器中获得。web.archive.org/web/20150209022016/http://myitforum.com/cs2/...
alphabet5

45

这是可以衡量的差异。

运行以下命令:

Create Table #TempTester (id int, col1 varchar(20), value varchar(20))
go

INSERT INTO #TempTester (id, col1, value)
VALUES
(1, 'this is #1', 'abcdefghij')
GO

INSERT INTO #TempTester (id, col1, value)
VALUES
(2, 'this is #2', 'foob'),
(3, 'this is #3', 'abdefghic'),
(4, 'this is #4', 'other'),
(5, 'this is #5', 'zyx'),
(6, 'this is #6', 'zyx'),
(7, 'this is #7', 'zyx'),
(8, 'this is #8', 'klm'),
(9, 'this is #9', 'klm'),
(10, 'this is #10', 'zyx')
GO 10000

CREATE CLUSTERED INDEX ixId ON #TempTester(id)CREATE CLUSTERED INDEX ixId ON #TempTester(id)

CREATE NONCLUSTERED INDEX ixTesting ON #TempTester(value)

然后:

SET SHOWPLAN_XML ON

然后:

SELECT * FROM #TempTester WHERE value LIKE 'abc%'

SELECT * FROM #TempTester WHERE value = 'abcdefghij'

产生的执行计划显示你的第一个操作的成本,LIKE比较,大约是10倍,比更昂贵的=比较。

如果可以使用=比较,请这样做。


2
+1用于实际测试。仅看展览计划可能并不能说明整个故事。我将做一些自己的测试,如果发现任何意外,将通知所有人。
汤姆·H

1
汤姆-是的,但这给了我足够的迹象,表明两者在幕后的处理方式不同。
JNK

1
执行计划中显示的成本是错误的。它们不反映实际性能。在第一个计划中,它们基于估计的行数,19.95因此,SQL Server成本需要进行额外的19个关键查询,而这些查询实际上不会实现(即使在实际的执行计划中,显示的成本也基于估计的子树成本)
Martin Smith

我刚刚完成了您的测试以及大约100万行的测试,在两种情况下,性能和查询计划都是相同的。这是在SQL 2008上,因为我在这台计算机上没有2005。
汤姆·H

1
@JNK-刚刚尝试过-差异可以忽略不计,但是差异是相同的。327毫秒LIKE,203毫秒=。我希望如果我进行更多测试并获得准确的平均值,则#temp和real table之间不会有真正的区别。
将在

13

您还应该记住,使用时like,某些sql风格将忽略索引,这会降低性能。如果您不像示例那样使用“开始于”模式,则尤其如此。

您应该真正查看查询的执行计划,看看它在做什么,并尽可能少地猜测。

话虽如此,“开始于”模式可以并且在sql server中进行了优化。它使用表的索引。EF 4.0切换到likeStartsWith这个原因。


2
当like模式是查询的一部分并且通配符在尾时,任何值得其使用的关系数据库都不会忽略索引。如果您要绑定值并且数据库支持绑定与查询准备分开,则情况可能会不同。
戴夫·史密斯,

这也是我的直觉告诉我的,但是我在这方面只有sql服务器方面的实践经验,因此我专门针对它进行了介绍。
布林迪

7

如果value未建立索引,则两者都会进行表扫描。在这种情况下,性能差异可以忽略不计。

value已在Daniel中指出的那样,如果索引了索引,=则将导致索引查找为O(log N)性能。LIKE(很可能取决于它的选择程度)将导致对索引进行部分扫描,>= 'abc'并且与< 'abd'相比需要更多的工作=

请注意,我在这里说的是SQL Server-并非所有DBMS都可以使用LIKE。


我认为您不知道二进制搜索的工作原理。如果sql识别出模式(并且确实如此),则=大小写和like '...%'大小写的行为都相同,因为在两种情况下,都是基于比较关系选择子树的。
布林迪

知道了 如果选择性足够高,LIKE仍可能会表现得更糟,尽管它仍然是O(log N)-O(log N)找出从何处开始进行部分扫描,然后通过索引进行多次前向读取,直到到达终点'abd'
将在

是的,但是OP的示例假定该范围内只有一个值,因此请记住,比较将是相同的。
布林迪

正确的观点-这不是完全清楚,这是什么OP说的话,但我认为这是较有可能的情况下。在这种情况下,性能将几乎相同。
将在

LIKE的范围查找可能很容易与=语句竞争,并且在许多情况下(例如,如果所有令人满意的行都在一页上,这是不太可能的情况)可能具有完全相同的性能,需要读取的次数相同。我认为说“将需要更多的努力”是一个错误的笼统声明。
ErikE 2011年

5

您在问错问题。在数据库中,无关紧要的是运算符的性能,始终是表达式的SARGability和整个查询的可覆盖性。运营商本身的表现在很大程度上是无关紧要的。

那么,如何LIKE=在SARGability方面比较?LIKE,当与不是以常数开头的表达式一起使用时(例如,当使用时LIKE '%something'),根据定义是非SARGabale。但这使它=或可LIKE 'something%'储蓄吗?否。与任何有关SQL性能的问题一样,答案不在于查询文本,而在于部署的架构。如果存在满足它们的索引,这些表达式可能是SARGable 。

因此,说实话,=和之间存在细微差异LIKE。但是,问一个运算符或另一个运算符在SQL中是否“更快”就像问“什么更快,一辆红色轿车或一辆蓝色轿车?”。您应该问关于发动机尺寸和车辆重量的问题,而不是颜色。要解决有关优化关系表的问题,查找的地方是WHERE子句(和其他子句中的索引表达式),但是通常以WHERE开头)。


5

一个使用mysql 5.5的个人示例:我在2个表之间进行了内部联接,其中300万行之一和1万行之一。

在下面的索引上使用“赞”(不使用通配符)时,大约花费了30秒:

where login like '12345678'

使用“解释”,我得到:

在此处输入图片说明

在同一查询上使用'='时,大约需要0.1秒:

where login ='600009'

使用“解释”,我得到:

在此处输入图片说明

如您所见,like完全取消了索引查找,因此查询花费了300倍的时间。


您也可以简单地查看执行计划以确认这一点
LittleBobbyTables-Au Revoir,2015年

谢谢@LittleBobbyTables。会看看。
阿里斯

我不知道这是否是由于我的最新版本(5.7)引起的,但是LIKE不会在这里破坏我的唯一索引。
塞巴(Sebas)

0

也许您正在寻找全文搜索

与全文搜索相反,LIKE Transact-SQL谓词仅适用于字符模式。同样,您不能使用LIKE谓词来查询格式化的二进制数据。此外,针对大量非结构化文本数据的LIKE查询要比针对相同数据的等效全文查询慢得多。对数百万行文本数据的LIKE查询可能需要几分钟才能返回;而对相同数据进行全文查询仅需几秒钟或更短的时间,具体取决于返回的行数。


-1

首先,

他们并不总是平等的

    select 'Hello' from dual where 'Hello  ' like 'Hello';

    select 'Hello' from dual where 'Hello  ' =  'Hello';

当事情并非总是平等时,谈论它们的表现就没有那么重要了。

如果您正在处理字符串并且仅使用char变量,则可以谈论性能。但不要使用like和“ =”作为通常可互换的东西。

正如您在许多帖子(上面的问题和其他问题)中已经看到的那样,在它们相等的情况下,由于模式匹配(排序规则),like的性能会变慢


如果'Hello 'VARCHAR(默认),则您是正确的,但如果不是CHAR,则不是。将其强制转换为a,CHAR(7)并且都返回true。另外,在不使用TRIMvarchars的情况下,您到底在做什么?(请注意:至少在这种情况下SQL Server 2008r2
abluejelly
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.