mysql创建存储过程declare 变量时报错

本文详细描述了MySQL数据库中delimiter的作用,包括如何使用delimiter解决存储过程中的问题,以及delimiter在不同客户端下的应用差异。通过实例展示了正确设置delimiter的重要性,并提供了在MySQL存储过程中遇到问题时的解决方案。

今天创建存储过程时,一直提示在declare这句话报错,检查了2 3个小时也木有个头绪,在大神的帮助下,知道了,原来是mysql ;(分号的问题)。delimiter //解决问题

以下的文章主要是向大家描述的是MySQL数据库中delimiter的作用是什么?我们一般都认为这个命令和存储过程关系不大,到底是不是这样的呢?以下的文章将会给你相关的知识,望你会有所收获。

其实就是告诉MySQL解释器,该段命令是否已经结束了,MySQL数据库是否可以执行了。默认情况下,delimiter是分号;。在命令行客户端中,如果有一行命令以分号结束,那么回车后,MySQL将会执行该命令。如输入下面的语句

 
  1. MySQL> select * from test_table; 

然后回车,那么MySQL将立即执行该语句。

但有时候,不希望MySQL这么做。在为可能输入较多的语句,且语句中包含有分号。如试图在命令行客户端中输入如下语句

 
  1. MySQL> CREATE FUNCTION `SHORTEN`(S VARCHAR(255), N INT)  
  2. MySQL> RETURNS varchar(255)  
  3. MySQL> BEGIN  
  4. MySQL> IF ISNULL(S) THEN  
  5. MySQL> RETURN '';  
  6. MySQL> ELSEIF N<15 THEN  
  7. MySQL> RETURN LEFT(S, N);  
  8. MySQL> ELSE  
  9. MySQL> IF CHAR_LENGTH(S) <=N THEN  
  10. MySQL> RETURN S;  
  11. MySQL> ELSE  
  12. MySQL> RETURN CONCAT(LEFT(S, N-10), '...', RIGHT(S, 5));  
  13. MySQL> END IF;  
  14. MySQL> END IF;  
  15. MySQL> END; 

默认情况下,不可能等到用户把这些语句全部输入完之后,再执行整段语句。因为MySQL一遇到分号,它就要自动执行。即,在语句RETURN '';时,MySQL数据库解释器就要执行了。这种情况下,就需要事先把delimiter换成其它符号,如//或$$。

 
  1. MySQL> delimiter //  
  2. MySQL> CREATE FUNCTION `SHORTEN`(S VARCHAR(255), N INT)  
  3. MySQL> RETURNS varchar(255)  
  4. MySQL> BEGIN  
  5. MySQL> IF ISNULL(S) THEN  
  6. MySQL> RETURN '';  
  7. MySQL> ELSEIF N<15 THEN  
  8. MySQL> RETURN LEFT(S, N);  
  9. MySQL> ELSE  
  10. MySQL> IF CHAR_LENGTH(S) <=N THEN  
  11. MySQL> RETURN S;  
  12. MySQL> ELSE  
  13. MySQL> RETURN CONCAT(LEFT(S, N-10), '...', RIGHT(S, 5));  
  14. MySQL> END IF;  
  15. MySQL> END IF;  
  16. MySQL> END;//  

这样只有当//出现之后,MySQL解释器才会执行这段语句

例子:

 
  1. MySQL> delimiter //   
  2. MySQL> CREATE PROCEDURE simpleproc (OUT param1 INT)   
  3. -> BEGIN   
  4. -> SELECT COUNT(*) INTO param1 FROM t;   
  5. -> END;   
  6. -> //   
  7. Query OK, 0 rows affected (0.00 sec)   
  8. MySQL> delimiter ;   
  9. MySQL> CALL simpleproc(@a);   
  10. Query OK, 0 rows affected (0.00 sec)   
  11. MySQL> SELECT @a;   
  12. +------+   
  13. | @a |   
  14. +------+   
  15. | 3 |   
  16. +------+   
  17. 1 row in set (0.00 sec)   

本文代码在 MySQL 5.0.41-community-nt 下运行通过。

