【问题标题】:Curious behaviour of MySQL when updating records with help of a temp table借助临时表更新记录时 MySQL 的奇怪行为
【发布时间】:2020-03-22 20:20:43
【问题描述】:

假设您有两张桌子。

数据由外部进程填充到其中一个表中(此处称为“事件”),并通过调用存储过程进行处理,该存储过程采用一些参数并尝试在其中查找记录表“事件”。如果找到它,它会在第二个表“action”中创建一条记录,并在“event”中标记匹配的记录,以防止在程序再次运行时再次处理它们。

在正常情况下,我会在表“事件”上使用“更新游标”,并将该字段设置为在游标循环中处理。但似乎 MySQL 不支持这一点。所以我寻找了另一种方法。我只是将表“事件”的匹配记录的 ID 存储在临时表中,然后尝试使用存储在临时表中的 ID 的约束来更新表“事件”。

该过程将由一个 shell 脚本调用,该脚本将一个变量设置为今天的日期并将其传递给该过程。 我期望的是只有临时表中带有 ID 的表“事件”的记录被更新。但是发生的情况是:表“event”的所有记录,字段为 event.submitted of today。

有人能解释一下吗?我找不到我的谬误...

您可以自己检查行为。

这会生成表格(更改数据库名称以匹配您的):

use yourdatabasename;
drop table if exists event;
create table if not exists event
(
    id integer not null AUTO_INCREMENT,
    sender varchar(127) not null,
    name varchar(127) not null,
    submitted Timestamp DEFAULT CURRENT_TIMESTAMP,
    status varchar(127) not null,
    logged Timestamp DEFAULT CURRENT_TIMESTAMP,
    ub_status varchar(127) not null,
    ub_status_date Timestamp DEFAULT CURRENT_TIMESTAMP,
    primary key (id)
);

-- create some entries in "event" with decending timestamps
insert into event (sender, name, submitted, status, logged,ub_status) values ('192.168.0.2', 'name1', now(), 'COMPLETED_SUCCESS',now(),'CREATED') ;
insert into event (sender, name, submitted, status, logged,ub_status) values ('192.168.0.2', 'name2', now() - interval 1 hour, 'COMPLETED_SUCCESS',now(),'CREATED') ;
insert into event (sender, name, submitted, status, logged,ub_status) values ('192.168.0.2', 'name3', now() - interval 2 hour, 'COMPLETED_SUCCESS',now(),'CREATED') ;
insert into event (sender, name, submitted, status, logged,ub_status) values ('192.168.0.2', 'name4', now() - interval 3 hour, 'COMPLETED_SUCCESS',now(),'CREATED') ;
insert into event (sender, name, submitted, status, logged,ub_status) values ('192.168.0.2', 'name5', now() - interval 4 hour, 'COMPLETED_SUCCESS',now(),'CREATED') ;
insert into event (sender, name, submitted, status, logged,ub_status) values ('192.168.0.2', 'name6', now() - interval 5 hour, 'COMPLETED_SUCCESS',now(),'CREATED') ;
insert into event (sender, name, submitted, status, logged,ub_status) values ('192.168.0.2', 'name7', now() - interval 6 hour, 'COMPLETED_SUCCESS',now(),'CREATED') ;
insert into event (sender, name, submitted, status, logged,ub_status) values ('192.168.0.2', 'name8', now() - interval 7 hour, 'COMPLETED_SUCCESS',now(),'CREATED') ;
insert into event (sender, name, submitted, status, logged,ub_status) values ('192.168.0.2', 'name9', now() - interval 8 hour, 'COMPLETED_SUCCESS',now(),'CREATED') ;


drop table if exists action;
create table if not exists action
(
    id integer not null AUTO_INCREMENT,
    name varchar(127) not null,
    sender varchar(127) not null,
    logged Timestamp not null DEFAULT CURRENT_TIMESTAMP,
    status varchar(127) not null,
    status_date Timestamp DEFAULT CURRENT_TIMESTAMP,
    primary key (id)
);

这是存储过程:

CREATE DEFINER=`flo_db`@`localhost` PROCEDURE `TestUpdateActionTwoJobs`(IN process_name varchar(40), 
                                                                IN sendingMachine varchar(127),
                                                                IN jobname1 varchar(127), 
                                                                IN jobname2 varchar(127), 
                                                                IN startdate varchar(10), 
                                                                IN update_event_table int)
begin
    DECLARE found_count long;
    DECLARE exit handler for sqlexception
    BEGIN
        GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, 
            @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
        SET @full_error = CONCAT("ERROR ", @errno, " (", @sqlstate, "): ", @text);
        SELECT @full_error;
        ROLLBACK;
    END;
    DECLARE exit handler for sqlwarning
    BEGIN
        GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, 
            @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
        SET @full_error = CONCAT("ERROR ", @errno, " (", @sqlstate, "): ", @text);
        SELECT @full_error;
        ROLLBACK;
    END;

   /* The procedure searches for certain records in table "event" - some fields have to match, some come
    * from stored procedure parameters.
    * If there is a match a new record is to be created in table "action" and the found records 
    * in table "event" are to marked as "processed" by setting the column "event.ub_status" to "PROCESSED"
    */
   SET @found_count = 0;
   SET @found_tmp = 0;
   START TRANSACTION;
       select count(*) from event  where
       sender = sendingMachine 
       and status = 'COMPLETED_SUCCESS'
       and submitted > startdate
       and (name = jobname1 or name = jobname2)
       and ub_status = 'CREATED' into @found_count ;
       /* we expect exactly 2 */
       IF @found_count = 2 THEN 
         CREATE TEMPORARY TABLE IF NOT EXISTS tmp_UpdateActionTwoJobs
            select id from event  where
                sender = sendingMachine 
                and status = 'COMPLETED_SUCCESS'
                and submitted > startdate
                and (name = jobname1 or name = jobname2)
                and ub_status = 'CREATED';

         INSERT INTO action (name, sender, logged, status, status_date) VALUES (process_name, sendingMachine, 
                       NOW(), 'CREATED', NOW());
         /* count the number of records in the temporary table */
         select count(*) from tmp_UpdateActionTwoJobs into @found_tmp;
         SET @info = CONCAT("number of records in tmp-table: ", @found_tmp);
         /* mark the records as processed if wanted */
         IF update_event_table = 1 THEN
            UPDATE event SET ub_status = 'PROCESSED', ub_status_date = NOW() WHERE id in (select id tmp_UpdateActionTwoJobs );
         END IF;
       else
         set @info = "no condition met!";
       END IF;  
   COMMIT;
   /* generate info output */
   SELECT @info;
