使用 SQL 时,使用=
inWHERE
子句而不是有什么好处LIKE
?
不用任何特殊运算符,LIKE
都是=
一样的吧?
LIKE
并且=
是不同的运营商。这里的大多数答案都集中在通配符支持上,这不是这些运算符之间的唯一区别!
=
是对数字和字符串进行操作的比较运算符。比较字符串时,比较运算符会比较整个字符串。
LIKE
是一个逐字符比较的字符串运算符。
更复杂的是,两个运算符都使用了一个排序规则,它会对比较结果产生重要影响。
让我们首先确定一个示例,其中这些运算符产生明显不同的结果。请允许我引用 MySQL 手册:
根据 SQL 标准,LIKE 在每个字符的基础上执行匹配,因此它可以产生与 = 比较运算符不同的结果:
mysql> SELECT 'ä' LIKE 'ae' COLLATE latin1_german2_ci;
+-----------------------------------------+
| 'ä' LIKE 'ae' COLLATE latin1_german2_ci |
+-----------------------------------------+
| 0 |
+-----------------------------------------+
mysql> SELECT 'ä' = 'ae' COLLATE latin1_german2_ci;
+--------------------------------------+
| 'ä' = 'ae' COLLATE latin1_german2_ci |
+--------------------------------------+
| 1 |
+--------------------------------------+
请注意,MySQL 手册的这一页称为字符串比较函数,=
没有讨论,这意味着=
严格来说它不是字符串比较函数。
=
工作?SQL 标准 § 8.2描述了如何比较=
字符串:
两个字符串的比较确定如下:
a) 如果 X 的字符长度不等于 Y 的字符长度,则为了比较,较短的字符串被有效地替换为已扩展至较长字符串长度的自身副本通过在一个或多个填充字符的右侧连接,其中填充字符是根据 CS 选择的。如果 CS 具有 NO PAD 属性,则填充字符是一个依赖于实现的字符,它不同于 X 和 Y 字符集中的任何字符,其排序小于 CS 下的任何字符串。否则,填充字符是 <space>。
b) X 和 Y 的比较结果由整理序列 CS 给出。
c) 根据整理顺序,即使两个字符串的长度不同或包含不同的字符序列,它们也可能比较相等。当操作 MAX、MIN、DISTINCT、对分组列的引用以及 UNION、EXCEPT 和 INTERSECT 操作符引用字符串时,这些操作从一组这样的相等值中选择的具体值取决于实现。
(强调补充。)
这是什么意思?这意味着在比较字符串时,=
运算符只是当前排序规则的一个薄包装器。排序规则是具有用于比较字符串的各种规则的库。下面是一个来自 MySQL 的二进制排序规则示例:
static int my_strnncoll_binary(const CHARSET_INFO *cs __attribute__((unused)),
const uchar *s, size_t slen,
const uchar *t, size_t tlen,
my_bool t_is_prefix)
{
size_t len= MY_MIN(slen,tlen);
int cmp= memcmp(s,t,len);
return cmp ? cmp : (int)((t_is_prefix ? len : slen) - tlen);
}
这种特殊的排序规则恰好是逐字节比较(这就是它被称为“二进制”的原因——它没有给字符串赋予任何特殊含义)。其他排序规则可能会提供更高级的比较。
例如,这里是一个支持不区分大小写比较的UTF-8 排序规则。代码太长,无法在此处粘贴,但请转到该链接并阅读my_strnncollsp_utf8mb4()
. 此排序规则一次可以处理多个字节,并且可以应用各种转换(例如不区分大小写的比较)。=
运算符完全从变幻莫测的排序规则中抽象出来。
LIKE
工作?SQL 标准 § 8.5描述了如何比较LIKE
字符串:
<谓词>
M LIKE P
如果存在将 M 划分为子串的情况,则为真:
i) M 的子串是 M 的 0 个或多个连续的<字符表示>的序列,并且 M 的每个 <字符表示>恰好是一个子串的一部分。
ii) 如果 P 的第 i 个子串说明符是任意字符说明符,则 M 的第 i 个子串是任何单个 <character representation>。
iii) 如果 P 的第 i 个子串说明符是任意字符串说明符,则 M 的第 i 个子串是 0 个或多个 <character representation> 的任意序列。
iv) 如果 P 的第 i 个子字符串说明符既不是任意字符说明符也不是任意字符串说明符,则根据 <like predicate> 的整理顺序,M 的第 i 个子字符串等于该子字符串说明符,没有将 <space> 字符附加到 M,并且具有与该子字符串说明符相同的长度。
v) M 的子串数等于 P 的子串说明符数。
(强调补充。)
这很罗嗦,所以让我们分解一下。项 ii 和 iii 分别指通配符_
和%
。如果P
不包含任何通配符,则仅适用第 iv 项。这是 OP 提出的感兴趣的案例。
在这种情况下,它使用当前排序规则将每个“子字符串”(单个字符)M
与每个子字符串进行比较。P
底线是比较字符串时,=
比较整个字符串,而LIKE
一次比较一个字符。两种比较都使用当前排序规则。这种差异在某些情况下会导致不同的结果,如本文的第一个示例所示。
你应该使用哪一个?没有人能告诉你——你需要使用适合你用例的那个。不要通过切换比较运算符过早地进行优化。
等于 (=) 运算符是“比较运算符比较两个值是否相等”。换句话说,在 SQL 语句中,除非等式两边相等,否则它不会返回 true。例如:
SELECT * FROM Store WHERE Quantity = 200;
LIKE 运算符“实现了模式匹配比较”,它试图将“字符串值与包含通配符的模式字符串进行匹配”。例如:
SELECT * FROM Employees WHERE Name LIKE 'Chris%';
LIKE 通常仅与字符串一起使用,而等于(我相信)更快。等于运算符将通配符视为文字字符。返回结果的差异如下:
SELECT * FROM Employees WHERE Name = 'Chris';
和
SELECT * FROM Employees WHERE Name LIKE 'Chris';
将返回相同的结果,尽管使用 LIKE 通常需要更长的时间作为模式匹配。然而,
SELECT * FROM Employees WHERE Name = 'Chris%';
和
SELECT * FROM Employees WHERE Name LIKE 'Chris%';
将返回不同的结果,其中使用“=”只会返回“Chris%”的结果,而 LIKE 运算符将返回以“Chris”开头的任何内容。
希望有帮助。可以在这里找到一些好的信息。
这是我对SQL 'like' vs '=' 性能问题的另一个答案的复制/粘贴:
一个使用 mysql 5.5 的个人示例:我在 2 个表之间有一个内部连接,一个是 300 万行,另一个是 10000 行。
在如下索引上使用 like 时(无通配符),大约需要 30 秒:
where login like '12345678'
使用“解释”我得到:
在同一查询上使用“=”时,大约需要 0.1 秒:
where login ='12345678'
使用“解释”我得到:
如您所见,like
完全取消了索引查找,因此查询花费了 300 倍以上的时间。
一个区别——除了可以在 LIKE 中使用通配符——在于尾随空格:= 运算符忽略尾随空格,但 LIKE 不会。
取决于数据库系统。
一般不用特殊字符,是的,=和LIKE是一样的。
但是,某些数据库系统可能会使用不同的运算符以不同的方式处理排序规则设置。
例如,在 MySQL 中,与 = 对字符串的比较默认情况下始终不区分大小写,因此没有特殊字符的 LIKE 是相同的。在其他一些 RDBMS 上,LIKE 不区分大小写,而 = 则不区分大小写。
对于这个例子,我们理所当然地认为 varcharcol 不包含''
并且没有针对该列的空单元格
select * from some_table where varcharCol = ''
select * from some_table where varcharCol like ''
第一个导致 0 行输出,而第二个显示整个列表。= 是严格匹配大小写的,而 like 就像一个过滤器。如果过滤器没有条件,则每个数据都是有效的。
like - 由于它的目的,它的工作速度有点慢,并且旨在与 varchar 和类似数据一起使用。
在运行时构建查询时,使用 = 可避免字符串中的通配符和特殊字符冲突。
这使得程序员的生活更轻松,因为不必转义所有可能滑入 LIKE 子句并且不会产生预期结果的特殊通配符。毕竟,= 是 99% 的用例场景,每次都必须逃避它们会很痛苦。
在 90 年代翻白眼
我也怀疑它有点慢,但如果模式中没有通配符,我怀疑它是否重要。
如果您搜索完全匹配,您可以同时使用 = 和 LIKE。
在这种情况下使用“=”会稍微快一点(搜索完全匹配) - 您可以通过在 SQL Server Management Studio 中两次使用相同的查询来自己检查这一点,一次使用“=”,一次使用“LIKE”,并且然后使用“查询”/“包括实际执行计划”。
执行这两个查询,您应该会看到两次结果,以及两个实际的执行计划。在我的例子中,它们被分成 50% 和 50%,但是“=”执行计划的“估计子树成本”更小(当您将鼠标悬停在最左边的“SELECT”框上时显示) - 但同样,它真的差别不大。
但是,当您开始在 LIKE 表达式中使用通配符进行搜索时,搜索性能会下降。搜索“LIKE Mill%”仍然可以很快 - SQL Server 可以在该列上使用索引,如果有的话。搜索“LIKE %expression%”非常慢,因为 SQL Server 满足此搜索的唯一方法是执行全表扫描。所以要小心你的LIKE!
马克
为了解决有关性能的原始问题,它归结为索引利用率。当发生简单的表扫描时,“LIKE”和“=”是相同的。当涉及到索引时,这取决于LIKE 子句是如何形成的。更具体地说,通配符的位置是什么?
考虑以下:
CREATE TABLE test(
txt_col varchar(10) NOT NULL
)
go
insert test (txt_col)
select CONVERT(varchar(10), row_number() over (order by (select 1))) r
from master..spt_values a, master..spt_values b
go
CREATE INDEX IX_test_data
ON test (txt_col);
go
--Turn on Show Execution Plan
set statistics io on
--A LIKE Clause with a wildcard at the beginning
DBCC DROPCLEANBUFFERS
SELECT txt_Col from test where txt_col like '%10000'
--Results in
--Table 'test'. Scan count 3, logical reads 15404, physical reads 2, read-ahead reads 15416, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Index SCAN is 85% of Query Cost
--A LIKE Clause with a wildcard in the middle
DBCC DROPCLEANBUFFERS
SELECT txt_Col from test where txt_col like '1%99'
--Results in
--Table 'test'. Scan count 1, logical reads 3023, physical reads 3, read-ahead reads 3018, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Index Seek is 100% of Query Cost for test data, but it may result in a Table Scan depending on table size/structure
--A LIKE Clause with no wildcards
DBCC DROPCLEANBUFFERS
SELECT txt_Col from test where txt_col like '10000'
--Results in
--Table 'test'. Scan count 1, logical reads 3, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Index Seek is 100% of Query Cost
GO
--an "=" clause = does Index Seek same as above
DBCC DROPCLEANBUFFERS
SELECT txt_Col from test where txt_col = '10000'
--Results in
--Table 'test'. Scan count 1, logical reads 3, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Index Seek is 100% of Query Cost
GO
DROP TABLE test
使用“=”与“LIKE”时,查询计划的创建也可能存在微不足道的差异。
=
除了通配符之外, AND之间的区别LIKE
还取决于 SQL 服务器的类型和列类型。
举个例子:
CREATE TABLE testtable (
varchar_name VARCHAR(10),
char_name CHAR(10),
val INTEGER
);
INSERT INTO testtable(varchar_name, char_name, val)
VALUES ('A', 'A', 10), ('B', 'B', 20);
SELECT 'VarChar Eq Without Space', val FROM testtable WHERE varchar_name='A'
UNION ALL
SELECT 'VarChar Eq With Space', val FROM testtable WHERE varchar_name='A '
UNION ALL
SELECT 'VarChar Like Without Space', val FROM testtable WHERE varchar_name LIKE 'A'
UNION ALL
SELECT 'VarChar Like Space', val FROM testtable WHERE varchar_name LIKE 'A '
UNION ALL
SELECT 'Char Eq Without Space', val FROM testtable WHERE char_name='A'
UNION ALL
SELECT 'Char Eq With Space', val FROM testtable WHERE char_name='A '
UNION ALL
SELECT 'Char Like Without Space', val FROM testtable WHERE char_name LIKE 'A'
UNION ALL
SELECT 'Char Like With Space', val FROM testtable WHERE char_name LIKE 'A '
使用MS SQL Server 2012时,比较中将忽略尾随空格,但LIKE
列类型为VARCHAR
.
使用MySQL 5.5,尾随空格将被忽略=
,但对于和LIKE
都不会。CHAR
VARCHAR
使用PostgreSQL 9.1时,空格对两者都很重要=
using LIKE
,VARCHAR
但不是CHAR
(请参阅文档)。
的行为LIKE
也与 不同CHAR
。
使用与上面相同的数据,CAST
在列名上使用显式也会有所不同:
SELECT 'CAST none', val FROM testtable WHERE char_name LIKE 'A'
UNION ALL
SELECT 'CAST both', val FROM testtable WHERE
CAST(char_name AS CHAR) LIKE CAST('A' AS CHAR)
UNION ALL
SELECT 'CAST col', val FROM testtable WHERE CAST(char_name AS CHAR) LIKE 'A'
UNION ALL
SELECT 'CAST value', val FROM testtable WHERE char_name LIKE CAST('A' AS CHAR)
这仅返回“CAST both”和“CAST col”的行。
实际上,它归结为您希望查询执行的操作。如果您的意思是完全匹配,请使用 =。如果您的意思是模糊匹配,请使用 LIKE。说出你的意思通常是一个很好的代码策略。
LIKE 关键字无疑带有附加的“性能价格标签”。也就是说,如果您的输入字段可能包含要在查询中使用的通配符,我建议仅在输入包含通配符之一时使用 LIKE。否则,使用等于比较的标准。
最好的祝福...
在 Oracle 中,没有通配符的“like”将返回与“equals”相同的结果,但可能需要额外的处理。根据 Tom Kyte的说法,Oracle 将在使用文字时将不带通配符的“like”视为“等于”,但在使用绑定变量时不会。
=
比 快得多LIKE
。
在 11GB 数据和超过 1000 万条记录的 MySQL 上测试,f_time 列被索引。
SELECT * FROM XXXXX WHERE f_time = '1621442261'
- 花费 0.00 秒并返回 330 条记录
SELECT * FROM XXXXX WHERE f_time like '1621442261'
- 耗时 44.71 秒,返回 330 条记录
=
和LIKE
不一样;
=
匹配确切的字符串LIKE
匹配可能包含通配符 (%) 的字符串