编写了个统计网站访问情况(user agent)的 MySQL 数据库存储过程。就是下面的这段 SQL 代码。

 
  1. drop procedure if exists pr_stat_agent;  
  2. -- call pr_stat_agent ('2008-07-17', '2008-07-18')  
  3. create procedure pr_stat_agent  
  4. (  
  5. pi_date_from date  
  6. ,pi_date_to date  
  7. )  
  8. begin  
  9. -- check input  
  10. if (pi_date_from is null) then  
  11. set pi_date_from = current_date();  
  12. end if;  
  13. if (pi_date_to is null) then  
  14. set pi_date_to = pi_date_from;  
  15. end if;  
  16. set pi_date_to = date_add(pi_date_from, interval 1 day);  
  17. -- stat  
  18. select agent, count(*) as cnt  
  19. from apache_log  
  20. where request_time >= pi_date_from  
  21. and request_time < pi_date_to 
  22. group by agent  
  23. order by cnt desc;  
  24. end;  

我在 EMS SQL Manager 2005 for MySQL 这个 MySQL 图形客户端下可以顺利运行。但是在 SQLyog MySQL GUI v5.02 这个客户端就会出错。最后找到原因是没有设置好 delimiter 的问题。

默认情况下,delimiter “;” 用于向 MySQL 提交查询语句。在存储过程中每个 SQL 语句的结尾都有个 “;”,如果这时候,每逢 “;” 就向 MySQL 提交的话,当然会出问题了。于是更改 MySQL 的 delimiter,上面 MySQL 存储过程就编程这样子了:

delimiter //; -- 改变 MySQL delimiter 为:“//”

 
  1. drop procedure if exists pr_stat_agent //  
  2. -- call pr_stat_agent ('2008-07-17', '2008-07-18')  
  3. create procedure pr_stat_agent  
  4. (  
  5. pi_date_from date  
  6. ,pi_date_to date  
  7. )  
  8. begin  
  9. -- check input  
  10. if (pi_date_from is null) then  
  11. set pi_date_from = current_date();  
  12. end if;  
  13. if (pi_date_to is null) then  
  14. set pi_date_to = pi_date_from;  
  15. end if;  
  16. set pi_date_to = date_add(pi_date_from, interval 1 day);  
  17. -- stat  
  18. select agent, count(*) as cnt  
  19. from apache_log  
  20. where request_time >= pi_date_from  
  21. and request_time < pi_date_to 
  22. group by agent  
  23. order by cnt desc;  
  24. end; //  
  25. delimiter ;  

改回默认的 MySQL delimiter:“;”

当然,MySQL delimiter 符号是可以自由设定的,你可以用 “/” 或者“$$” 等。但是 MySQL数据库 存储过程中比较常见的用法是 “//” 和 “$$”。上面的这段在 SQLyog 中的代码搬到 MySQL 命令客户端(MySQL Command Line Client)却不能执行。

MySQL> delimiter //; -- 改变 MySQL delimiter 为:“//”

 
  1. MySQL> 
  2. MySQL> drop procedure if exists pr_stat_agent //  
  3. -> 
  4. -> -- call pr_stat_agent ('2008-07-17', '2008-07-18')  
  5. -> 
  6. -> create procedure pr_stat_agent  
  7. -> (  
  8. -> pi_date_from date  
  9. -> ,pi_date_to date  
  10. -> )  
  11. -> begin  
  12. -> -- check input  
  13. -> if (pi_date_from is null) then  
  14. -> set pi_date_from = current_date();  
  15. -> end if;  
  16. -> 
  17. -> if (pi_date_to is null) then  
  18. -> set pi_date_to = pi_date_from;  
  19. -> end if;  
  20. -> 
  21. -> set pi_date_to = date_add(pi_date_from, interval 1 day);  
  22. -> 
  23. -> -- stat  
  24. -> select agent, count(*) as cnt  
  25. -> from apache_log  
  26. -> where request_time >= pi_date_from  
  27. -> and request_time < pi_date_to 
  28. -> group by agent  
  29. -> order by cnt desc;  
  30. -> end; //  
  31. -> 
  32. -> delimiter ; 

