MySQL存储过程在动态sql内获取返回值

MySQL动态SQL与存储过程
本文介绍MySQL中如何通过存储过程实现动态SQL的构建与执行,包括查询总记录数、分页查询等实用技巧,并提供了具体的示例代码。

MySQL存储过程在动态sql内获取返回值  

2012-05-04 18:21:32|  分类: MySQL |  标签:mysql  procedure  |字号 订阅

不需要使用游标循环来读取,直接select * into @变量就OK了,还可以传递参数来执行。 
1MySql通用分页存储过程
2
3过程参数
4p_cloumns varchar(500),p_tables varchar(100),p_where varchar(4000),p_order varchar(100),p_pageindex int,p_pagesize int,out p_recordcount int,out p_pagecount int
5
6$$:begin
7 declare v_sqlcounts varchar(4000);
8 declare v_sqlselect varchar(4000);
9 #拼接查询总记录的SQL语句
10 set v_sqlcounts = concat('select count(*) into @recordcount from ',p_tables,p_where);
11 #select v_sqlcounts;leave $$;
12 set @sqlcounts = v_sqlcounts;
13 prepare stmt from @sqlcounts;
14 execute stmt;
15 deallocate prepare stmt;
16 #获取动态SQL语句返回值
17 set p_recordcount = @recordcount;
18 #根据总记录跳数计算出总页数
19 set p_pagecount = ceiling((p_recordcount+0.0)/p_pagesize);
20 if p_pageindex <1 then
21 set p_pageindex = 1;
22 elseif p_pageindex > p_pagecount and p_pagecount <> 0 then
23 set p_pageindex = p_pagecount;
24 end if;
25 #拼接分页查询记录的动态SQL语句
26 set v_sqlselect = concat('select ',p_cloumns,' from ',p_tables,p_where,if(p_order is not null,p_order,''),' limit ',(p_pageindex-1)*p_pagesize,' , ',p_pagesize);
27 #select v_sqlselect;leave $$;
28 set @sqlselect = v_sqlselect;
29 prepare stmtselect from @sqlselect;
30 execute stmtselect;
31 deallocate prepare stmtselect;
32end $$

Sql代码
#拼接查询总记录的SQL语句
set v_sqlcounts = concat('select count(*) into @recordcount from ',v_tables,v_where);
set @sqlcounts := v_sqlcounts;
#预处理动态SQL
prepare stmt from @sqlcounts;
#传递动态SQL内参数
set @s1= categoryid;
execute stmt using @s1;
deallocate prepare stmt;
#获取动态SQL语句返回值
set recordcount = @recordcount;
#拼接查询总记录的SQL语句 set v_sqlcounts = concat('select count(*) into @recordcount from ',v_tables,v_where); set @sqlcounts := v_sqlcounts; #预处理动态SQL prepare stmt from @sqlcounts; #传递动态SQL内参数 set @s1= categoryid; execute stmt using @s1; deallocate prepare stmt; #获取动态SQL语句返回值 set recordcount = @recordcount;
引用:http://apps.hi.baidu.com/share/detail/5447167

例子:

传入表明和参数及列明拼成sql执行:

DELIMITER $$
CREATE PROCEDURE set_col_value
(in_table     VARCHAR(128),in_column    VARCHAR(128),in_new_value VARCHAR(1000),in_where  VARCHAR(4000))
begin

DECLARE l_sql VARCHAR(4000);
SET l_sql=CONCAT('',' UPDATE ',in_table,' SET ',in_column,'=',in_new_value,' WHERE ',in_where);
SET @sql=l_sql;
PREPARE s1 FROM @sql;
EXECUTE s1;
DEALLOCATE PREPARE s1;
end$$

DELIMITER ;

