A função LAST_INSERT_ID () do MySql está garantida como correta?

36

Quando faço uma única linha INSERTem uma tabela que possui uma AUTO_INCREMENTcoluna, gostaria de usar a LAST_INSERT_ID()função para retornar o novo AUTO_INCREMENTvalor armazenado para essa linha.

Como muitos desenvolvedores e administradores do Microsoft SQL Server, sem dúvida, sabem que a funcionalidade equivalente no SQL Server ( SCOPE_IDENTITYe @@IDENTITY) não ficou isenta de problemas .

Eu sei o estado dos documentos do MySQL:

O ID que foi gerado é mantido no servidor por conexão . Isso significa que o valor retornado pela função para um determinado cliente é o primeiro AUTO_INCREMENTvalor gerado para a instrução mais recente que afeta uma AUTO_INCREMENTcoluna por esse cliente . Este valor não pode ser afetado por outros clientes, mesmo que eles gerem AUTO_INCREMENTvalores próprios. Esse comportamento garante que cada cliente possa recuperar seu próprio ID sem se preocupar com a atividade de outros clientes e sem a necessidade de bloqueios ou transações.

(fonte)

e até chega a dizer:

Usar LAST_INSERT_ID()e AUTO_INCREMENTcolunas simultaneamente de vários clientes é perfeitamente válido.

(fonte)

Existem riscos ou cenários conhecidos que podem causar a LAST_INSERT_ID()não devolução do valor correto?

Estou usando o MySQL 5.5 no CentOS 5.5 x64 e Fedora 16 x64 e no mecanismo InnoDB.

Kev
fonte

Respostas:

35

Algumas advertências que eu gostaria de destacar ao usar LAST_INSERT_ID:

  1. Eu sei que você mencionou inserções de linha única. Mas ao fazer inserções de várias linhas, LAST_INSERT_ID()retornará o valor da primeira linha inserida (não a última).

  2. Se a inserção falhar, LAST_INSERT_ID()será indefinido. O mesmo vale para reversões automáticas de transações (devido a erros).

  3. Se você fizer uma inserção em uma transação bem-sucedida e ainda emitir um ROLLBACK, LAST_INSERT_ID()será deixado como antes da reversão.

  4. Existem algumas ressalvas ao usar AUTO_INCREMENTe LAST_INSERT_IDna replicação baseada em instruções. O primeiro é quando usado em um gatilho ou função. O segundo é o cenário menos comum em que sua coluna de incremento automático faz parte de uma chave primária composta e não é a primeira coluna na chave.

Derek Downey
fonte
se você tiver "ON DUPLICATE KEY UPDATE" que também retornará 0 se nada for atualizado, se você definir algum date_field = now (), ele sempre retornará corretamente
max4ever
7

Para expandir ainda mais o ponto 2 da resposta dada pelo DTest:

Nas versões do MySQL que eu usei, é uma boa idéia redefinir explicitamente o valor de LAST_INSERT_ID antes de cada bloco de código em que você planeja executar uma inserção.

Isso pode ser feito da seguinte maneira:

-- 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();

Após a execução da série de instruções acima, você saberá se a inserção teve algum efeito, verificando se LAST_INSERT_ID ainda estava definido como "some_flag_init_value_of_your_choice" no final da execução.

Caso contrário, você pode acabar com a seguinte situação 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'.

Como a segunda inserção falhou , você pode esperar que a segunda chamada para LAST_INSERT_ID retorne NULL ou que produza um conjunto de resultados vazio (zero linhas). O fato de ainda retornar um identificador inteiro válido pode induzir você a pensar que a segunda inserção foi SUCEDIDA quando não ocorreu.

As coisas ficam MESMO WEIRDER quando você considera que LAST_INSERT_ID continuará preservando e repetindo o último ID exclusivo bem-sucedido, mesmo que as instruções de inserção com falha subsequentes estejam direcionando tabelas diferentes da tabela que produziu o último ID exclusivo bem-sucedido. Em outras palavras, você insere na tabela TA e obtém um ID 5, depois insere na TB (mas falha), mas ainda vê um 5. Com base nisso, você acha que acabou de criar uma nova linha no TA com id de 5 e uma nova linha em TB com id de 5, enquanto que, na realidade, não existe nenhuma linha em TB com id 5 ou existe essa linha, mas na verdade não tem nada a ver com qualquer código que você apenas correu.

pestófago
fonte
2
Em primeiro lugar, você não deveria ter usado a existência de last_insert_id()para julgar se uma consulta foi bem-sucedida. Afinal, é o último ID inserido, mantendo os valores necessários quando você já sabia que teve sucesso.
Pacerier