改回默认的 MySQL delimiter:“;”

 
  1. -> //  
  2. -> //  
  3. -> //  
  4. -> ;  
  5. -> ;  
  6. -> 

真是奇怪了!最后终于发现问题了,在 MySQL 命令行下运行 “delimiter //; ” 则 MySQL 的 delimiter 实际上是 “//;”,而不是我们所预想的 “//”。其实只要运行指令 “delimiter //” 就 OK 了。

MySQL> delimiter // -- 末尾不要符号 “;”

 
  1. MySQL> 
  2. MySQL> drop procedure if exists pr_stat_agent //  
  3. Query OK, 0 rows affected (0.00 sec)  
  4. MySQL> 
  5. MySQL> -- call pr_stat_agent ('2008-07-17', '2008-07-18')  
  6. MySQL> 
  7. MySQL> create procedure pr_stat_agent  
  8. -> (  
  9. -> pi_date_from date  
  10. -> ,pi_date_to date  
  11. -> )  
  12. -> begin  
  13. -> -- check input  
  14. -> if (pi_date_from is null) then  
  15. -> set pi_date_from = current_date();  
  16. -> end if;  
  17. -> 
  18. -> if (pi_date_to is null) then  
  19. -> set pi_date_to = pi_date_from;  
  20. -> end if;  
  21. -> 
  22. -> set pi_date_to = date_add(pi_date_from, interval 1 day);  
  23. -> 
  24. -> -- stat  
  25. -> select agent, count(*) as cnt  
  26. -> from apache_log  
  27. -> where request_time >= pi_date_from  
  28. -> and request_time < pi_date_to 
  29. -> group by agent  
  30. -> order by cnt desc;  
  31. -> end; //  
  32. Query OK, 0 rows affected (0.00 sec)  
  33. MySQL> 
  34. MySQL> delimiter ;   

末尾不要符号 “//”

 
  1. MySQL> 

顺带一提的是,我们可以在 MySQL 数据库中执行在文件中的 SQL 代码。例如,我把上面存储过程的代码放在文件 d:\pr_stat_agent.sql 中。可以运行下面的代码建立存储过程。

 
  1. MySQL> source d:\pr_stat_agent.sql  
  2. Query OK, 0 rows affected (0.00 sec)  
  3. Query OK, 0 rows affected (0.00 sec)  

source 指令的缩写形式是:“\.”

 
  1. MySQL> \. d:\pr_stat_agent.sql  
  2. Query OK, 0 rows affected (0.00 sec)  
  3. Query OK, 0 rows affected (0.00 sec)  

最后,可见 MySQL数据库的客户端工具在有些地方是各自为政,各有各的一套。


