MySQL:查询中的整理 - 任何副作用?
Posted
技术标签:
【中文标题】MySQL:查询中的整理 - 任何副作用?【英文标题】:MySQL: Collate in query - any side effects? 【发布时间】:2015-07-07 23:12:18 【问题描述】:我的OpenCart
表排序规则是utf8_bin
,遗憾的是我无法搜索名称中带有重音的产品名称。我在 Google 上进行了搜索,发现排序规则必须是 utf8_general_ci
才能进行重音兼容且不区分大小写的搜索。
如果我在搜索查询中添加整理声明会怎样?
SELECT *
FROM `address`
COLLATE utf8_general_ci
LIMIT 0 , 30
它有任何(坏的)副作用吗?我对索引、性能方面的问题感到担忧?还是完全安全?
【问题讨论】:
我怀疑一个简单的 select 语句会产生任何不良的副作用(除了您正在执行的 select 语句的性能问题)。毕竟,您并没有更改表定义。 【参考方案1】:这可能会有所帮助:UTF-8: General? Bin? Unicode?
请注意utf8_bin
也区分大小写。所以我会去将表格排序规则更改为utf8_general_ci
,并为未来安心。
【讨论】:
【参考方案2】:在using of COLLATE in SQL statements 中,我没有找到这种用法,无论如何,为了解释您使用排序规则的影响的主要问题,我找到了一些提示,但起初:
来自 dev.mysql.com:
非二进制字符串(存储在
CHAR
、VARCHAR
和TEXT
数据类型中)具有字符集和排序规则。一个给定的字符集可以有多个排序规则,每个排序规则都为集合中的字符定义了一个特定的排序和比较顺序。
-
排序规则只是用于字符串比较的排序——它(几乎)与用于数据存储的字符编码无关。我说几乎是因为排序规则只能用于某些字符集,所以更改排序规则可能会强制更改字符编码。
在修改字符编码的范围内,MySQL 将正确地将值重新编码为新字符集,无论是从单字节到多字节还是反之亦然。请注意,对于列而言太大的任何值都将被截断。[1]
二进制排序的实际优势在于它的速度,因为字符串比较非常简单/快速。在一般情况下,二进制索引可能不会产生预期的排序结果,但对于精确匹配,它们可能很有用。[2]
如果有多个操作数,可能会产生歧义。例如:
SELECT x FROM T WHERE x = 'Y';
比较应该使用列 x
的排序规则,还是字符串文字 'Y'
的排序规则? x
和 'Y'
都有排序规则,那么哪个排序规则优先?
标准 SQL 使用过去称为 “强制力” 的规则来解决此类问题。 [3]
ORDER BY
-[也在WHERE
]- 不能使用任何INDEX
;因此它的效率可能出奇的低。 [4]
由于强制排序规则是在与列编码相同的字符集上定义的,因此不会对性能产生任何影响(与将排序规则定义为列的默认值相比;而 utf8_general_ci
在比较中几乎肯定会比 @ 慢987654339@ 由于需要额外的查找/计算)。
但是,如果强制使用在不同字符集上定义的排序规则,MySQL 将不得不对列的值进行转码(这会影响性能)。[5]
【讨论】:
【参考方案3】:恐怕您必须考虑对查询性能的副作用,尤其是那些使用索引的副作用。这是一个简单的测试:
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 上的索引,这对您来说可能是个大问题。
为确保您的索引被用于查询,您可能需要将列排序规则更改为最常用的排序规则。
【讨论】:
MySQL 将停止使用索引仅用于此查询(在我的情况下为搜索查询)或将来的所有其他查询? 仅适用于使用不同排序规则的查询。如果您的查询不使用(默认)排序规则,我认为 mysql 仍然可以使用索引。【参考方案4】:如果可行,更改列定义。
ALTER TABLE tbl
MODIFY col VARCHAR(...) COLLATE utf8_general_ci ...;
(您应该包括列定义中已经存在的任何其他内容。)如果您有多个列要修改,请在同一个 ALTER 中执行所有操作(以提高速度)。
如果由于某种原因您不能使用ALTER
,那么,是的,您可以调整SELECT
以更改排序规则:
您提到的SELECT
没有用于过滤的WHERE
子句,所以让我更改测试用例:
假设你有这个,它只会找到“圣何塞”:
SELECT *
FROM tbl
WHERE city = 'San Jose'
包括San José
:
SELECT *
FROM tbl
WHERE city COLLATE utf8_general_ci = 'San Jose'
如果您可能有“组合重音”,请考虑使用 utf8_unicode_ci。 More on Combining Diacriticals 和 More on your topic。
至于副作用?没有,除了潜在的大一个:不能使用列上的索引。在我的第二个SELECT
(上图)中,INDEX(city)
没用。 ALTER
避免了对 SELECT
的这种性能损失,但一次性 ALTER
本身的成本很高。
【讨论】:
以上是关于MySQL:查询中的整理 - 任何副作用?的主要内容,如果未能解决你的问题,请参考以下文章