web-dev-qa-db-ja.com

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

_AUTO_INCREMENT_列があるテーブルに対して単一の行INSERTを実行すると、LAST_INSERT_ID()関数を使用して新しい_AUTO_INCREMENT_ edを返したいその行に格納されている値。

多くのMicrosoft SQL Server開発者および管理者は、SQL Serverの同等の機能(_SCOPE_IDENTITY_および_@@IDENTITY_)が 問題なしで であることに気付いていることは間違いありません。

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

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

(ソース)

そして言うことまでさえ行きます:

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

(ソース)

LAST_INSERT_ID()が正しい値を返さない可能性がある既知のリスクまたはシナリオはありますか?

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

36
Kev

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

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

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

  3. 成功したトランザクションで挿入を実行しても、ROLLBACKを発行すると、LAST_INSERT_ID()はロールバック前の状態のままになります。

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

36
Derek Downey

DTestによって与えられた回答のポイント2をさらに拡張するには:

私が使用したMySQLのバージョンでは、挿入を実行する予定のコードの各ブロックの前に、explicity 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番目の挿入には失敗があるため、LAST_INSERT_IDへの2回目の呼び出しでNULLが返されるか、空の結果セット(ゼロ行)が生成されると予想していた可能性があります。それでも有効な整数識別子が返されるという事実は、2番目の挿入が成功しなかったときに成功したと誤解させる可能性があります。

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

7
pestophagous