MySQLの重複キー-最後の挿入ID?


132

次のクエリがあります。

INSERT INTO table (a) VALUES (0)
  ON DUPLICATE KEY UPDATE a=1

挿入または更新のいずれかのIDが必要です。insert_id()は「挿入された」IDのみを返し、更新されたIDは返さないと思うので、通常、これを取得するために2番目のクエリを実行します。

2つのクエリを実行せずに行のIDを挿入/更新して取得する方法はありますか?


3
想定するより、自分でテストしてみませんか?上記の編集のSQLは機能し、私のテストでは、挿入の失敗をキャッチしたり、INSERT IGNOREを使用したり、最初に重複があるかどうかを確認したりするよりも高速です。
Michael Fenwick、2011

4
警告:提案されたソリューションは機能しますが、auto_increment値は、挿入がない場合でも増加し続けます。重複キーが頻繁に発生する場合はalter table tablename AUTO_INCREMENT = 0;、上記のクエリの後に実行して、ID値の大きなギャップを回避することができます。
フランクフォルテ

回答:


175

このページを確認してください:https : //web.archive.org/web/20150329004325/https : //dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.htmlページ
の下部それらは、そのMySQL関数に式を渡すことにより、LAST_INSERT_IDを更新で意味のあるものにする方法を説明しています。

MySQLドキュメントの例から:

テーブルにAUTO_INCREMENT列が含まれ、INSERT ... UPDATEが行を挿入する場合、LAST_INSERT_ID()関数はAUTO_INCREMENT値を返します。ステートメントが代わりに行を更新する場合、LAST_INSERT_ID()は意味がありません。ただし、LAST_INSERT_ID(expr)を使用してこれを回避できます。idがAUTO_INCREMENT列であるとします。LAST_INSERT_ID()を更新で意味のあるものにするには、次のように行を挿入します。

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;

2
どういうわけかそのページを見たときにそれを逃した。したがって、更新部分は次のように表示されます。UPDATE id = LAST_INSERT_ID(id)そして、それはうまくいきます。ありがとう!
thekevinscott

7
php関数mysql_insert_id()はどちらの場合も正しい値を返すと言われています:php.net/manual/en/function.mysql-insert-id.php#59718
jayarjo

2
@PetrPeller-まあ、MySQLの内部を見ないで、それはおそらく値を生成することを意味しますが、その値は実行したクエリとは関係ありません。つまり、デバッグが面倒な問題です。
Jason

13
5.1.12以降は、これはおそらく不要になったと思われますが、今日は例外が見つかりました。自動インクリメントpkがあり、電子メールアドレスなどの一意のキーがあり、電子メールアドレスに基づいて 'on duplicate update'がトリガーされる場合、last_insert_idは更新された行の自動インクリメント値ではないことに注意してください。最近挿入された自動インクリメント値のようです。これは大きな違いをもたらします。回避策は、ここに示すものと同じです。つまり、更新クエリでid = LAST_INSERT_ID(id)を使用します。
sckd 2014

1
5.5では、@ sckdのコメントはまだ当てはまります。
e18r

37

正確には、これが元のクエリである場合:

INSERT INTO table (a) VALUES (0)
 ON DUPLICATE KEY UPDATE a=1

「id」は自動インクリメントの主キーであり、これが実際のソリューションになるでしょう。

INSERT INTO table (a) VALUES (0)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), a=1

すべてここにあります:http : //dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html

テーブルにAUTO_INCREMENT列が含まれ、INSERT ... UPDATEが行を挿入する場合、LAST_INSERT_ID()関数はAUTO_INCREMENT値を返します。ステートメントが代わりに行を更新する場合、LAST_INSERT_ID()は意味がありません。ただし、LAST_INSERT_ID(expr)を使用してこれを回避できます。idがAUTO_INCREMENT列であるとします。


7
はい、あなたが言ったのと同じことについて受け入れられた答えを見てください。3年前の投稿を復活させる必要はありません。とにかくあなたの努力をありがとう。
fancyPants 2012年

1
@tombom私がこの回答を投稿した唯一の理由は、受け入れられた回答が正しくないためです。更新するものが何もない場合は機能しません。
Aleksandar Popovic

2

REPLACEは、レコードが存在する場合は基本的に削除/挿入です。ただし、自動インクリメントフィールドが存在する場合、これが変更され、他のデータとの関係が壊れる可能性があります。


1
ああそう-私は以前のIDを取り除かないものを探しています
thekevinscott

これは、(制約により)別の関連データも削除する可能性があるため、危険な場合もあります。
Serge、


1

ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)が主キーを1ずつインクリメントするときに問題が発生しました。したがって、セッション内の次の入力のIDは2ずつインクリメントされます。


0

注目に値します。これは明らかです(ただし、ここでは明確にするために、とにかく言います)、REPLACEは新しいデータを挿入する前に既存の一致する行を吹き飛ばします。ON DUPLICATE KEY UPDATEは、指定した列のみを更新し、行を保持します。

マニュアルから:

REPLACEはINSERTとまったく同じように機能しますが、テーブルの古い行がPRIMARY KEYまたはUNIQUEインデックスの新しい行と同じ値を持っている場合、新しい行が挿入される前に古い行が削除されます。


0

自動インクリメントを使用する場合、既存のソリューションは機能します。ユーザーがプレフィックスを定義できる状況にあり、シーケンスを3000から再開する必要があります。このさまざまなプレフィックスのため、挿入でlast_insert_idを空にする自動インクリメントを使用できません。私はそれを次のように解決しました:

INSERT INTO seq_table (prefix, id) VALUES ('$user_prefix', LAST_INSERT_ID(3000)) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id + 1);
SELECT LAST_INSERT_ID();

プレフィックスが存在する場合は、プレフィックスを増やして、last_insert_idを設定します。プレフィックスが存在しない場合は、値3000のプレフィックスを挿入し、last_insert_idに3000を入力します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.