MySQL准备语句 - 如何循环

时间:2014-09-11 04:08:36

标签: mysql sql prepared-statement

我有一个填充了ID号的表,我需要循环遍历,并在预准备语句中用作变量。我不知道是否需要为此使用存储过程,或者如果常规查询可以执行此操作。这是一个简单的例子。

SELECT id from var_list;

loop through @ID = var_list.id ....


SET @s1 = "SELECT * FROM data WHERE id = @ID"; 
PREPARE stmt1 FROM @s1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;

正如评论中所提到的,我只需要这个来导出一些数据,我在表中有大约50-100个ID,并且编写了一个查询,一次一个地将文件导出到服务器

EDIT 我计划使用类似的东西将每次迭代的结果转储到文件中。

INTO OUTFILE '/tmp/orders.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'

2 个答案:

答案 0 :(得分:2)

正如其他人已经建议的那样,我们通常主要出于性能原因避免循环结果集 RBAR (通过痛苦排行)。我们不想养成循环结果集的习惯。但是,这并没有回答你提出的问题。

要回答您提出的问题,这里是MySQL存储程序的基本示例,该程序使用CURSOR单独处理查询返回的行。 MySQL不支持匿名块,所以唯一的方法是在MySQL存储程序中,如PROCEDURE

DELIMITER $$

CREATE PROCEDURE loop_through_var_list
BEGIN
   DECLARE done INT DEFAULT 0;
   DECLARE v_id INT DEFAULT NULL;  
   DECLARE csr_var_list CURSOR FOR SELECT id FROM var_list ORDER BY id;
   DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
   OPEN csr_var_list;
   get_id: LOOP
      FETCH csr_var_list INTO v_id; 
      IF done = 1 THEN
         LEAVE get_id;
      END IF;

      -- at this point, we have an id value in v_id, so we can do whatever
      SET @s1 = CONCAT('SELECT ... WHERE id =''', v_id, ''' ...');


   END LOOP get_id;
   CLOSE csr_var_list;
END$$

DELIMITER ;

执行程序:

CALL loop_through_var_list();

注意:在MySQL中处理CURSOR的语法与其他数据库有很大不同。

要获得"循环",我们需要使用LOOP ... END LOOP构造。

但为了防止该循环永远运行,我们需要一个允许我们退出循环的LEAVE语句。

我们使用条件测试来确定何时离开。在这个例子中,我们想在完成最后一行的处理后退出。

当没有更多行要提取时,FETCH将抛出异常。

我们"赶上" CONTINUE HANDLER中的异常(由于某些神秘的原因,"处理程序"必须声明最后的事情;如果我们尝试在HANDLER之后声明某些东西(除了另一个HANDLER之外),MySQL会抛出错误。

当MySQL抛出"没有更多行"异常,触发处理程序代码。在此示例中,我们只是将变量(名为done)设置为值。

因为它是"继续"处理程序,处理在抛出异常的语句处开始备份,在这种情况下,它将是FETCH之后的语句。所以,我们要做的第一件事就是检查我们是否已经完成了#34;或不。如果我们已经完成",那么我们退出循环,并关闭光标。

否则,我们知道我们从名为id的过程变量中存储了来自var_list的{​​{1}}值。所以现在我们可以做任何我们想做的事。看起来你想把一些SQL文本放到用户定义的变量中(包括在v_id的值中的SQL文本,然后是PREPARE,EXECUTE和DEALLOCATE PREPARE。

请务必使用适当的数据类型声明v_id变量,该数据类型与v_idid列的数据类型相匹配,我只是假设它是'是一个INT。

当我们到达循环结束时,MySQL"循环"回到循环的开头,然后我们再去。

在循环体中,您可能希望将CONCAT v_id转换为要执行的SQL文本。看起来你已经掌握了PREPARE,DEALLOCATE准备。对于测试,您可能希望在游标声明中的SELECT上添加LIMIT子句,然后执行简单的SELECT v_id;在添加更多代码之前,只是为了验证循环是否正常工作。


<强>后续

我想提到另一种替代方法,即基于模板运行一系列语句,替换单个SQL select语句提供的值...

例如,如果我有这个模板:

var_list

我需要用SELECT语句返回的列表中的特定id值替换@ID的出现,例如:

SELECT * 
  INTO OUTFILE '/tmp/orders_@ID.csv'
  FIELDS TERMINATED BY ',' ENCLOSED BY '"'
  LINES TERMINATED BY '\n'
FROM data 
WHERE id = @ID
ORDER BY 1 

我可能不会使用带有CURSOR循环的MySQL存储程序,我会使用不同的方法。

我使用SELECT语句生成一组可以执行的SQL语句。假设SELECT id FROM var_list WHERE id IS NOT NULL GROUP BY id 是整数类型,我可能会这样做:

id

对于从SELECT CONCAT(' SELECT * INTO OUTFILE ''/tmp/orders_',s.id,'.csv'' FIELDS TERMINATED BY '','' ENCLOSED BY ''"'' LINES TERMINATED BY ''\n'' FROM data WHERE id = ',s.id,' ORDER BY 1;') AS `stmt` FROM ( SELECT v.id FROM var_list v WHERE v.id IS NOT NULL GROUP BY v.id ) s ORDER BY s.id 返回的id的每个值,该语句返回可以(并且需要)执行的SQL SELECT语句的文本。将其捕获到文本文件中会给我一个我可以运行的SQL脚本。

答案 1 :(得分:1)

使用简单的查询,例如:

SELECT *
FROM data
WHERE id IN (
    SELECT id
    FROM var_list
)