<think>嗯,用户需要解决MySQL存储过程中字符集相关的报错问题。从用户提供的引用内容来看,这个问题主要涉及存储过程中文字符集不匹配导致的数据插入错误或索引失效。 用户可能正在编写包含中文数据的存储过程,遇到了类似"Error 1366"或"无法插入中文"的报错。这类问题通常有四个核心原因需要排查:存储过程本身的字符集声明、连接会话的字符集设置、数据库/表的字符集一致性,以及字符串字面量的隐式转换规则。 参考引用[2]提到的"MySQL存储过程字符集不同索引不生效"和"charset-literal charset-collation-coercibility",需要特别注意字符串字面量的字符集继承规则。根据MySQL官方文档,未显式声明的字符串字面量会继承连接字符集,如果与表字段字符集不一致可能导致索引失效或比较错误。 解决方案应该分层次处理: 首先检查存储过程创建的字符集设置,可以通过SHOW CREATE PROCEDURE确认。如果未指定,默认会使用数据库字符集,但用户可能需要在CREATE PROCEDURE显式声明CHARACTER SET utf8mb4。 其次要确保连接会话的字符集一致性,引用[1]提供的SHOW VARIABLES命令可以验证character_set_connection等关键变量。建议在存储过程开头用SET NAMES 'utf8mb4'统一会话设置。 对于表字段字符集,需要确认与连接设置匹配,特别是主表和临表。引用[3]提到default-character-set=utf8mb4的服务端配置很重要。 最容易被忽视的是字符串字面量的处理。根据用户引用[2]提到的"Character Set Repertoire"规则,在WHERE条件或变量赋值,应该用CONVERT()函数显式转换字符串,或者使用_N'中文'的写法强制指定字符集。 用户引用[4]的案例很有参考价值,说明存储过程插入中文失败往往源于字符集链断裂。建议用户在存储过程中增加字符集检查语句,类似DECLARE cur_charset VARCHAR(50) DEFAULT @@session.character_set_connection;这样的调试代码。 最后生成的三个相关问题都紧扣用户的使用场景:性能影响对应索引失效问题,迁移方案涉及多环境适配,调试技巧则是实际排错需要的。特别是第二个关于存储过程迁移的问题,考虑到用户可能需要在开发/生产环境同步配置,这点很实用。</think>针对MySQL存储过程中字符集相关的报错问题,以下是系统化的解决方案: --- ### **一、核心原因分析** 1. **字符集链断裂** MySQL处理数据需经过`连接层 → 数据库层 → 表层 → 字段层`的字符集转换,任一环节不匹配会导致乱码或报错。 2. **存储过程特殊机制** - 存储过程内字符串字面量**默认继承`character_set_connection`编码** - 若与表字段字符集不一致,可能引发`Error 1366`(非法字符)或索引失效 --- ### **二、解决方案步骤** #### **步骤1:统一字符集环境** ```sql -- 检查全局字符集(需管理员权限) SHOW GLOBAL VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%'; -- 修改存储过程使用的字符集(在创建指定) DELIMITER $$ CREATE PROCEDURE your_procedure() BEGIN -- 显式设置会话字符集 SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'; -- 业务逻辑代码 END $$ DELIMITER ; ``` #### **步骤2:修正字符串字面量** ```sql -- 方法1:强制转换字面量编码 SET @name = CONVERT('中文数据' USING utf8mb4); INSERT INTO your_table (text_column) VALUES (@name); -- 方法2:使用_N前缀(MySQL 8.0+) INSERT INTO your_table (text_column) VALUES (_utf8mb4 '中文数据'); ``` #### **步骤3:验证存储过程字符集** ```sql -- 查看已存在存储过程的字符集 SHOW CREATE PROCEDURE your_procedure_name; -- 重建存储过程(指定字符集) DROP PROCEDURE IF EXISTS your_procedure; CREATE PROCEDURE your_procedure() CHARACTER SET 'utf8mb4' -- 关键声明 BEGIN ... END ``` #### **步骤4:检查表字段一致性** ```sql -- 确认表字段字符集 SHOW FULL COLUMNS FROM your_table; -- 修改字段字符集(示例) ALTER TABLE your_table MODIFY text_column VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` --- ### **三、关键配置项** | 配置项 | 推荐值 | 检查命令 | |---------------------------|---------------------|------------------------------------------| | `character_set_client` | utf8mb4 | `SHOW VARIABLES LIKE 'character_set_%';` | | `character_set_connection` | utf8mb4 | | | `character_set_database` | utf8mb4 | | | `collation_connection` | utf8mb4_unicode_ci | | > **注意**:若使用临表,需显式指定字符集: > ```sql > CREATE TEMPORARY TABLE temp_table (...) > CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; > ``` --- ### **四、调试技巧** 1. **日志追踪** 在存储过程首部添加: ```sql DECLARE cur_charset VARCHAR(50); SET cur_charset = @@session.character_set_connection; SELECT CONCAT('当前字符集:', cur_charset) AS Debug; ``` 2. **逐段隔离** 注释代码块逐步定位报错位置 --- ### **五、典型错误案例** **错误:** `Incorrect string value: '\xE4\xB8\xAD' for column` **原因:** 客户端发送UTF-8数据,但`character_set_connection=latin1` **解决:** 在存储过程入口添加 `SET NAMES utf8mb4;` ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值