MySQL:整理查询 - 有任何副作用吗?
MySQL: Collate in query - any side effects?
我的 OpenCart
table 排序规则是 utf8_bin
,不幸的是我无法搜索名称中带有重音符号的产品名称。我在 Google 上搜索,发现排序规则必须是 utf8_general_ci
才能进行重音兼容和不区分大小写的搜索。
如果我在搜索查询中添加整理声明会怎么样?
SELECT *
FROM `address`
COLLATE utf8_general_ci
LIMIT 0 , 30
它有任何(不良)副作用吗?我对索引、性能方面的问题很生气?还是完全安全?
在 using of COLLATE in SQL statements 中,我没有找到那个用法,无论如何,为了解释你使用归类的效果的主要问题,我找到了一些提示,但起初:
来自dev.mysql.com:
Nonbinary strings (as stored in the CHAR
, VARCHAR
, and TEXT
data types) have a character set and collation. A given character set can have several collations, each of which defines a particular sorting and comparison order for the characters in the set.
- 归类仅仅是用于字符串比较的排序——它(几乎)与用于数据存储的字符编码无关。我说几乎是因为排序规则只能用于某些字符集,所以更改排序规则可能会强制更改字符编码。
在字符编码被修改的范围内,MySQL 将正确地将值重新编码为新字符集,无论是从单字节到多字节还是反之。请注意,对于列来说太大的任何值都将被截断。[1]
- 二进制排序的实际优势在于它的速度,因为字符串比较非常 simple/fast。在一般情况下,二进制索引可能不会产生预期的排序结果,但对于精确匹配它们可能很有用。[2]
对于多个操作数,可能会出现歧义。例如:
SELECT x FROM T WHERE x = 'Y';
比较应该使用列 x
的排序规则还是字符串文字 'Y'
的排序规则? x
和 'Y'
都有排序规则,那么哪个排序规则优先?
标准 SQL 使用过去称为 “可强制性” 的规则来解决此类问题。 [3]
- 如果更改字段的排序规则,
ORDER BY
-[也在 WHERE
]- 不能使用任何 INDEX
;因此它的效率可能出奇地低。 [4]
- 由于强制排序规则是在与列编码相同的字符集上定义的,因此不会对性能产生任何影响(相对于将该排序规则定义为列的默认排序规则;而
utf8_general_ci
几乎肯定会执行得更慢在比较中比 utf8_bin
由于需要额外的 lookups/computation)。
但是,如果强制使用在不同字符集上定义的排序规则,MySQL 将不得不转码列的值(这会影响性能)。[5]
如果可行,请更改列定义。
ALTER TABLE tbl
MODIFY col VARCHAR(...) COLLATE utf8_general_ci ...;
(您应该包括列定义中已有的任何其他内容。)如果您有多个要修改的列,请在同一个 ALTER 中全部执行(为了速度)。
如果由于某种原因您无法执行 ALTER
,那么可以调整 SELECT
来更改排序规则:
你说的SELECT
没有过滤的WHERE
子句,我改一下测试用例:
假设你有这个,它只会找到 'San Jose':
SELECT *
FROM tbl
WHERE city = 'San Jose'
包括San José
:
SELECT *
FROM tbl
WHERE city COLLATE utf8_general_ci = 'San Jose'
如果您可能 "combining accents",请考虑使用 utf8_unicode_ci。 More on Combining Diacriticals and More on your topic.
至于副作用? None except for on potentially big one: 无法使用列上的索引。在我的第二个 SELECT
(上图)中,INDEX(city)
是没有用的。 ALTER
避免了 SELECT
的这种性能损失,但是一次性 ALTER
本身是昂贵的。
恐怕你必须考虑对查询性能的副作用,尤其是那些使用索引的。这是一个简单的测试:
mysql> create table aaa (a1 varchar(100) collate latin1_general_ci, tot int);
insert into aaa values('test1',3) , ('test2',4), ('test5',5);
mysql> create index aindex on aaa (a1);
Query OK, 0 rows affected (0.59 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc aaa;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| a1 | varchar(100) | YES | MUL | NULL | |
| tot | int(11) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.53 sec)
mysql> explain select * from aaa where a1='test1' ;
+----+-------------+-------+------+---------------+--------+---------+-------+--
----+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | r
ows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+--
----+-----------------------+
| 1 | SIMPLE | aaa | ref | aindex | aindex | 103 | const |
1 | Using index condition |
+----+-------------+-------+------+---------------+--------+---------+-------+--
----+-----------------------+
1 row in set (0.13 sec)
mysql> explain select * from aaa where a1='test1' collate utf8_general_ci;
+----+-------------+-------+------+---------------+------+---------+------+-----
-+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows
| Extra |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+-------------+
| 1 | SIMPLE | aaa | ALL | NULL | NULL | NULL | NULL | 3
| Using where |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+-------------+
1 row in set (0.06 sec)
您可以看到,当您使用其他排序规则搜索 a1 时,MySQL 正在停止使用 a1 上的索引,这对您来说可能是个大问题。
为确保您的索引用于查询,您可能必须将列排序规则更改为最常用的排序规则。
这可能有帮助:UTF-8: General? Bin? Unicode?
请注意 utf8_bin
也区分大小写。所以我会去改变 table 归类为 utf8_general_ci
并为未来安心。
我的 OpenCart
table 排序规则是 utf8_bin
,不幸的是我无法搜索名称中带有重音符号的产品名称。我在 Google 上搜索,发现排序规则必须是 utf8_general_ci
才能进行重音兼容和不区分大小写的搜索。
如果我在搜索查询中添加整理声明会怎么样?
SELECT *
FROM `address`
COLLATE utf8_general_ci
LIMIT 0 , 30
它有任何(不良)副作用吗?我对索引、性能方面的问题很生气?还是完全安全?
在 using of COLLATE in SQL statements 中,我没有找到那个用法,无论如何,为了解释你使用归类的效果的主要问题,我找到了一些提示,但起初:
来自dev.mysql.com:
Nonbinary strings (as stored in the
CHAR
,VARCHAR
, andTEXT
data types) have a character set and collation. A given character set can have several collations, each of which defines a particular sorting and comparison order for the characters in the set.
- 归类仅仅是用于字符串比较的排序——它(几乎)与用于数据存储的字符编码无关。我说几乎是因为排序规则只能用于某些字符集,所以更改排序规则可能会强制更改字符编码。
在字符编码被修改的范围内,MySQL 将正确地将值重新编码为新字符集,无论是从单字节到多字节还是反之。请注意,对于列来说太大的任何值都将被截断。[1] - 二进制排序的实际优势在于它的速度,因为字符串比较非常 simple/fast。在一般情况下,二进制索引可能不会产生预期的排序结果,但对于精确匹配它们可能很有用。[2]
对于多个操作数,可能会出现歧义。例如:
SELECT x FROM T WHERE x = 'Y';
比较应该使用列
x
的排序规则还是字符串文字'Y'
的排序规则?x
和'Y'
都有排序规则,那么哪个排序规则优先?
标准 SQL 使用过去称为 “可强制性” 的规则来解决此类问题。 [3]- 如果更改字段的排序规则,
ORDER BY
-[也在WHERE
]- 不能使用任何INDEX
;因此它的效率可能出奇地低。 [4] - 由于强制排序规则是在与列编码相同的字符集上定义的,因此不会对性能产生任何影响(相对于将该排序规则定义为列的默认排序规则;而
utf8_general_ci
几乎肯定会执行得更慢在比较中比utf8_bin
由于需要额外的 lookups/computation)。
但是,如果强制使用在不同字符集上定义的排序规则,MySQL 将不得不转码列的值(这会影响性能)。[5]
如果可行,请更改列定义。
ALTER TABLE tbl
MODIFY col VARCHAR(...) COLLATE utf8_general_ci ...;
(您应该包括列定义中已有的任何其他内容。)如果您有多个要修改的列,请在同一个 ALTER 中全部执行(为了速度)。
如果由于某种原因您无法执行 ALTER
,那么可以调整 SELECT
来更改排序规则:
你说的SELECT
没有过滤的WHERE
子句,我改一下测试用例:
假设你有这个,它只会找到 'San Jose':
SELECT *
FROM tbl
WHERE city = 'San Jose'
包括San José
:
SELECT *
FROM tbl
WHERE city COLLATE utf8_general_ci = 'San Jose'
如果您可能 "combining accents",请考虑使用 utf8_unicode_ci。 More on Combining Diacriticals and More on your topic.
至于副作用? None except for on potentially big one: 无法使用列上的索引。在我的第二个 SELECT
(上图)中,INDEX(city)
是没有用的。 ALTER
避免了 SELECT
的这种性能损失,但是一次性 ALTER
本身是昂贵的。
恐怕你必须考虑对查询性能的副作用,尤其是那些使用索引的。这是一个简单的测试:
mysql> create table aaa (a1 varchar(100) collate latin1_general_ci, tot int);
insert into aaa values('test1',3) , ('test2',4), ('test5',5);
mysql> create index aindex on aaa (a1);
Query OK, 0 rows affected (0.59 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc aaa;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| a1 | varchar(100) | YES | MUL | NULL | |
| tot | int(11) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.53 sec)
mysql> explain select * from aaa where a1='test1' ;
+----+-------------+-------+------+---------------+--------+---------+-------+--
----+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | r
ows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+--
----+-----------------------+
| 1 | SIMPLE | aaa | ref | aindex | aindex | 103 | const |
1 | Using index condition |
+----+-------------+-------+------+---------------+--------+---------+-------+--
----+-----------------------+
1 row in set (0.13 sec)
mysql> explain select * from aaa where a1='test1' collate utf8_general_ci;
+----+-------------+-------+------+---------------+------+---------+------+-----
-+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows
| Extra |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+-------------+
| 1 | SIMPLE | aaa | ALL | NULL | NULL | NULL | NULL | 3
| Using where |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+-------------+
1 row in set (0.06 sec)
您可以看到,当您使用其他排序规则搜索 a1 时,MySQL 正在停止使用 a1 上的索引,这对您来说可能是个大问题。
为确保您的索引用于查询,您可能必须将列排序规则更改为最常用的排序规则。
这可能有帮助:UTF-8: General? Bin? Unicode?
请注意 utf8_bin
也区分大小写。所以我会去改变 table 归类为 utf8_general_ci
并为未来安心。