MySqlのLAST_INSERT_ID()関数は正しいことが保証されていますか?


36

INSERTを持つテーブルに単一の行を作成するAUTO_INCREMENT場合、LAST_INSERT_ID()関数を使用して、AUTO_INCREMENTその行に格納されている新しい'ed値を返します。

多くのMicrosoft SQL Server開発者と管理者がSQL Server(SCOPE_IDENTITYおよび@@IDENTITY)の同等の機能に問題がないことに気付いていないことは間違いない。

MySQLドキュメントの状態を知っています:

生成されたIDは、接続ごとにサーバーで維持されます。これは、関数によって特定のクライアントに返される値が、そのクライアントによって列にAUTO_INCREMENT影響を与える最新のステートメントに対して生成される最初の値であることを意味します。他のクライアントが独自の値を生成しても、この値は他のクライアントの影響を受けません。この動作により、各クライアントは、他のクライアントのアクティビティを気にすることなく、ロックやトランザクションを必要とせずに独自のIDを取得できます。AUTO_INCREMENTAUTO_INCREMENT

(ソース)

さらに言えば:

複数のクライアントからLAST_INSERT_ID()and AUTO_INCREMENT列を同時に使用することは完全に有効です。

(ソース)

LAST_INSERT_ID()正しい値を返さない原因となる既知のリスクやシナリオはありますか?

CentOS 5.5 x64およびFedora 16 x64およびInnoDBエンジンでMySQL 5.5を使用しています。

回答:


35

使用する際に指摘したいいくつかの警告LAST_INSERT_ID

  1. 単一行の挿入について言及したことは知っています。しかし、複数行の挿入をLAST_INSERT_ID()行う場合、最後にではなく、最初に挿入された行の値を返します。

  2. 挿入が失敗した場合、LAST_INSERT_ID()未定義になります。トランザクションの自動ロールバック(エラーによる)についても同じことが言えます。

  3. あなたが成功したトランザクションの挿入を行うと、あなたはまだ発行する場合ROLLBACKLAST_INSERT_ID()それはロールバックする前にあったように残されることになります。

  4. ステートメントベースのレプリケーションを使用する場合、およびステートメントベースのレプリケーションでは、いくつかの注意事項があります。最初は、トリガーまたは関数で使用される場合です。2番目は、auto_increment列が複合主キーの一部であり、キーの最初の列ではない、あまり一般的ではないシナリオです。AUTO_INCREMENTLAST_INSERT_ID


何も更新されていない場合に0を返す「ON DUPLICATE KEY UPDATE」がある場合、date_field = now()を設定すると、常に正しく返されます
max4ever

7

DTestの回答のポイント番号2をさらに展開するには:

私が使用したMySQLのバージョンでは、挿入を実行する予定のコードの各ブロックの前に、LAST_INSERT_IDの値を明示的にリセットすることをお勧めします。

これは次のように実行できます。

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

上記の一連のステートメントが実行された後、実行の最後でLAST_INSERT_IDがまだ「some_flag_init_value_of_your_choice」に設定されているかどうかを確認することにより、挿入が影響を与えたかどうかを知ることができます。

そうしないと、次の問題のある状況に陥ります。

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

2番目の挿入が失敗したため、2番目のLAST_INSERT_IDの呼び出しでNULLが返されるか、空の結果セット(ゼロ行)が生成されることを期待していた可能性があります。それでも有効な整数識別子が返されるという事実は、2番目の挿入が成功しなかった場合に2番目の挿入が成功したと誤解する可能性があります。

後続の失敗した挿入ステートメントが最後に成功した一意のIDを生成したテーブルとは異なるテーブルを対象にしている場合でも、LAST_INSERT_IDが最後に成功した一意のIDを保持および繰り返し続けると考えると、物事はEVEN WEIRDERになります。つまり、テーブルTAに挿入して5のIDを取得し、TBに挿入します(ただし失敗します)が、5が表示されます。それに基づいて、TAに新しい行を作成したと思います idが5 、idが5のTBに新しい行があるのに対し、実際には、idが5のTBに行が存在しないか、そのような行が存在しますが、実際にはコードとは何の関係もありません走った


2
そもそも、last_insert_id()クエリの成功を判断するためにの存在を使用すべきではありませんでした。結局、最後に挿入されたIDであり、成功したことが既にわかっているときに必要な値を保持しています。
パセリエ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.