使用准备和执行

时间:2015-03-04 18:50:45

标签: mysql stored-procedures cursor into-outfile

我有一张有训练记录的桌子。每条记录都有一个具有代理值的字段。我有另一张只是代理价值的表格。我想将记录导出到每个代理商的CSV文件中。有超过200万条记录,所以我不想导出整个表并手动完成。

我创建了一个存储过程,该过程使用游标从agency_codes表中提取值,并在 WHERE 子句的select语句中使用该值,并在 INTO中使用该值OUTFILE 名称。

该过程有效,但仅适用于agency_codes表中的前两个值。在第三个(ACB)值上,它给出错误未知列' ACB'在' where子句' 我很困惑为什么它与前两个值一起工作然后在第三个值停止。

以下是程序:

DELIMITER $$
DROP PROCEDURE IF EXISTS  export_csv $$
CREATE PROCEDURE export_csv()
BEGIN
DECLARE agency_name VARCHAR(255);

DECLARE exit_loop BOOLEAN;         

DECLARE agency_cursor CURSOR FOR
SELECT agency FROM agency_codes;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;

OPEN agency_cursor;

agency_loop: LOOP
FETCH  agency_cursor INTO agency_name;

SET @sql_text = Concat("(select 'class_code','course_code','course_name','course_type','username','grade_type','score','letter_grade','is_passed','completion_date','completion_status','registration_date','registration_entry_status','registration_type','comment','first_name','last_name','class_name','agency') Union (select class_code,course_code,course_name,course_type,username,grade_type,score,letter_grade,is_passed,completion_date,completion_status,registration_date,registration_entry_status,registration_type,comment,first_name,last_name,class_name,agency from hrdis_oru where hrdis_oru.agency =", agency_name," into outfile 'C:/HDD/",agency_name,".csv' fields enclosed by '\"' terminated by ',' escaped by '\"' lines terminated by '\r\n')");

prepare s1 from @sql_text;
execute s1;
deallocate prepare s1; 


IF exit_loop THEN
     CLOSE agency_cursor;
     LEAVE agency_loop;
 END IF;
END LOOP agency_loop;
END $$
DELIMITER ;

我的Agency表中的前几个值是:

  • 17
  • 303
  • ACB
  • 精算
  • AGR
  • AIM

任何帮助都会很棒。感谢。

1 个答案:

答案 0 :(得分:0)

如果你考虑@sql_text在每次迭代时会持有什么值,问题应该立即显而易见。为清晰起见,添加一些空格:

(
  select 'class_code','course_code','course_name','course_type','username',
         'grade_type','score','letter_grade','is_passed','completion_date',
         'completion_status','registration_date','registration_entry_status',
         'registration_type','comment','first_name','last_name','class_name',
         'agency'
) Union (
  select class_code,course_code,course_name,course_type,username,
         grade_type,score,letter_grade,is_passed,completion_date,
         completion_status,registration_date,registration_entry_status,
         registration_type,comment,first_name,last_name,class_name,
         agency
  from   hrdis_oru
  where  hrdis_oru.agency =ACB
  into   outfile 'C:/HDD/ACB.csv'
  fields enclosed   by '\"'
         terminated by ','
         escaped    by '\"'
  lines  terminated by '\r\n'
)

请特别注意where htdis_oru.agency =ACB

由于ACB尚未被引用,MySQL将其解析为模式对象标识符,并在无法找到任何此类命名对象时进行投诉(纯数字代理商名称不是这种情况,因为它们是解析为整数,随后获得cast to strings during expression evaluation)。

要让MySQL正确地将非数字值解析为字符串文字,必须引用它们:

... where hrdis_oru.agency ='", agency_name, "' ...
                            ^                 ^

当然,如果agency_name包含'字符串引号字符,则会出现问题 - 任何此类事件当然必须为escaped。 MySQL为此提供了QUOTE()功能:

... where hrdis_oru.agency =", QUOTE(agency_name), " ...

但是,为了防止可能的SQL注入攻击,你真的应该参数化你准备好的语句:

FETCH agency_cursor INTO @agency_name;

(您不再需要DECLARE agency_name);然后:

... where hrdis_oru.agency = ? into outfile CONCAT('C:/HDD/', ?, '.csv') ...

其次是:

PREPARE s1 FROM @sql_text;
EXECUTE s1 USING @agency_name, @agency_name;
DEALLOCATE PREPARE s1;

请注意,您现在还可以在进入循环之前PREPARE语句并在循环内简单地EXECUTE它(具有适当的值) - 这应该会产生轻微的性能改进。退出循环后请记得DEALLOCATE

最后一点:您应该在exit_loop命令之后立即检查FETCH < - >,否则最终会尝试执行SELECT ... INTO OUTFILE语句当没有更多的代理商时。


值得注意的是,在这种情况下,您根本不需要使用准备好的语句。您可以简单地执行以下操作:

CREATE PROCEDURE export_csv() BEGIN
  DECLARE agency_name VARCHAR(255);
  DECLARE agency_cursor CURSOR FOR SELECT agency FROM agency_codes;
  DECLARE EXIT HANDLER FOR NOT FOUND CLOSE agency_cursor;

  OPEN agency_cursor;
  LOOP
    FETCH agency_cursor INTO agency_name;

      SELECT 'class_code','course_code','course_name','course_type','username',
             'grade_type','score','letter_grade','is_passed','completion_date',
             'completion_status','registration_date','registration_entry_status',
             'registration_type','comment','first_name','last_name','class_name'

    UNION ALL

      SELECT class_code,course_code,course_name,course_type,username,
             grade_type,score,letter_grade,is_passed,completion_date,
             completion_status,registration_date,registration_entry_status,
             registration_type,comment,first_name,last_name,class_name
      FROM   hrdis_oru
      WHERE  agency = agency_name

    INTO   OUTFILE CONCAT('C:/HDD/', agency_name, '.csv')
    FIELDS ENCLOSED   BY '"'
           TERMINATED BY ','
           ESCAPED    BY '"'
    LINES  TERMINATED BY '\r\n';

  END LOOP;
END