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

非二进制字符串(存储在CHARVARCHARTEXT 数据类型中)具有字符集和排序规则。一个给定的字符集可以有多个排序规则,每个排序规则都为集合中的字符定义了一个特定的排序比较顺序

    排序规则只是用于字符串比较的排序——它(几乎)与用于数据存储的字符编码无关。我说几乎是因为排序规则只能用于某些字符集,所以更改排序规则可能会强制更改字符编码。 在修改字符编码的范围内,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:查询中的整理 - 任何副作用?的主要内容,如果未能解决你的问题,请参考以下文章

嵌套查询及其作用域:

mysql视图的作用是啥

MySQL中的外键是什么有什么作用

MySQL整理

游标的作用是啥?

MySql数据库之视图(定义视图查询视图更新视图视图的作用)