☰
困扰了三天的mysql存储过程报错[1267]:用DBeaver排查排序规则冲突的完整记录
2026/10/8 18:00:16 网站建设 项目流程

1. 报错[1267]到底在说什么:从一次存储过程执行失败说起

[1267] Illegal mix of collations这个报错,直译过来就是「排序规则非法混用」。它经常出现在 MySQL 8.0 的存储过程里,尤其是你从旧版本迁移过来、或者用外部编辑器改过存储过程定义之后。我第一次遇到它的时候,存储过程本身逻辑完全没问题,单独把里面的 SQL 拎出来在 DBeaver 里跑也正常,可一旦CALL就炸,报错信息还特别长:

[1267] Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='

这句话的核心信息有两层。第一层,参与比较的两个字段(或者字段和变量)用了不同的排序规则:一个是utf8mb4_unicode_ci,另一个是utf8mb4_0900_ai_ci。第二层,IMPLICIT表示这个排序规则不是显式写死的,而是从表定义、列定义或者连接会话里「继承」来的。MySQL 在比较两个字符串时,如果排序规则不一致,又没法自动推导出一个公共规则,就会直接抛 1267。

为什么存储过程特别容易踩这个坑?因为存储过程在创建时,参数、局部变量、游标里的字段都会带上当时的字符集和排序规则上下文。MySQL 8.0 默认字符集是utf8mb4,默认排序规则是utf8mb4_0900_ai_ci;而很多老库、老表建的时候用的是utf8mb4_unicode_ci甚至utf8mb4_general_ci。当存储过程里的变量(继承会话或库的0900_ai_ci)去和表字段(unicode_ci)做=、JOIN、IN比较时,冲突就发生了。

这个场景适合谁看?适合所有在 MySQL 8.0 上维护存储过程、用 DBeaver 做日常开发、并且被 1267 卡住过的后端和 DBA。接下来我会把排查路径拆成可复制的步骤:先确认会话和库的排序规则,再定位到底是哪两个对象在冲突,最后给出改存储过程定义并重新验证的完整流程。整个过程不需要你背概念,照着查就行。

2. 用 TaoToken 前置准备:把排查思路和模型对话结合起来

排查 1267 这种报错,最耗时间的其实不是改代码,而是「猜」。报错只告诉你两个排序规则不一致,但没告诉你具体是哪一行、哪两个字段。这时候如果能有一个地方帮你快速梳理排查路径、解释information_schema查询结果,效率会高很多。我平时会把这类问题丢给 TaoToken 的模型对话来辅助分析,它能把报错语义、可能的冲突点和对应的查询语句一起理出来,省去反复翻文档的时间。

TaoToken 是一个聚合多种大模型能力的平台,你可以把它理解成一个「统一入口」:不用在多个模型服务之间来回切换账号和 Key,通过一套 API 就能调用不同模型。对于排查数据库报错这种需要「解释 + 给命令」的场景,它比较实用。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api (这个不加 UTM)。

如果你只是想快速问几个排查问题,直接用模型对话就行:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。如果你打算把这种「报错分析 + 生成查询」的能力接进自己的脚本或 IDE 插件里,那需要先拿到 API Key,在控制台创建:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,然后到 API Keys 页面生成密钥:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有 Base URL、鉴权方式和各模型的 Model ID 说明。

这里要强调一点:TaoToken 是辅助你分析和生成排查命令的工具,不是用来替代 DBeaver 或 MySQL 客户端的。真正的排序规则查询、存储过程修改、CALL验证,还是要在你的数据库环境里完成。把两者配合起来用,思路是:先用模型对话把「该查什么」列清楚,再回到 DBeaver 里执行。

对于长期要做数据库开发、经常和存储过程打交道的人,可以考虑 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,它更适合把模型能力持续用在编码和排障流程里。如果你用的是 Claude Code 这类命令行工具,接入方式参考:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite 。下面进入正题,先查会话和库的排序规则。

3. 可复制配置:查会话排序规则、定位冲突字段、改存储过程定义

