PDO使用utf8mb4_unicode_ci排序规则查询MySQL无返回结果问题
问题根因及解决方案
这个问题本质是PHP端到MySQL连接层的字符编码隐式转换不匹配,虽然你已经配置了utf8mb4,仍有几个容易忽略的坑会导致结果不一致:
1. DSN字符集配置格式错误
很多开发者写DSN时会给charset参数多加多余分号,或者使用PHP 5.3.6以下版本时DSN的charset参数本身不生效:
你配置的;charset=utf8mb4前面多了分号,如果拼接DSN时末尾没有其他参数,会导致charset参数识别失败,PDO实际用默认的latin1编码建立连接。
正确的DSN格式示例:
$dsn = 'mysql:host=localhost;dbname=your_db;charset=utf8mb4';
如果使用PHP 5.3.6以前的版本,DSN的charset参数无效,必须配合PDO属性配置保证编码生效。
2. PDO模拟预处理未关闭导致字符转码异常
PDO默认开启模拟预处理时,会在PHP本地完成参数转义,若本地使用的字符集和配置的utf8mb4不一致,就会把ø这类特殊字符转成乱码,传到MySQL后自然匹配不到结果。phpMyAdmin默认使用真实预处理,不会触发本地转码逻辑,所以查询结果正常。
添加以下PDO初始化配置即可解决:
$this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
关闭模拟预处理后,参数会直接传给MySQL服务端处理,不会出现本地转码错误。
3. 查询字符串本身编码不是UTF-8
如果存放SQL语句的PHP文件本身保存编码不是UTF-8(无BOM),会导致硬编码在SQL里的ø字符本身就是乱码,传到MySQL后无法匹配。phpMyAdmin界面默认是UTF-8编码,输入的字符格式正常,所以能查到结果。
解决方法:用文本编辑器打开对应PHP文件,确认保存编码为UTF-8(无BOM格式);如果是动态拼接的查询条件,要保证传入的字符串来源编码也是UTF-8。
4. 表字段排序规则和连接排序规则不匹配
虽然你执行了SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci,但要确认last_name和first_name字段的排序规则也为utf8mb4_unicode_ci,如果字段用了其他排序规则(比如utf8mb4_general_ci或者旧版utf8系列排序规则),跨排序规则比较时会出现字符识别异常。
可以执行以下SQL查看字段的实际排序规则:
SHOW FULL COLUMNS FROM 你的表名 WHERE Field IN ('last_name','first_name');
如果不一致,修改字段排序规则即可:
ALTER TABLE 你的表名 MODIFY COLUMN last_name VARCHAR(字段长度) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL; ALTER TABLE 你的表名 MODIFY COLUMN first_name VARCHAR(字段长度) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
内容的提问来源于stack exchange,提问作者RPB1001

