ON DUPLICATE KEY UPDATE не работает, когда есть триггер UPDATE - PullRequest
6 голосов
/ 05 ноября 2010

У меня есть INSERT утверждение, которое выглядит так:

INSERT INTO officer (officer_number,
                     name,
                     bank_id)    
VALUES ('',
        '',
        8)

ON DUPLICATE KEY UPDATE officer_number = '',
                        name = '',
                        bank_id = 8,
                        id = LAST_INSERT_ID(id)

Этот способ работал просто отлично. Он перестал работать, когда я добавил следующий триггер:

CREATE TRIGGER officer_update BEFORE UPDATE ON `officer`
FOR EACH ROW SET NEW.updated_at = NOW(), NEW.created_at = OLD.created_at

Дело не в том, что запись officer не вставляется. Просто кажется, что триггер захватывает LAST_INSERT_ID() или что-то в этом роде. Я говорю это, потому что следующий запрос, который выполняется:

INSERT INTO account (import_id,
                     branch_id,
                     account_number,
                     officer_id,
                     customer_id,
                     open_date,
                     maturity_date,
                     interest_rate,
                     balance,
                     opening_balance)
VALUES ('123',
        '4567',
        '789',
        '0', # This is the officer id which is of course invalid
        '321',
        '1992-04-22',
        '2012-05-22',
        '0.0123',
        '0',
        '10000')

Поскольку я выполнил десятки успешных импортов с одним и тем же файлом, я не изменил свой код, и теперь мой импорт не работает после того, как я добавил этот триггер, я должен сделать вывод, что триггер является виновником. У меня была похожая ситуация с другой таблицей, и удаление триггера решило проблему.

Итак, мои вопросы:

  1. Может кто-нибудь объяснить, что, конкретно, вызывает мой офицер идентификатор, который будет установлен в 0?
  2. Что хорошего решение этой проблемы?

У меня есть еще один триггер на officer.created_at (и на многих других таблицах created_at с), и я бы предпочел избежать какого-то неловкого решения, когда у меня есть триггер на created_at, но DEFAULT CURRENT_TIMESTAMP на updated_at. По какой-то причине MySQL допускает только одну автоматическую отметку времени для каждой таблицы, поэтому я не могу сделать CURRENT_TIMESTAMP для created_at и updated_at.

Вот SHOW CREATE TABLE для officer:

CREATE TABLE `officer` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `officer_number` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `bank_id` bigint(20) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`),
  UNIQUE KEY `officer_number` (`officer_number`,`name`),
  UNIQUE KEY `officer_number_2` (`officer_number`,`bank_id`),
  KEY `bank_id` (`bank_id`),
  CONSTRAINT `officer_ibfk_1` FOREIGN KEY (`bank_id`) REFERENCES `bank` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=102735 DEFAULT CHARSET=latin1

Ответы [ 2 ]

4 голосов
/ 06 декабря 2010

Ваш INSERT ... ON DUPLICATE KEY UPDATE является способом предотвращения ошибки, если officer_number уже существует.Вам нужно, чтобы произошло обновление (для запуска TRIGGER), или вы могли бы вместо этого использовать INSERT IGNORE?:

INSERT IGNORE INTO officer (officer_number,
                     name,
                     bank_id)    
VALUES ('',
        '',
        8);

Это бы просто ничего не делало, если officer_id уже существует, таким образом устраняя необходимостьобновление (и, следовательно, LAST_INSERT_ID()) в целом.

Если это невозможно, возможно, ваш INSERT ... ON DUPLICATE KEY UPDATE может быть изменен.Мне неясно, для чего:

id = LAST_INSERT_ID(id)

LAST_INSERT_ID() (без каких-либо аргументов) возвращает первое автоматически сгенерированное значение, которое было установлено для столбца AUTO_INCREMENT самой последней выполненной инструкцией INSERT, чтобы повлиять натакой столбец.

Однако, если вы задаете аргумент, он возвращает значение этого аргумента, а следующий вызов LAST_INSERT_ID() (без каких-либо аргументов) возвращает то же значение.Например:

SELECT LAST_INSERT_ID(100);
+---------------------+
| LAST_INSERT_ID(100) |
+---------------------+
|                 100 |
+---------------------+

SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|              100 |
+------------------+

Итак, если мы предположим, что id == 100, то это должно быть верно:

SELECT LAST_INSERT_ID(id);
+--------------------+
| LAST_INSERT_ID(id) |
+--------------------+
|                100 |
+--------------------+

SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|              100 |
+------------------+

Исходя из этого:

id = LAST_INSERT_ID(id)

Должно быть таким же, как:

id = id

Или, как предположил Джош Дэвис, в этом не должно быть необходимости.Вы пробовали просто id = id?Что именно происходит, когда вы исключаете его?

В руководстве указано, что:

Однако, если вы смешаете ссылки на LAST_INSERT_ID () и LAST_INSERT_ID (expr),эффект не определен

и:

Сгенерированный идентификатор сохраняется на сервере для каждого соединения .Это означает, что значение, возвращаемое функцией данному клиенту, является первым значением AUTO_INCREMENT, сгенерированным для самого последнего оператора, влияющего на столбец AUTO_INCREMENT этим клиентом .На это значение не могут повлиять другие клиенты, даже если они генерируют собственные значения AUTO_INCREMENT.

Поскольку вы используете LAST_INSERT_ID() и LAST_INSERT_ID(expr), поведение не определено.Кроме того, TRIGGER может рассматриваться как одно соединение (оно запускается непосредственно на сервере), тогда как операторы INSERT и CREATE, возможно, вызываются из другого соединения.Учитывая это, а также различные изменения и ошибки, которые, как сообщалось, связаны с LAST_INSERT_ID между версиями, вполне вероятно, что будет проблем с вашим подходом.

Возвращаясь к тому, что сказал Джош Дэвис,Я был бы склонен разрешить использование id = LAST_INSERT_ID(id) в вашем выражении INSERT.Также было бы полезно узнать, как вы получаете officer_id в вашем INSERT INTO account утверждении, которое получает нулевое значение.

0 голосов
/ 16 ноября 2010

Я не понимаю, почему вы пытаетесь обновить id.Разве вы не можете просто удалить вызов LAST_INSERT_ID()?

INSERT INTO officer (officer_number,
                     name,
                     bank_id)
VALUES ('',
        '',
        8)

ON DUPLICATE KEY UPDATE officer_number = '',
                        name = '',
                        bank_id = 8

Кроме того, вы должны опубликовать свою версию MySQL (а также используемый движок), поскольку в поведение LAST_INSERT_ID() были внесены некоторые измененияпрошлое.

...