排查 1267 的第一步,是确认「当前会话」和「当前库」的字符集与排序规则。因为存储过程执行时的变量上下文,很大程度上受会话影响。在 DBeaver 里新建一个 SQL 编辑器,执行下面这组查询:

-- 查看与会话、字符集、排序规则相关的系统变量 SHOW VARIABLES LIKE 'character_set%'; SHOW VARIABLES LIKE 'collation%'; -- 查看当前数据库的默认字符集和排序规则 SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = DATABASE();

执行后重点看三个值:character_set_client、character_set_connection、collation_connection。如果collation_connection是utf8mb4_0900_ai_ci,而你的表字段是utf8mb4_unicode_ci,那冲突的种子就已经埋下了。接着查表级和列级的排序规则:

-- 查看指定库里所有表的排序规则 SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = '你的库名' ORDER BY TABLE_COLLATION, TABLE_NAME; -- 查看指定表的列级字符集与排序规则 SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '你的库名' AND TABLE_NAME = '你的表名' AND COLLATION_NAME IS NOT NULL;

把结果按COLLATION_NAME排一下,你大概率会看到同一张表里混着utf8mb4_unicode_ci和utf8mb4_0900_ai_ci。这就是 1267 的根源之一。接下来要定位存储过程里到底哪一句在比较。MySQL 没有直接告诉你行号,但你可以用SHOW CREATE PROCEDURE把定义拉出来,逐段看JOIN ... ON、WHERE ... =、IF 变量 = 字段这些位置:

SHOW CREATE PROCEDURE 你的库名.你的存储过程名;

如果存储过程里用了局部变量去和表字段比较,比如WHERE t.name = v_name,而v_name是DECLARE出来的变量,它的排序规则会继承会话。解决办法有两种:一是显式给变量或比较加COLLATE,二是统一库表排序规则。先给一个最小改动的写法:

-- 在比较处显式指定排序规则,强制统一 SELECT id, name FROM user_info WHERE name = v_name COLLATE utf8mb4_unicode_ci;

如果你希望从根上解决,可以在存储过程开头显式设置会话排序规则,让变量上下文和表字段对齐:

CREATE PROCEDURE your_proc() BEGIN -- 显式统一会话排序规则,避免继承默认的 0900_ai_ci SET collation_connection = 'utf8mb4_unicode_ci'; SET character_set_client = 'utf8mb4'; SET character_set_connection = 'utf8mb4'; -- 你的业务逻辑 SELECT id FROM user_info WHERE name = v_name; END;

在 DBeaver 里改存储过程时,建议直接右键存储过程 → 编辑 → 修改后执行CREATE OR REPLACE。如果你习惯用外部编辑器(比如 UltraEdit)改完再粘回来,一定要注意保存编码为 UTF-8,否则可能引入新的字符集问题。改完后重新CALL验证。

4. 验证请求与成功结果:重新执行存储过程并确认不再报 1267

改完定义后,不要急着下结论,按下面的顺序验证一遍。第一步,确认存储过程已经更新:

-- 确认存储过程定义里已经包含 COLLATE 或 SET collation_connection SHOW CREATE PROCEDURE 你的库名.你的存储过程名;

第二步,在 DBeaver 里新开一个 SQL 编辑器(不要复用之前报错的会话,避免旧会话参数残留),先显式设置会话排序规则,再调用:

SET collation_connection = 'utf8mb4_unicode_ci'; SET character_set_client = 'utf8mb4'; SET character_set_connection = 'utf8mb4'; CALL 你的库名.你的存储过程名(参数1, 参数2);

如果这次没有报 1267,并且返回了你预期的结果集,说明冲突点已经被消除。第三步,做一次「反向验证」:故意把会话排序规则设成utf8mb4_0900_ai_ci再调用一次,看看是否还会报错。如果仍然正常,说明你的存储过程内部已经做了显式统一,不再依赖外部会话;如果又报错,说明还有没覆盖到的比较点,回到第 3 节继续定位。

-- 反向验证:切换会话排序规则后再调用 SET collation_connection = 'utf8mb4_0900_ai_ci'; CALL 你的库名.你的存储过程名(参数1, 参数2);

