MySQL更新更新了比应该更多的行

时间:2019-04-17 13:28:59

标签: mysql sql-update inner-join temp-tables

我遇到了一个更新语句应该(据我所知)更新5行的问题(我在临时表中选择了5行,并在更新语句中使用了INNER JOIN)

但是,在运行更新语句时,它会更新可能已选择到临时表中的所有内容,而不仅仅是临时表本身的联接内容。

我在选择语句中使用DT[which(rowSums(mapply(`%in%`, DT[, names(param), with = FALSE], param)) == length(param)), ] # a b c d #1: 1 3 5 1 #2: 3 3 7 3 代码来锁定行,(因为我希望一次将多个查询针对该表,(注意删除不会改变错误。效果)

我已经概括了整个代码库,并且仍然具有相同的效果,在过去的几天中,我一直在研究它,并且我确定这只是我必须做的愚蠢的事情

代码说明

FOR UPDATE 用于存储数据并显示已被外部程序使用的表。

TABLE `data`.`data_table 调试代码以填充上表。

Stored Procedure `admin`.`func_fill_table 旨在检索批量记录的实际代码,将其标记为已拾取,然后将其返回给外部应用程序。

基本设置代码

Stored Procedure `data`.`func_get_data

过程创建

DROP TABLE IF EXISTS `data`.`data_table`;
DROP PROCEDURE IF EXISTS `admin`.`func_fill_table`;
DROP PROCEDURE IF EXISTS `data`.`func_get_data`;
DROP SCHEMA IF EXISTS `data`;
DROP SCHEMA IF EXISTS `admin`;
CREATE SCHEMA `admin`;
CREATE SCHEMA `data`;

CREATE TABLE `data`.`data_table` (
  `identification_field_1` char(36) NOT NULL,
  `identification_field_2` char(36) NOT NULL,
  `identification_field_3` int(11) NOT NULL,
  `information_field_1` int(11) NOT NULL,
  `utc_action_time` datetime NOT NULL,
  `utc_actioned_time` datetime DEFAULT NULL,
  PRIMARY KEY (`identification_field_1`,`identification_field_2`,`identification_field_3`),
  KEY `NC_IDX_data_table_action_time` (`utc_action_time`)
);

运行代码

DELIMITER //

CREATE PROCEDURE `admin`.`func_fill_table`(
    IN records int
)
BEGIN
    IF records < 1
        THEN SET records = 50;
    END IF;

    SET @processed = 0;
    SET @action_time = NULL;

    WHILE @processed < records
    DO
        SET @action_time = DATE_ADD(now(), INTERVAL FLOOR(RAND()*(45)-10) MINUTE);#time shorter for temp testing
        SET @if_1 = UUID();
        SET @if_2 = UUID();
        INSERT INTO data.data_table(
            identification_field_1
            ,identification_field_2
            ,identification_field_3
            ,information_field_1
            ,utc_action_time
            ,utc_actioned_time)
        VALUES (
             @if_1
            ,@if_2
            ,FLOOR(RAND()*5000+1)
            ,FLOOR(RAND()*5000+1)
            ,@action_time
            ,NULL);

        SET @processed = @processed +1;
    END WHILE;
END
//
CREATE PROCEDURE `data`.`func_get_data`(
    IN batch int
)
BEGIN
    IF batch < 1
        THEN SET batch = 1; /*Minimum Batch Size of 1 */
    END IF;

    DROP TABLE IF EXISTS `data_set`;
    CREATE TEMPORARY TABLE `data_set` 
        SELECT 
            `identification_field_1` as `identification_field_1_local`
            ,`identification_field_2` as `identification_field_2_local`
            ,`identification_field_3` as `identification_field_3_local`
        FROM `data`.`data_table`
        LIMIT 0; /* Create a temp table using the same data format as the table but insert no data*/

    SET SESSION sql_select_limit = batch;

    INSERT INTO `data_set` (
        `identification_field_1_local`
        ,`identification_field_2_local` 
        ,`identification_field_3_local`)
    SELECT 
        `identification_field_1`
        ,`identification_field_2` 
        ,`identification_field_3` 
     FROM `data`.`data_table`
     WHERE
        `utc_actioned_time` IS NULL
            AND `utc_action_time` < NOW()
    FOR UPDATE; #Select out the rows to process (up to batch size (eg 5)) and lock those rows

    UPDATE 
       `data`.`data_table` `dt`
    INNER JOIN 
        `data_set` `ds`
        ON (`ds`.`identification_field_1_local` = `dt`.`identification_field_1`
            AND `ds`.`identification_field_2_local` = `dt`.`identification_field_2`
            AND `ds`.`identification_field_3_local` = `dt`. `identification_field_3`)
    SET `dt`.`utc_actioned_time` = NOW();
    # Update the table to say these rows are being processed

    select ROW_COUNT(),batch;
    #Debug output for rows altered (should be maxed by batch number)

    SELECT * FROM 
        `data`.`data_table` `dt`
    INNER JOIN 
        `data_set` `ds`
        ON (`ds`.`identification_field_1_local` = `dt`.`identification_field_1`
            AND `ds`.`identification_field_2_local` = `dt`.`identification_field_2`
            AND `ds`.`identification_field_3_local` = `dt`. `identification_field_3`);
    # Debug output of the rows that should have been modified

    SELECT 
       `identification_field_1_local`
        ,`identification_field_2_local` 
        ,`identification_field_3_local`
    FROM 
        `data_set`; /* Output data to external system*/

    /* Commit the in process field and allow other processes to access thoese rows again */
END;
//

1 个答案:

答案 0 :(得分:1)

您滥用sql_select_limit设置:

  

要从SELECT语句返回的最大行数。

它仅适用于select语句(以限制发送给客户端的结果 ),不适用于insert ... select ...。其目的是为了防止用户意外地被数以百万计的结果淹没,而不是其他limit功能。

虽然通常不能为limit使用变量,但是可以在存储过程中使用(对于MySQL 5.5 +):

  

LIMIT子句可用于约束SELECT语句返回的行数。 LIMIT接受一个或两个数字参数,它们都必须是非负整数常量,但以下情况除外:[...]

     
      
  • 在存储的程序中,可以使用整数值的例程参数或局部变量来指定LIMIT参数。
  •   

因此,您可以使用

...     
FROM `data`.`data_table`
WHERE `utc_actioned_time` IS NULL AND `utc_action_time` < NOW()
LIMIT batch
FOR UPDATE;