MYSQL存储过程在第二次运行时失败

时间:2018-02-14 20:34:16

标签: mysql stored-procedures

我有一个存储过程来创建一个名册表。 Source是一个收集所需数据的视图。存储过程应准备要显示给团队的数据。对于特定团队成员或所有人的概述。当我第一次调用程序时,一切都很好,我按预期收到数据。但是在使用不同参数的第二次运行时,它会失败并出现错误' SQL错误[1042] [42S22] :( conn:218)未知列' d0294220.duties.TM 3'在'字段列表'查询是:调用somp_roster(' Ritchie')'

缺少的列取决于第一次运行中的参数。

该视图从4个表中收集数据:

CREATE VIEW somv_roster AS
SELECT bd.businessday AS Datum,
v.venue AS Dropzone,
d.duty AS Service,
c.nickname AS Staff
FROM som_roster r
INNER JOIN som_businessday bd ON bd.businessdayID = r.ID_businessday
INNER JOIN som_venue v ON v.venueID = bd.ID_venue
INNER JOIN som_duty d ON d.dutyID = r.ID_duty
INNER JOIN som_contact c ON c.contactID = r.ID_contact
ORDER BY bd.businessday, v.venue
WHERE bd.businessday >= CURDATE();

结果如下所示:

Datum       Dropzone    Service     Staff
2018-04-28  Illertissen TM 2        Manu
2018-04-28  Illertissen Packer      Martina B.
2018-04-28  Illertissen TM 1        Rued
2018-04-28  Illertissen TM 3        Hane
2018-04-29  Illertissen Manifest    Sissi
2018-04-29  Illertissen TM 2        Ritchie
2018-04-29  Illertissen Packer      Martina B.
2018-04-29  Illertissen TM 1        Rued
2018-04-29  Illertissen TM 3        Hane
2018-05-01  Illertissen TM 1        Ritchie
2018-05-01  Illertissen TM 3        Hane
2018-05-01  Illertissen TM 2        Purzl
2018-05-01  Illertissen Packer      Martina B.
2018-05-26  Illertissen TM 1        Rued
2018-05-26  Kempten     TM 1        Ritchie
2018-05-26  Kempten     TM 2        Hane
2018-05-26  Kempten     Manifest    Sissi

现在我要建立一个新的表格来表明:

Datum       Dropzone    Manifest    Packer      TM 1    TM 2    TM 3
2018-04-28  Illertissen [NULL]      Martina B.  Rued    Manu    Hane
2018-04-29  Illertissen Sissi       Martina B.  Rued    Ritchie Hane
2018-05-01  Illertissen [NULL]      Martina B.  Ritchie Purzl   Hane
2018-05-26  Illertissen [NULL]      [NULL]      Rued    [NULL]  [NULL]
2018-05-26  Kempten     Sissi       [NULL]      Ritchie Hane    [NULL]

或者对于特定的团队成员,它应该如下所示:

Datum       Dropzone    TM 2    TM 3
2018-04-28  Illertissen [NULL]  Hane
2018-04-29  Illertissen [NULL]  Hane
2018-05-01  Illertissen [NULL]  Hane
2018-05-26  Kempten     Hane    [NULL]

在存储过程中,我定义了一个游标并循环遍历该游标。在临时表中,我添加结果并在结尾显示表。

存储过程:

    /*
 * Create routine somp_roster()
 * Requires somp_roster_et() to expand the columns dynamically.
 * Requires somv_roster view to read the rosters.
 * 4Air 2018 (c)
 */

DROP PROCEDURE IF EXISTS somp_roster;

