2010-11-05 4 views
6

У меня есть INSERT заявление, которое выглядит следующим образом:дублированием KEY UPDATE не работает, когда есть UPDATE триггер

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 
+0

Не могли бы вы опубликовать результаты 'SHOW CREATE TABLE officer' и запрос, который работает некорректно? Ваше название говорит 'ON DUPLICATE KEY UPDATE', но такого сообщения в сообщении нет. – Quassnoi

+0

Woops. Хороший улов. Я обновил вопрос. –

+0

Можете ли вы вставить свой код, который вы используете, в * retireve * значение 'LAST_INSERT_ID()'? похоже, отлично работает на моей машине. MySql 5.1.53 –

ответ

4

Ваш INSERT ... ON DUPLICATE KEY UPDATE представляется способом предотвращения ошибки, если officer_number уже существует. Нужно ли вам обновление произойдет (чтобы запустить триггер), или вы могли бы использовать вместо 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? Что именно происходит, когда вы его исключаете?

В manual говорится, что:

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

и:

Идентификатор, который был сгенерирован, является , хранящимся на сервере на за подключение basis. Это означает, что значение, возвращаемое функцией, чтобы данного клиента первое значение 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

Вот что я сделал: я использовал 'INSERT IGNORE', но если бы я его оставил,' LAST_INSERT_ID() 'не получится. После моего «INSERT» я только что выбрал любую запись, соответствующую тому, что я только что вставил. Это может быть не самое сжатое решение в мире, но оно работает, и я устал от общения с ним. –

0

Я не понимаю, почему вы пытаетесь обновить 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() поведения в прошлом.

+0

Он не работает без 'LAST_INSERT_ID()'. Я использую MySQL 5.1.49 и InnoDB. –

Смежные вопросы