¿Se garantiza que la función LAST_INSERT_ID () de MySql es correcta?

36

Cuando hago una sola fila INSERTen una tabla que tiene una AUTO_INCREMENTcolumna, me gustaría usar la LAST_INSERT_ID()función para devolver el nuevo AUTO_INCREMENTvalor editado para esa fila.

Como muchos desarrolladores y administradores de Microsoft SQL Server, sin duda, saben que la funcionalidad equivalente en SQL Server ( SCOPE_IDENTITYy @@IDENTITY) no ha estado exenta de problemas .

Sé que el estado de los documentos MySQL:

La ID que se generó se mantiene en el servidor por conexión . Esto significa que el valor devuelto por la función a un cliente dado es el primer AUTO_INCREMENTvalor generado para la declaración más reciente que afecta a una AUTO_INCREMENTcolumna de ese cliente . Este valor no puede verse afectado por otros clientes, incluso si generan AUTO_INCREMENTsus propios valores. Este comportamiento garantiza que cada cliente pueda recuperar su propia ID sin preocuparse por la actividad de otros clientes y sin la necesidad de bloqueos o transacciones.

(fuente)

e incluso ir tan lejos como para decir:

Usar LAST_INSERT_ID()y AUTO_INCREMENTcolumnas simultáneamente de múltiples clientes es perfectamente válido.

(fuente)

¿Existen riesgos o escenarios conocidos que puedan hacer que LAST_INSERT_ID()no se devuelva el valor correcto?

Estoy usando MySQL 5.5 en CentOS 5.5 x64 y Fedora 16 x64 y el motor InnoDB.

Kev
fuente

Respuestas:

35

Un par de advertencias que me gustaría señalar al usar LAST_INSERT_ID:

  1. Sé que mencionaste inserciones de una sola fila. Pero al hacer inserciones de varias filas, LAST_INSERT_ID()devolverá el valor de la primera fila insertada (no la última).

  2. Si el inserto fallara, LAST_INSERT_ID()estaría indefinido. Lo mismo es cierto para las reversiones automáticas de transacciones (debido a errores).

  3. Si realiza una inserción en una transacción que tiene éxito y todavía emite una ROLLBACK, LAST_INSERT_ID()se dejaría como estaba antes de la reversión.

  4. Hay un par de advertencias al usar AUTO_INCREMENTy LAST_INSERT_IDen la replicación basada en instrucciones. El primer ser cuando se usa en un disparador o función. El segundo es el escenario menos común donde su columna auto_increment es parte de una clave primaria compuesta y no es la primera columna de la clave.

Derek Downey
fuente
si tiene "ON DUPLICATE KEY UPDATE" que también devuelve 0 si no se actualiza nada, si establece date_field = now () siempre volverá correctamente
max4ever
7

Para ampliar aún más el punto número 2 en la respuesta dada por DTest:

En las versiones de MySQL que he usado, es una buena idea restablecer explícitamente el valor de LAST_INSERT_ID antes de cada bloque de código donde planea realizar una inserción.

Esto se puede hacer así:

-- initialize the LAST_INSERT_ID to some flag value:
SELECT LAST_INSERT_ID( some_flag_init_value_of_your_choice );
-- perform the insert  
INSERT INTO ttt (ccc) VALUES (vvv);
-- retrieve the id of the inserted row:  
SELECT LAST_INSERT_ID();

Después de que se ejecute la serie anterior de declaraciones, sabrá si la inserción tuvo algún efecto al verificar si LAST_INSERT_ID todavía estaba configurado en "some_flag_init_value_of_your_choice" al final de la ejecución.

De lo contrario, puede terminar con la siguiente situación problemática:

INSERT INTO ttt ( ccc ) VALUES ( 'a' );    -- assume this succeeds.
SELECT LAST_INSERT_ID();                   -- this will return the unique id of the new row with value 'a'.
INSERT INTO ttt ( ccc ) VALUES ( 'b' );    -- assume this FAILS.
SELECT LAST_INSERT_ID();                   -- this will STILL RETURN the unique id of the row with 'a'.

Debido a que la segunda inserción ha fallado , es posible que haya esperado que la segunda llamada a LAST_INSERT_ID devuelva NULL o que produzca un conjunto de resultados vacío (cero filas). El hecho de que todavía devuelva un identificador entero válido puede inducirlo a error a pensar que el segundo inserto tuvo ÉXITO cuando no lo hizo.

Las cosas se ponen MÁS EXTRAÑAS cuando consideras que LAST_INSERT_ID continuará preservando y repitiendo la última identificación única exitosa, incluso si las siguientes declaraciones de inserción fallidas apuntan a tablas diferentes a la tabla que produjo la última identificación única exitosa. En otras palabras, inserta en la tabla TA y obtiene una identificación de 5, luego inserta en TB (pero falla), pero aún ve un 5. Basado en eso, cree que acaba de crear una nueva fila en TA con id de 5 y una nueva fila en TB con id de 5, mientras que en realidad no existe ninguna fila en TB con id 5, o sí existe tal fila, pero en realidad no tiene nada que ver con ningún código que simplemente corrió.

pesófago
fuente
2
En primer lugar, no debería haber utilizado la existencia de last_insert_id()para juzgar si una consulta había tenido éxito. Después de todo, es el último Id insertado, que contiene los valores que necesita cuando ya sabía que había tenido éxito.
Pacerier