DELIMITER $$
CREATE PROCEDURE somp_roster(IN staff_name VARCHAR(25))
BEGIN
    /*
     *  variables have to be declared at the start of the procedure
     *  declare variables for the loops
     *  flag to be set at the end of the loop
     */
    DECLARE finishedloop INTEGER DEFAULT 0;

    -- flag to loop through the duty table to
    DECLARE temp_date DATE DEFAULT NULL;
    DECLARE temp_venue VARCHAR(25) DEFAULT '';
    DECLARE temp_duty VARCHAR(25) DEFAULT '';
    DECLARE temp_staff VARCHAR(25) DEFAULT '';

    -- this var holds the string for the alter table statement
    DECLARE alttable VARCHAR(250) DEFAULT '';

    -- the first cursor to prepare the temp table
    DECLARE roster_cursor CURSOR FOR
        SELECT * FROM somv_roster WHERE Datum >= CURDATE() AND Staff LIKE CONCAT('%',staff_name) ORDER BY Service;    

    -- to recognize the end of the loops. Set to 0 before starting the loop!
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET finishedloop = 1;

    -- cleanup first from a interrupted previous run
    DROP TEMPORARY TABLE IF EXISTS duties;

    -- now we need a temp table for the duties
    CREATE TEMPORARY TABLE duties (Datum DATE, Dropzone VARCHAR(25), PRIMARY KEY(Datum, Dropzone)) ENGINE=MEMORY DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;

    -- loop through the temp table
    SET finishedloop = 0;
    OPEN roster_cursor;
    SET @alttable = '';

    get_roster_loop: LOOP

        -- now read the duties and create the new rows in the temp rosters
        FETCH roster_cursor INTO temp_date, temp_venue, temp_duty, temp_staff;
        IF finishedloop = 1 THEN
            LEAVE get_roster_loop;
        END IF;

        /*
         * fist step - try to expand the table with needed column
         * the statement has to be this:
         * call somp_rosters_et('tablename','columnname','columndefs')
         * example:
         * CALL somp_rosters_et('duties','TM 1','VARCHAR(25)');
         */

        CALL somp_roster_et('duties',temp_duty,'VARCHAR(25)');

        /*
         * next prepare the statement to fill the data...
         * implies the test if the date and venue already exists
         * if so, then use UPDATE TABLE rather then INSERT TABLE
         */
        SET @alttable = CONCAT('INSERT INTO duties (`Datum`,`Dropzone`,`', temp_duty , '`) VALUES (''' , temp_date , ''',''' , temp_venue , ''',''' , temp_staff , ''') ON DUPLICATE KEY UPDATE `', temp_duty , '` = ''', temp_staff , ''';') ;
        PREPARE stmt FROM @alttable;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;


    END LOOP get_roster_loop;

    CLOSE roster_cursor;

    SELECT * FROM duties ORDER BY Datum, Dropzone;

    DROP TEMPORARY TABLE IF EXISTS duties;

END $$

DELIMITER ;

将所需列添加到临时表的第二个存储过程: 当我尝试添加现有列时,我构建它以获得sql异常的第二个处理程序。

   /*
 * Stored Procedure to expand the table and do not care about an error if the column exists.
 * Used by somp_roster();
 * 4Air 2018 (c).
 */

DROP PROCEDURE IF EXISTS somp_roster_et;

DELIMITER $$

CREATE PROCEDURE somp_roster_et(IN t_name VARCHAR(25), IN c_name VARCHAR(25), IN c_attribute VARCHAR(25))
BEGIN
    DECLARE eflag INTEGER DEFAULT 0; 
    DECLARE modtable VARCHAR(150) DEFAULT "";
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET eflag = 1;
    SET @modtable = CONCAT('ALTER TABLE ' , t_name , ' ADD COLUMN `' , c_name , '` ' , c_attribute , ';');
    -- INSERT INTO som_log (Entry) VALUES(CONCAT('somp_roster_et: ',@modtable));
    PREPARE stmt FROM @modtable;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
END $$

DELIMITER ;

这适用于第一次运行。在第二次运行时,使用不同的团队成员作为第一个过程的参数,过程结束时的SELECT语句将引发错误:

SQL错误[1054] [42S22] :( conn:218)未知栏' d0294220.duties.TM 3'在'字段列表'查询是:调用somp_roster(' Ritchie')

当我从过程中删除最后一个SELECT并删除drop table命令时,我可以手动接收数据:

call somp_roster('Ritchie');
SELECT * FROM duties;
call somp_roster('Martina B.');
SELECT * FROM duties;

这很好用。为什么在选择过程中的数据时会出现异常?

理查德

2018-DEC-04

仍在与此作斗争...我已经改变了程序,但奇怪的行为仍然存在。

新程序:

/*
 * Create routine somp_roster()
 *
 * 2018-02-15 / implement a second handler for sql exceptions to handle the expand and select in this script
 * 
 * somp_roster_et is depricated with this change.
 * 
 * Requires somv_roster view to read the rosters.
 * 4Air 2018 (c)
 */

DROP PROCEDURE IF EXISTS somp_roster;

DELIMITER $$
CREATE PROCEDURE somp_roster(IN staff_name VARCHAR(25))
BEGIN
    /*
     *  variables have to be declared at the start of the procedure
     *  declare variables for the loops
     *  flag to be set at the end of the loop
     */
    DECLARE finishedloop,eflag, loopcount INTEGER DEFAULT 0;

    -- flag to loop through the duty table to
    DECLARE temp_date DATE DEFAULT NULL;
    DECLARE temp_venue, temp_duty, temp_staff VARCHAR(25) DEFAULT '';

    -- this var holds the string for the alter table statement
    DECLARE alttable,modtable VARCHAR(250) DEFAULT '';

    -- the first cursor to prepare the temp table
    DECLARE roster_cursor CURSOR FOR
        SELECT * FROM somv_roster WHERE date >= CURDATE() AND staff LIKE CONCAT('%',staff_name) ORDER BY service;    

    -- to recognize the end of the loops. Set to 0 before starting the loop!
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET finishedloop = 1;

    -- to recognize a sql exception and continue
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET eflag = 1;

    -- cleanup first from a interrupted previous run
    DROP TEMPORARY TABLE IF EXISTS duties;

    -- now we need a temp table for the duties
    CREATE TEMPORARY TABLE duties (Datum DATE, Dropzone VARCHAR(25), PRIMARY KEY(Datum, Dropzone)) ENGINE=MEMORY DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;

    -- loop through the temp table
    SET finishedloop = 0;
    OPEN roster_cursor;
      SET @alttable = '';

    get_roster_loop: LOOP

        -- now read the duties and create the new rows in the temp rosters
        FETCH roster_cursor INTO temp_date, temp_venue, temp_duty, temp_staff;
        IF finishedloop = 1 THEN
          LEAVE get_roster_loop;
        END IF;

        SET @modtable = CONCAT('ALTER TABLE duties ADD COLUMN `' , temp_duty , '` VARCHAR(25);');
        PREPARE stmt FROM @modtable;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;

        /*
         * next prepare the statement to fill the data...
         * implies the test if the date and venue already exists
         * if so, then use UPDATE TABLE rather then INSERT TABLE
         */
        SET @alttable = CONCAT('INSERT INTO duties (`Datum`,`Dropzone`,`', temp_duty , '`) VALUES (''' , temp_date , ''',''' , temp_venue , ''',''' , temp_staff , ''') ON DUPLICATE KEY UPDATE `', temp_duty , '` = ''', temp_staff , ''';') ;

        PREPARE stmt FROM @alttable;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;

    END LOOP get_roster_loop;
    CLOSE roster_cursor;
    SELECT * FROM duties ORDER BY Datum, Dropzone;
    DROP TEMPORARY TABLE IF EXISTS duties;

END $$

DELIMITER ;

现在我将临时表更改为持久的INNODB表,以查看该过程创建的内容,这始终是我期望的结果。现在我将最后的SELECT语句更改为:

SELECT COUNT(*) FROM duties;

我总是得到正确的计数。

任何想法是什么原因造成的?

理查德

0 个答案:

没有答案