实测下来,大部分 1267 都能通过「显式 COLLATE + 统一会话」解决。如果存储过程里涉及临时表,还要额外注意临时表的排序规则。临时表默认继承会话,如果它和主表 JOIN,同样会触发 1267。这时候建临时表时要显式指定:

CREATE TEMPORARY TABLE tmp_result ( id BIGINT, name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

验证通过后,建议把这次用到的查询语句存成一个 DBeaver 脚本,下次再遇到类似报错可以直接跑。排查这类问题的关键不是记住所有排序规则名称,而是养成「先查会话、再查库表、最后定位比较点」的习惯。

5. 本篇常见错排查:401、local proxy failed、reading choices 与 OAuth 报错对照

在把 TaoToken 接入排查流程时,有几个报错比较常见,这里集中对照一下。第一个是401 Unauthorized,通常出现在你调用 API 时 Key 没带对或者已经失效。检查方式:确认请求头里的Authorization: Bearer <你的Key>格式正确,Key 没有多余空格,并且是在 API Keys 页面新生成的。如果用的是 Claude Code 接入,Base URL 要填https://taotoken.net/api,Model ID 按文档里的名称填,三件套(Base URL + Key + Model ID)缺一不可。

第二个是local proxy failed,这个一般出现在本地网络环境或代理配置异常时。先确认你的请求地址是https://taotoken.net/api,没有多写路径或端口;再检查本地是否有其他工具占用了相同端口。这个报错和数据库本身无关,属于接入层问题。

第三个是reading choices相关报错,通常出现在流式响应解析时。如果你用脚本调用,确认响应体是标准的 JSON 结构,choices字段存在且非空。如果模型返回被截断,也会出现类似提示,可以适当调大max_tokens。

第四个是 OAuth 相关报错,多出现在 Claude Code 或类似工具的授权流程里。确认你使用的是 API Key 方式而不是 OAuth 回调方式,接入文档里对两种方式有区分。如果工具强制走 OAuth,检查回调地址是否和文档一致。

回到数据库本身,1267 的排查还有一个容易忽略的点:information_schema查询出来的排序规则,和存储过程实际执行时的上下文可能不一致。因为information_schema本身也有排序规则,查询时如果涉及字符串比较,也可能报 1267。这时候给查询加COLLATE即可:

SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '你的库名' AND TABLE_NAME = 'user_info' COLLATE utf8mb4_general_ci;

把接入层报错和数据库报错分开看,排查效率会高很多。接入层的问题查 Key、Base URL、Model ID;数据库的问题查会话、库表、比较点。

6. 把排查流程固化下来:从一次 1267 到可复用的检查清单

这次 1267 排查下来,最大的收获不是记住了utf8mb4_unicode_ci和utf8mb4_0900_ai_ci的区别,而是形成了一套可复用的检查顺序。第一步永远是查会话:SHOW VARIABLES LIKE 'collation%',确认collation_connection是什么。第二步查库表:用information_schema.SCHEMATA、TABLES、COLUMNS三张表把排序规则分布拉出来。第三步定位比较点:SHOW CREATE PROCEDURE看JOIN、WHERE、IF里的字符串比较。第四步改定义:要么加COLLATE,要么在过程开头SET collation_connection。第五步验证:新开会话CALL,再做反向验证。

如果你经常要写存储过程,建议在建库建表阶段就统一排序规则。MySQL 8.0 新建库默认是utf8mb4_0900_ai_ci,如果你有历史表是utf8mb4_unicode_ci,迁移时要么统一改表,要么在存储过程里显式声明。统一之后,1267 基本不会再出现。

对于需要长期做数据库开发、经常和报错打交道的场景,可以把 TaoToken 的 Coding Plan 作为日常辅助:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。遇到不认识的报错,先用模型对话把排查方向列出来,再回到 DBeaver 里执行验证。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,API Key 在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。把工具用在「理思路」和「生成查询」上,把数据库操作留在本地环境,这样既高效又可控。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询