<think> 嗯,用户想了解MySQL存储过程动态SQL的使用方法。我先整合一下手头的资料:引用[1]提到用预处理语句处理动态SQL,核心步骤是PREPARE/EXECUTE/DEALLOCATE;引用[2]给出了具体案例,通过CONCAT拼接SQL语句并用参数传递排序规则;引用[3]和[4]则补充了从动态SQL获取返回值的方法,特别是SELECT...INTO和用户变量@var;引用[5]展示了动态SQL在数据迁移中的实际应用场景。 用户应该是开发者,需要解决实际业务中的动态查询需求。可能遇到的问题是:如何安全拼接SQL、防止注入、获取执行结果。需要重点强调三点:1) 预处理语句防注入机制 2) 游标遍历结果集的方法 3) 输出参数的设置技巧。 存储过程动态SQL难点在于语法细节容易出错。比如用户变量和局部变量的区别,游标必须在handler声明之前定义,CONCAT时单引号的转义处理等。准备用分页查询的案例说明,因为这个需求包含排序字段、方向、限制条数等多个动态元素,最能体现技术要点。 安全方面要提醒用户:动态参数绝不能直接拼接,必须用占位符。虽然示例使用了用户输入变量,但实际应用应该先校验输入值合法性。另外游标使用后要及时关闭,避免资源泄露。 最后考虑补充两个实用技巧:当动态SQL返回多行时,可以用临时表存储结果;调试时先把拼接好的SQL打印出来检查。用户可能还需要知道如何从Java程序调用这类存储过程,引用[3]提到Hibernate的调用方式可以简要带过。 </think> 在 MySQL 存储过程中使用动态 SQL 语句(即根据运行时条件构造 SQL 字符串并执行)主要通过 **预处理语句 (`PREPARE`、`EXECUTE`、`DEALLOCATE PREPARE`)** 来实现。以下是详细的使用方法和步骤: --- ### **核心步骤** 1. **声明变量存储SQL字符串**: 使用 `CONCAT()` 或字符串拼接构造动态 SQL 语句。 2. **准备预处理语句 (`PREPARE`)**: 将字符串编译为可执行的语句对象。 3. **执行预处理语句 (`EXECUTE`)**: 运行构造好的 SQL,可绑定参数(使用 `USING`)。 4. **释放预处理语句 (`DEALLOCATE PREPARE`)**: 清理资源,防止内存泄漏。 --- ### **基础示例 (无参数)** ```sql DELIMITER // CREATE PROCEDURE sp_dynamic_example() BEGIN -- 1. 声明变量存储动态SQL DECLARE v_sql VARCHAR(1000); SET v_sql = 'SELECT * FROM student WHERE age > 15'; -- 动态构造SQL -- 2. 准备语句 PREPARE stmt FROM v_sql; -- 3. 执行语句 EXECUTE stmt; -- 4. 释放语句 DEALLOCATE PREPARE stmt; END // DELIMITER ; ``` --- ### **带参数的动态SQL (使用 `USING`)** 当 SQL 中包含需要动态传入的**值**时: ```sql DELIMITER // CREATE PROCEDURE sp_dynamic_with_param(IN p_min_age INT) BEGIN DECLARE v_sql VARCHAR(1000); -- 使用 ? 作为占位符 SET v_sql = 'SELECT * FROM student WHERE age > ?'; PREPARE stmt FROM v_sql; EXECUTE stmt USING p_min_age; -- 将参数 p_min_age 绑定到第一个 ? DEALLOCATE PREPARE stmt; END // DELIMITER ; ``` --- ### **动态构造表名/列名/排序等 (使用 `CONCAT()`)** 当需要动态指定表名、列名或 `ORDER BY` 子句等**SQL语法元素**时: ```sql DELIMITER // CREATE PROCEDURE sp_dynamic_order(IN p_order_column VARCHAR(50), IN p_order_dir VARCHAR(4)) BEGIN DECLARE v_sql VARCHAR(1000); -- 拼接 SQL 字符串,特别注意反引号 ` 保护列名/表名 SET v_sql = CONCAT('SELECT id, name, age FROM student ORDER BY `', p_order_column, '` ', p_order_dir); PREPARE stmt FROM v_sql; EXECUTE stmt; -- 无值参数,直接执行 DEALLOCATE PREPARE stmt; END // DELIMITER ; -- 调用示例 CALL sp_dynamic_order('name', 'DESC'); -- 按 name 降序排列 [^2] ``` --- ### **获取动态SQL的执行结果** #### **1. 返回单行单列值 (`INTO`)** ```sql DELIMITER // CREATE PROCEDURE sp_get_count(OUT p_count INT) BEGIN DECLARE v_sql VARCHAR(1000); SET v_sql = 'SELECT COUNT(*) FROM student'; PREPARE stmt FROM v_sql; EXECUTE stmt INTO p_count; -- 将查询结果存入输出参数 p_count DEALLOCATE PREPARE stmt; END // DELIMITER ; ``` #### **2. 返回结果集 (使用游标)** ```sql DELIMITER // CREATE PROCEDURE sp_dynamic_cursor(IN p_min_age INT) BEGIN DECLARE v_done INT DEFAULT 0; DECLARE v_id INT; DECLARE v_name VARCHAR(50); DECLARE v_sql VARCHAR(1000); -- 1. 构造动态SQL SET v_sql = CONCAT('SELECT id, name FROM student WHERE age > ', p_min_age); -- 2. 准备并执行 PREPARE stmt FROM v_sql; EXECUTE stmt; -- 3. 获取结果集句柄 (隐式游标) -- 注意: MySQL 存储过程的游标必须基于已存在的 PREPARE 语句 DECLARE cur CURSOR FOR stmt; -- 4. 声明 NOT FOUND 处理器 DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = 1; OPEN cur; read_loop: LOOP FETCH cur INTO v_id, v_name; IF v_done THEN LEAVE read_loop; END IF; -- 处理每行数据 (例如: SELECT v_id, v_name; 或插入另一张表等) END LOOP; CLOSE cur; -- 5. 释放预处理语句 (游标关闭后) DEALLOCATE PREPARE stmt; END // DELIMITER ; ``` --- ### **关键注意事项** 1. **防 SQL 注入**: * 对动态拼接的**表名、列名等标识符**,确保其来源可信或严格校验(如白名单)。 * 对**数据值**,务必使用 `?` 占位符和 `USING` 传递参数,让 MySQL 驱动处理转义。 2. **变量作用域**: 预处理语句 (`stmt`) 是会话级对象,务必在存储过程结束前 `DEALLOCATE PREPARE`。 3. **错误处理**: 在 `DECLARE ... HANDLER` 中添加对 `SQLWARNING` 和 `SQLEXCEPTION` 的处理。 4. **调试**: 可在执行前 `SELECT v_sql;` 输出构造的 SQL 字符串进行检查。 5. **性能**: 频繁构造动态 SQL 可能影响性能,需权衡灵活性代价。 --- ### **应用场景** 1. **动态分页查询**: 根据传入的页码、页大小、排序列动态构造 `LIMIT` 和 `ORDER BY` 子句 [^4]。 2. **通用报表查询**: 根据用户选择的筛选条件动态拼接 `WHERE` 子句。 3. **数据迁移/ETL**: 动态指定源表和目标表进行操作 [^5]。 4. **构建灵活的排序逻辑**: 如上所示按传入列名排序 [^2]。 --- ### **Java 程序调用(Hibernate 示例)** ```java String callSP = "{call sp_dynamic_order(?, ?)}"; // 存储过程名和参数占位符 Query query = session.createSQLQuery(callSP); query.setParameter(1, "age"); // 按 age 列排序 query.setParameter(2, "ASC"); // 升序 List<Object[]> results = query.list(); // 获取结果集 [^3] ``` 通过预处理语句和字符串拼接,MySQL 存储过程可以灵活应对各种需要运行时构造 SQL 的场景,关键在于正确处理参数绑定和防范注入风险 [^1][^2][^4][^5]。 --- ### **相关问题** 1. 如何在 MySQL 存储过程中安全地防止动态 SQL 注入攻击? 2. 动态 SQL 中使用游标遍历结果集时有哪些常见错误?如何避免? 3. 除了 `PREPARE/EXECUTE`,MySQL 还有哪些执行动态 SQL 的方法? 4. 如何优化频繁执行动态 SQL存储过程性能? 5. 在 Java (JDBC) 中调用返回结果集的动态 SQL 存储过程需要注意什么?[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值