end

要调用该过程,您可以使用这样的 bash 脚本(用户、密码、主机和数据库名称必须替换...):

#!/bin/bash
# we are looking for entries in "event" that were created today
today=$(date '+%Y-%m-%d')
mysql -u database_user -p'password' -h host -e "call TestUpdateActionTwoJobs('TargetJobname', '192.168.0.2', 'name2', 'name3', '$today', 1);" database_name

非常感谢!

【问题讨论】:

    标签: mysql stored-procedures


    【解决方案1】:

    我重写了您使用游标的过程,因此不需要临时表。 您的查询有效,但我的安全设置不允许这样做,因为

    错误 1175 (HY000):您正在使用安全更新模式,并且您尝试更新没有使用 KEY 列的 WHERE 的表。

    但是我用过

    select id FROM tmp_UpdateActionTwoJobs ;
    

    而不是你写的。

    所以试试我的存储过程

    USE `testdb`;
    DROP procedure IF EXISTS `TestUpdateActionTwoJobs`;
    
    DELIMITER $$
    USE `testdb`$$
    CREATE DEFINER=`root`@`localhost` PROCEDURE `TestUpdateActionTwoJobs`(IN process_name varchar(40), 
                                                                    IN sendingMachine varchar(127),
                                                                    IN jobname1 varchar(127), 
                                                                    IN jobname2 varchar(127), 
                                                                    IN startdate varchar(20), 
                                                                    IN update_event_table int)
    begin
        DECLARE found_count long;
        DECLARE _id long;
        DECLARE finished INTEGER DEFAULT 0;
        DEClARE curid 
            CURSOR FOR 
                select id from event  where
                    sender = sendingMachine 
                    and status = 'COMPLETED_SUCCESS'
                    and submitted > startdate
                    and (name = jobname1 or name = jobname2)
                    and ub_status = 'CREATED';
    
        -- declare NOT FOUND handler
        DECLARE CONTINUE HANDLER 
            FOR NOT FOUND SET finished = 1;    
        DECLARE exit handler for sqlexception
        BEGIN
            GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, 
                @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
            SET @full_error = CONCAT("ERROR ", @errno, " (", @sqlstate, "): ", @text);
            SELECT @full_error;
            ROLLBACK;
        END;
        DECLARE exit handler for sqlwarning
        BEGIN
            GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, 
                @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
            SET @full_error = CONCAT("ERROR ", @errno, " (", @sqlstate, "): ", @text);
            SELECT @full_error;
            ROLLBACK;
        END;
    
       /* The procedure searches for certain records in table "event" - some fields have to match, some come
        * from stored procedure parameters.
        * If there is a match a new record is to be created in table "action" and the found records 
        * in table "event" are to marked as "processed" by setting the column "event.ub_status" to "PROCESSED"
        */
       SET @found_count = 0;
       SET @found_tmp = 0;
       START TRANSACTION;
    
           select count(*) from event  where
           sender = sendingMachine 
           and status = 'COMPLETED_SUCCESS'
           and submitted > startdate
           and (name = jobname1 or name = jobname2)
           and ub_status = 'CREATED' into @found_count ;
    
            SET @found_tmp = 0;
            IF @found_count = 2 THEN
                INSERT INTO action (name, sender, logged, status, status_date) VALUES (process_name, sendingMachine, 
                   NOW(), 'CREATED', NOW());
                OPEN curid;
                    getid: LOOP
                        FETCH curid INTO _id;
                        IF finished = 1 THEN 
                            LEAVE getid;
                        END IF;
                        UPDATE event SET ub_status = 'PROCESSED', ub_status_date = NOW() WHERE id = _id;
                        SET @found_tmp = @found_tmp +1;
                    END LOOP getid;
                CLOSE curid;
                SET @info = CONCAT("number of records in processed: ", @found_tmp);
           ELSE
             set @info = "no condition met!";            
                /* we expect exactly 2 */
            END IF;
    
       COMMIT;
       /* generate info output */
       SELECT @info;
    end$$
    
    DELIMITER ;
    

    【讨论】:

    • 哦,我真笨!你在几个方面是对的......首先:我的建议包含错误的 SQL,而 MySQL 不会抱怨。当然,执行更新的语句中缺少关键字“FROM”。如果我插入它,那么一切都会按预期工作。第二:游标的使用要好得多。谢谢你指路。 MySQL 为何处理错误的 SQL 仍然是个谜……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-15
    • 1970-01-01
    • 1970-01-01
    • 2020-01-30
    • 1970-01-01
    • 2012-07-14
    相关资源
    最近更新 更多