「INSERT IGNORE」と「INSERT…ON DUPLICATE KEY UPDATE」


833

INSERT多くの行を含むステートメントを実行しているときに、他の方法では失敗の原因となる重複したエントリをスキップしたいと思います。いくつかの調査の後、私の選択肢はどちらかを使用することです:

  • ON DUPLICATE KEY UPDATE なんらかのコストで不必要な更新を意味する、または
  • INSERT IGNORE これは、他の種類の失敗が未発表で滑り込むことへの招待を意味します。

私はこれらの仮定で正しいですか?重複を引き起こす可能性のある行を単にスキップして、他の行に進むための最良の方法は何ですか?

回答:


991

の使用をお勧めしINSERT...ON DUPLICATE KEY UPDATEます。

を使用する場合INSERT IGNORE、重複するキーが発生すると、行は実際には挿入されません。しかし、ステートメントはエラーを生成しません。代わりに警告を生成します。これらのケースは次のとおりです。

  • PRIMARY KEYまたはUNIQUE制約付きの列に重複キーを挿入します。
  • NOT NULL制約付きの列にNULLを挿入します。
  • 行をパーティション分割テーブルに挿入しますが、挿入した値はパーティションにマップされません。

を使用する場合REPLACE、MySQLは実際に内部にDELETE後続し、INSERT予期しない副作用がいくつかあります。

  • 新しい自動インクリメントIDが割り当てられます。
  • 外部キーを持つ従属行は削除される場合があります(カスケード外部キーを使用している場合)REPLACE
  • 起動するトリガーがDELETE不必要に実行されます。
  • 副作用はレプリカにも伝播します。

訂正:両方REPLACEINSERT...ON DUPLICATE KEY UPDATE非標準、MySQLへの独自の発明の固有のものです。ANSI SQL 2003はMERGE、同じニーズ(およびそれ以上)を解決できるステートメントを定義していますが、MySQLはそのMERGEステートメントをサポートしていません。


ユーザーがこの投稿を編集しようとしました(編集はモデレーターによって拒否されました)。編集INSERT...ON DUPLICATE KEY UPDATEにより、新しい自動インクリメントIDが割り当てられるクレームを追加しようとしました。新しいIDが生成されることは事実ですが、変更された行では使用されません。

Percona Server 5.5.28でテストされた以下のデモを参照してください。構成変数innodb_autoinc_lock_mode=1(デフォルト):

mysql> create table foo (id serial primary key, u int, unique key (u));
mysql> insert into foo (u) values (10);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   10 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1

mysql> insert into foo (u) values (10) on duplicate key update u = 20;
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

上記は、IODKUステートメントが重複を検出し、更新を呼び出しての値を変更することを示していuます。AUTO_INCREMENT=3は、IDが生成されたが、その行では使用されなかったことを示しています。

一方REPLACE、元の行を削除して新しい行を挿入し、新しい自動インクリメントIDを生成して保存します。

mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+
mysql> replace into foo (u) values (20);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  3 |   20 |
+----+------+

3
MySQL開発チームがANSI SQL 2003からMERGEを採用するつもりはあるのでしょうか。
ロニーベスト

1
@LonnieBest:MERGEを実装するための機能リクエストは2005年に行われましたが、私の知る限り、進展や計画はありません。 bugs.mysql.com/bug.php?id=9018
Bill Karwin、

2
無効な型の不一致に対して警告(エラーではなく)を生成しますが、複合主キーの重複については警告を生成しません。
FABRICIOマット

11
たくさんのINSERT ... ON DUPLICATE KEY UPDATE ...ステートメントが入力されているテーブルを見てきました。多くのデータが重複しており、AI PKの1つのインスタンスが2つの行の間で17,029,941から46,271,740に増加しています。毎回新しいAIが生成されるということは、範囲をすぐに満たすことができ、クリーンアップする必要があることを意味します。このテーブルはたった2週間前のものです。
Engineer81

4
@AntTheKnee、ああ、ビッグデータの時代に取り組むことの課題。
ビルカーウィン2014

174

これが何を意味するのかを確認したい場合のために、ここにすべてのブローバイブローがあります:

CREATE TABLE `users_partners` (
  `uid` int(11) NOT NULL DEFAULT '0',
  `pid` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`uid`,`pid`),
  KEY `partner_user` (`pid`,`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

主キーは、このクイックリファレンステーブルの両方の列に基づいています。主キーには一意の値が必要です。

さぁ、始めよう:

INSERT INTO users_partners (uid,pid) VALUES (1,1);
...1 row(s) affected

INSERT INTO users_partners (uid,pid) VALUES (1,1);
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1);
...0 row(s) affected

INSERT INTO users_partners (uid,pid) VALUES (1,1) ON DUPLICATE KEY UPDATE uid=uid
...0 row(s) affected

上記は、列をそれ自体に等しく設定することで余分な作業を節約しました。実際には更新は必要ありません。

REPLACE INTO users_partners (uid,pid) VALUES (1,1)
...2 row(s) affected

そして今いくつかの複数行のテスト:

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...3 row(s) affected

コンソールで他のメッセージは生成されず、テーブルデータに4つの値が含まれるようになりました。(1,1)以外をすべて削除したので、同じ競技場からテストできます

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) ON DUPLICATE KEY UPDATE uid=uid
...3 row(s) affected

REPLACE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...5 row(s) affected

だからあなたはそれを持っています。これはデータがほとんどなく、本番環境ではない新しいテーブルですべて実行されたため、実行時間は微視的で無関係でした。実世界のデータを持っている人なら誰でもそれを提供することを歓迎します。


重複したキーと置換先の両方を実行しました。私のテーブルは約12万行で終了し、約30%の行が重複しています。重複キーは102秒で実行され、置換キーは105秒で実行されました。私の場合、重複したキーを使い続けています。
腋窩炎

1
MariaDB 10で上記をテストし、実行時に警告が表示されましたINSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
Floris

このためにどのMySQLバージョンを使用しましたか?
Radu Murzea

41

追加すべき重要事項:INSERT IGNOREを使用していて、キー違反がある場合、MySQLは警告を発しません!

たとえば、一度に100件のレコードを挿入しようとすると、1つのレコードに障害があると、インタラクティブモードになります。

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

ご覧のとおり:警告はありません!この動作は、MySQLの公式ドキュメントで誤って説明されています。

スクリプトに通知する必要がある場合、(キー違反のために)一部のレコードが追加されていない場合は、mysql_info()を呼び出して「重複」値を解析する必要があります。


6
PHPを使用mysqli_affected_rows()している場合は、INSERT実際に発生したかどうかを知るためにを使用する必要があります。
Amal Murali

MySQLの5.5とMariaDB 10の両方で、私はやるエラーを取得Cannot add or update a child row: a foreign key constraint fails し、何行(でも有効なもの)が追加されません。
フローリス

2
@Florisこのエラーは外部キー制約が原因であり、重複キーが原因ではありません。MySQL 5.5.28を使用しています。を使用するとINSERT IGNORE、重複したキーは無視され、エラーや警告は表示されません。
toxalot 2014年

20

私は日常的にを使用していますがINSERT IGNORE、まさにあなたが探しているような行動のようにも聞こえます。インデックスの競合を引き起こす行が挿入されないことがわかっていて、それに応じてプログラムを計画している限り、問題は発生しません。


4
重複以外のエラーは無視するのではないかと心配です。これは正しいですか、またはINSERT IGNOREは複製の失敗を無視するだけですか?ありがとう!
トーマスGヘンリー

2
エラーは警告に変わります。私の答えでそのようなケースのリストを見てください。
ビルカーウィン、

残念です; 私はそれが重複した失敗を無視することを望みます。
ロニーベスト

キー違反はエラーの原因になります!@Jensの回答で私のコメントを参照してください。
フローリス

1
@Pacerier、それはあなたのアプリケーションが警告をチェックするかどうかに依存します。または、警告チェックできるかどうか。たとえば、ほとんどのORMパッケージは機会を与えません。一部のコネクタ(JDBCなど)もMySQL APIからユーザーを分離するため、警告を確認する機会がありません。
Bill Karwin、2015年

18

これが古いことはわかっていますが、INSERT..IGNOREに関する情報を見つけようとしているときに他の誰か(私のような)がこのページにアクセスした場合に備えて、このメモを追加します。

上記のように、INSERT..IGNOREを使用すると、INSERTステートメントの実行中に発生したエラーは警告として扱われます。

明示的に述べられていないことの1つは、INSERT..IGNOREが挿入されると無効な値が最も近い値に調整されることです(一方、無効な値は、IGNOREキーワードが使用されなかった場合にクエリを中止します)。


6
「無効な値」が何を意味しているのかよくわかりませんが、何に修正しましたか?例または詳細な説明を提供できますか?
Marenz、

4
つまり、 "INSERT IGNORE"を使用しているときに間違ったデータ型をフィールドに挿入すると、フィールドのデータ型と一致するようにデータが変更され、潜在的に無効な値が挿入されて、クエリが引き続き実行されます。「INSERT」のみの場合、不正なデータタイプに関するエラーが発生し、クエリは中止されます。これは、varcharまたはテキストフィールドに数値を挿入しても問題ない場合がありますが、数値データタイプのフィールドにテキスト文字列を挿入すると、データが不良になります。
codewaggle

2
@Marenzの別の例:テーブルにnull以外の列があり、 "INSERT IGNORE"クエリでその列の値が指定されていない場合、厳密なsql_modeが有効かどうかに関係なく、行はその列にゼロ値で挿入されます。
シャノン

無効な値の良い点!このスレッドは「INSERT IGNORE」について学ぶのに最適です。5セントも残しておきます。medium.com / legacy-systems-diary / 「INSERT IGNORE」を使用する際の注意の例についての素晴らしい記事ステートメント。
0x49D1

8

ON DUPLICATE KEY UPDATEは実際には標準にはありませ。REPLACEと同じくらい標準です。SQL MERGEを参照してください。

基本的に、両方のコマンドは標準コマンドの代替構文バージョンです。


1
replaceは削除と挿入を行いますが、重複キー更新は既存の行を更新します。いくつかの違いは次のとおりです。自動インクリメントID、行の位置、
一連

8

Replaceにオプションのようです。または、

IF NOT EXISTS(QUERY) Then INSERT

挿入または削除してから挿入します。私はIF NOT EXISTS最初にチェックに行く傾向があります。


早速のお返事ありがとうございます。私はいたるところにいると思いますが、これは不要な更新を実行するという点でON DUPLICATE KEY UPDATEに似ていると思います。無駄に見えますが、よくわかりません。これらのいずれも動作するはずです。誰がどちらがベストか知っているかどうか疑問に思っています。
トーマスGヘンリー

6
NTuplip-このソリューションは、並行トランザクションによる挿入からの競合状態に対して依然としてオープンです。
Chris KL、

REPLACEany PRIMARYまたはUNIQUEkeyに一致するテーブル内のすべての行を削除し、次に INSERTs。これは、潜在的にIODKUよりもはるかに多くの作業です。
リックジェームズ

4

INSERT IGNOREの潜在的な危険。VARCHAR値をより長く挿入しようとすると、列が定義された-値は切り捨てられ、厳密モードが有効な場合でも挿入されます。


3

クエリセットの最後にステートメントを使用するinsert ignoreSHOW WARNINGS;、重複しているIDを含むすべての警告がテーブルに表示されます。


SHOW WARNINGS;最新のクエリにのみ影響するようです。複数のステートメントがある場合、以前のステートメントは蓄積されません。
Kawu

2

テーブルに挿入し、主キーまたは一意のインデックスの競合がある場合は、その行を挿入する代わりに競合する行を更新します。

構文:

insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;

ここで、このinsertステートメントは、前に見たものとは異なって見える場合があります。この挿入ステートメントは、aおよびbの値を持つtable1の行をそれぞれ列column1およびcolumn2に挿入しようとしています。

このステートメントを深く理解しましょう:

例:ここで、column1はtable1の主キーとして定義されています。

ここで、table1の場合、column1に値「a」を持つ行はありません。したがって、このステートメントはtable1に行を挿入します。

ここで、table1のcolumn2に値「a」を持つ行があるとします。したがって、このステートメントは、列1の値が「a」である「c」で行のcolumn2の値を更新します。

そのため、新しい行を挿入する場合は、主キーまたは一意のインデックスの競合時に行を更新します。
このリンクで詳細を読む


0

INSERT...ON DUPLICATE KEY UPDATE 予期しない例外管理を防ぐために推奨されます。

このソリューションは、** 1つの一意の制約**がある場合にのみ機能します

私の場合はそれを知ってcol1おりcol2、ユニークな複合インデックスを作成します。

エラーを追跡しますが、複製時に例外をスローしません。パフォーマンスに関しては、MySQLがこれを認識して更新しないため、同じ値による更新は効率的です。

INSERT INTO table
  (col1, col2, col3, col4)
VALUES
  (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
    col1 = VALUES(col1),
    col2 = VALUES(col2)

このアプローチを使用するアイデアは、phpdelusions.net/pdoのコメントから生まれました

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