【问题标题】:Using Prepare and Execute使用准备和执行
【发布时间】:2015-03-04 18:50:44
【问题描述】:

我有一张包含培训记录的表格。每条记录都有一个带有代理值的字段。我有另一张只是代理价值的表格。我想将记录导出到每个机构的 CSV 文件中。有超过 200 万条记录,所以我不想导出整个表并手动执行。

我创建了一个存储过程,它使用游标从 Agency_codes 表中提取一个值,并在 WHERE 子句和部分 INTO OUTFILE 名字。

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

程序如下:

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
  • 精算师
  • 农业
  • 目标

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

【问题讨论】:

    标签: mysql stored-procedures cursor into-outfile


    【解决方案1】:

    如果您考虑一下@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

    最后一点:您应该在FETCH 命令之后检查exit_loop 立即 - 否则您最终将尝试在没有更多机构的最后一次执行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
    

    【讨论】:

    • 谢谢。这非常有效,为我节省了数小时的工作时间。我会尝试不使用准备好的语句。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-17
    • 1970-01-01
    • 2016-11-19
    • 1970-01-01
    相关资源
    最近更新 更多