2010-11-05 9 views
6

Tengo una declaración INSERT que tiene este aspecto:EN DUPLICADO KEY UPDATE no funciona cuando hay un desencadenador 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) 

Esta forma de hacer que ha estado trabajando muy bien. Que dejó de funcionar cuando he añadido el siguiente trigger:

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

No es que no está siendo insertado el registro officer. Simplemente parece que el detonador es secuestro LAST_INSERT_ID() o algo así. Digo esto porque la próxima consulta que se ejecuta es la siguiente:

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') 

Dado que me he encontrado decenas de importaciones de éxito con el mismo archivo exacto, no he cambiado mi código, y ahora mis importaciones no están funcionando después de agregar este disparador, debo deducir que el desencadenante es el culpable. Tuve una situación similar con otra mesa y al eliminar el disparador se solucionó el problema.

Así que mis preguntas son:

  1. Puede alguien explicar por qué, específicamente, está causando mi identificación oficial para ponerse a 0?
  2. ¿Qué es una buena solución a este problema?

tengo otro gatillo en officer.created_at (y un montón de otras mesas created_at s) y yo preferiría evitar algún tipo de solución incómoda en la que tengo un disparador en created_at sino un DEFAULT CURRENT_TIMESTAMP en updated_at. Por alguna razón, MySQL solo permite una marca de tiempo automática por tabla, por lo que no puedo hacer CURRENT_TIMESTAMP para ambos created_at y updated_at.

Aquí es el SHOW CREATE TABLE para 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

¿Podría publicar los resultados del comando 'SHOW CREATE TABLE' y la consulta que funciona incorrectamente? Su título dice 'ON DUPLICATE KEY UPDATE' pero no hay tal consulta en la publicación. – Quassnoi

+0

Woops. Buena atrapada. He actualizado la pregunta. –

+0

¿Puedes pegar el código que utilizas para * retireve * el valor de 'LAST_INSERT_ID()'? parece estar funcionando bien en mi máquina MySql 5.1.53 –

Respuesta

4

Su INSERT ... ON DUPLICATE KEY UPDATE parece ser una forma de evitar un error si officer_number ya existe. ¿Necesita la actualización a pasar (al activar el disparador), o podría utilizar en su lugar INSERT IGNORE:?

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

que simplemente no hacer nada si officer_id ya existe, eliminando así la necesidad de la actualización (y por lo tanto LAST_INSERT_ID()) en total.

Si eso no es posible, entonces quizás su INSERT ... ON DUPLICATE KEY UPDATE podría ser ajustado.No me queda claro en el propósito de:

id = LAST_INSERT_ID(id) 

LAST_INSERT_ID() (sin argumentos), devuelve el primer valor generado automáticamente que se fijó para una columna AUTO_INCREMENT por la sentencia INSERT ejecutada más recientemente a afectar a una columna de este tipo.

Sin embargo, si proporciona un argumento, devuelve el valor de ese argumento, y la siguiente llamada a LAST_INSERT_ID() (sin ningún argumento), devuelve el mismo valor. Por ejemplo:

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

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

Por lo tanto, si asumimos que id == 100, entonces esto debe ser cierto:

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

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

raíz de esta:

id = LAST_INSERT_ID(id) 

debe ser el mismo que:

id = id 

O, como lo sugirió Josh Davis, no debería ser necesario en absoluto. ¿Has probado simplemente id = id? ¿Qué sucede exactamente cuando lo excluyes?

Los manual establece que:

Sin embargo, si se mezclan referencias a LAST_INSERT_ID() y LAST_INSERT_ID (expr), el efecto es indefinido

y:

La ID que se generó es mantenida en el servidor en una por conexión base. Esto significa que el valor devuelto por la función de un cliente determinado es el primer valor AUTO_INCREMENT generado para declaración más reciente que afecta a una columna AUTO_INCREMENT por ese cliente. Este valor no puede verse afectado por otros clientes , incluso si generan valores AUTO_INCREMENT propios.

Como está utilizando tanto LAST_INSERT_ID() y LAST_INSERT_ID(expr), el comportamiento es indefinido. Además, el TRIGGER se puede considerar como una conexión (se ejecuta directamente en el servidor), mientras que las instrucciones INSERT y CREATE se pueden llamar desde una conexión diferente. Dado esto, y los diversos cambios y errores que se han reportado asociados con LAST_INSERT_ID entre versiones, es probable que haya problemas con su enfoque.

Volviendo a lo que dijo Josh Davis, me inclino a resolver el uso de id = LAST_INSERT_ID(id) en su instrucción INSERT. También sería útil saber cómo deriva el officer_id en su declaración INSERT INTO account, la que recibe un valor cero.

+0

Esto es lo que terminé haciendo: utilicé 'INSERT IGNORE', pero si lo dejaba así, el' LAST_INSERT_ID() 'no aparecería correctamente. Después de mi 'INSERT', seleccioné cualquier registro que coincida con lo que acabo de insertar. Puede que no sea la solución más sucinta del mundo, pero funciona y estoy cansado de jugar con eso. –

0

No entiendo por qué se intenta actualizar el id. ¿No puedes simplemente eliminar la llamada al LAST_INSERT_ID()?

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

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

Además, debe publicar su versión de MySQL (al igual que el motor utilizado), ya que no ha habido algunos cambios a LAST_INSERT_ID() comportamiento en el pasado.

+0

No funciona sin el 'LAST_INSERT_ID()'. Estoy usando MySQL 5.1.49 e InnoDB. –

Cuestiones relacionadas