Обновление MySQL обновляет больше строк, чем должно быть - PullRequest
0 голосов
/ 17 апреля 2019

У меня возникают проблемы, когда оператор обновления должен (насколько мне известно) обновить 5 строк (я выбрал 5 строк во временную таблицу и использовал INNER JOIN в операторе обновления)

однако при этомдоходит до выполнения оператора update, он обновляет все, что могло быть выделено во временную таблицу, а не только объединенное содержимое самой временной таблицы.

Я использую код 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;
//

Код выполнения

call `admin`.`func_fill_table`(5000);
call `data`.`func_get_data`(5);

1 Ответ

1 голос
/ 17 апреля 2019

Вы неправильно используете sql_select_limit -setting:

Максимальное количество строк, возвращаемых из операторов 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...