多数の行を含むINSERT
ステートメントを実行している間、そうでなければ失敗の原因となる重複エントリーをスキップしたいのです。いくつかの調査の結果、私の選択肢は次のいずれかを使用することのようです。
ON DUPLICATE KEY UPDATE
INSERT IGNORE
は、他の種類の失敗が予告なしにずれることを招くことを意味します。私はこれらの仮定に正しいですか?重複の原因となる可能性がある行を単純にスキップし、他の行に進むための最善の方法は何ですか?
私はINSERT...ON DUPLICATE KEY UPDATE
を使うことを勧めます。
INSERT IGNORE
を使用した場合、重複キーになると実際には行は挿入されません。しかし、ステートメントはエラーを生成しません。代わりに警告を生成します。これらのケースは次のとおりです。
PRIMARY KEY
またはUNIQUE
制約を持つ列に重複キーを挿入します。NOT NULL
制約を使用して列にNULLを挿入します。REPLACE
を使用する場合、MySQLは実際にはDELETE
に続いてINSERT
を内部的に実行します。これは、予期しない副作用をもたらします。
REPLACE
を防ぐことができます。DELETE
を起動するトリガは不必要に実行されます。 修正: REPLACE
とINSERT...ON DUPLICATE KEY UPDATE
の両方はMySQLに特有の非標準的な、独自の発明です。 ANSI SQL 2003では、同じニーズ(およびそれ以上)を解決できるMERGE
ステートメントが定義されていますが、MySQLはMERGE
ステートメントをサポートしていません。
ユーザーがこの投稿を編集しようとしました(編集はモデレーターによって却下されました)。編集はINSERT...ON DUPLICATE KEY UPDATE
が新しい自動インクリメントIDを割り当てられるという主張を追加しようとしました。新しいIDは generated ですが、変更された行では使用されません。
下記のデモを参照してください。PerconaServer 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 |
+----+------+
これが何を意味するのかを知りたい場合は、次のようにしてください。
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
それであなたはそれを持っています。これはすべて、ほとんどデータがなく、本番環境ではない新しいテーブルで実行されたため、実行時間は微視的で無関係でした。現実のデータを持っている人は誰でもそれを提供することを歓迎します。
追加するべき重要なこと:INSERT IGNOREを使用していて、あなたが主な違反をしている時、MySQLは警告を発しません!
たとえば、一度に100個のレコードを1つの不良レコードとともに挿入しようとすると、対話モードになります。
Query OK, 99 rows affected (0.04 sec)
Records: 100 Duplicates: 1 Warnings: 0
ご覧のとおり、警告はありません。この振る舞いは公式のMysqlドキュメンテーションでさえ間違って説明されています。
スクリプトに通知する必要がある場合、(キーの違反のために)いくつかのレコードが追加されていない場合は、mysql_info()を呼び出して "Duplicates"値について解析する必要があります。
私は日常的にINSERT IGNORE
を使っています、そしてそれはまさにあなたが探している種類のふるまいのように聞こえます。インデックスの競合を引き起こす可能性のある行が挿入されず、それに応じてプログラムを計画していることを知っていれば、問題は発生しません。
私はこれが古いことを知っています、しかし私はINSERT..IGNOREで情報を見つけようとしている間に他の誰か(私のような)がこのページに到着した場合に備えてこのメモを追加します。
前述のように、INSERT..IGNOREを使用すると、INSERTステートメントの実行中に発生したエラーは代わりに警告として扱われます。
明示的に言及されていないことの1つは、挿入時に無効な値が最も近い値に調整されるということです(IGNOREキーワードが使用されていない場合、無効な値によってクエリが中止されます)。
Replace
Intoはオプションのようです。またはで確認できます
IF NOT EXISTS(QUERY) Then INSERT
挿入または削除してから挿入します。私は最初にIF NOT EXISTS
チェックをする傾向があります。
ON DUPLICATE KEY UPDATEは標準では 本当に ではありません。 REPLACEと同じくらい標準的です。 SQL MERGE を参照してください。
基本的に両方のコマンドは標準コマンドの代替構文バージョンです。
IGNOREを挿入することの潜在的な危険性。 VARCHAR値をもっと長く挿入しようとしているのであれば、columnはで定義されています - 値は切り捨てられ、厳密モードが有効な場合は挿入されます。
クエリセットの最後にinsert ignore
ステートメントを持つSHOW WARNINGS;
を使用すると、重複したIDを含むすべての警告を含むテーブルが表示されます。
テーブルに挿入し、主キーまたは一意のインデックスの競合がある場合は、その行を挿入する代わりに競合する行を更新します。
構文:
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"を持つ行があるとします。そのため、このステートメントは行のcolumn2の値を "c"で更新します。ここで、column1の値は "a"です。
そのため、新しい行を挿入したい場合は、そうでなければ主キーまたは一意のインデックスの競合でその行を更新してください。
このリンクについてもっと読む