回答:
の使用をお勧めし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が生成されることは事実ですが、変更された行では使用されません。
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 |
+----+------+
INSERT ... ON DUPLICATE KEY UPDATE ...
ステートメントが入力されているテーブルを見てきました。多くのデータが重複しており、AI PKの1つのインスタンスが2つの行の間で17,029,941から46,271,740に増加しています。毎回新しいAIが生成されるということは、範囲をすぐに満たすことができ、クリーンアップする必要があることを意味します。このテーブルはたった2週間前のものです。
これが何を意味するのかを確認したい場合のために、ここにすべてのブローバイブローがあります:
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 INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
。
追加すべき重要事項:INSERT IGNOREを使用していて、キー違反がある場合、MySQLは警告を発しません!
たとえば、一度に100件のレコードを挿入しようとすると、1つのレコードに障害があると、インタラクティブモードになります。
Query OK, 99 rows affected (0.04 sec)
Records: 100 Duplicates: 1 Warnings: 0
ご覧のとおり:警告はありません!この動作は、MySQLの公式ドキュメントで誤って説明されています。
スクリプトに通知する必要がある場合、(キー違反のために)一部のレコードが追加されていない場合は、mysql_info()を呼び出して「重複」値を解析する必要があります。
mysqli_affected_rows()
している場合は、INSERT
実際に発生したかどうかを知るためにを使用する必要があります。
Cannot add or update a child row: a foreign key constraint fails
し、何行(でも有効なもの)が追加されません。
INSERT IGNORE
、重複したキーは無視され、エラーや警告は表示されません。
私は日常的にを使用していますがINSERT IGNORE
、まさにあなたが探しているような行動のようにも聞こえます。インデックスの競合を引き起こす行が挿入されないことがわかっていて、それに応じてプログラムを計画している限り、問題は発生しません。
これが古いことはわかっていますが、INSERT..IGNOREに関する情報を見つけようとしているときに他の誰か(私のような)がこのページにアクセスした場合に備えて、このメモを追加します。
上記のように、INSERT..IGNOREを使用すると、INSERTステートメントの実行中に発生したエラーは警告として扱われます。
明示的に述べられていないことの1つは、INSERT..IGNOREが挿入されると無効な値が最も近い値に調整されることです(一方、無効な値は、IGNOREキーワードが使用されなかった場合にクエリを中止します)。
ON DUPLICATE KEY UPDATEは実際には標準にはありません。REPLACEと同じくらい標準です。SQL MERGEを参照してください。
基本的に、両方のコマンドは標準コマンドの代替構文バージョンです。
Replace
にオプションのようです。または、
IF NOT EXISTS(QUERY) Then INSERT
挿入または削除してから挿入します。私はIF NOT EXISTS
最初にチェックに行く傾向があります。
REPLACE
any PRIMARY
またはUNIQUE
keyに一致するテーブル内のすべての行を削除し、次に INSERTs
。これは、潜在的にIODKUよりもはるかに多くの作業です。
テーブルに挿入し、主キーまたは一意のインデックスの競合がある場合は、その行を挿入する代わりに競合する行を更新します。
構文:
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の値を更新します。
そのため、新しい行を挿入する場合は、主キーまたは一意のインデックスの競合時に行を更新します。
このリンクで詳細を読む
INSERT...ON DUPLICATE KEY UPDATE
予期しない例外管理を防ぐために推奨されます。
私の場合はそれを知ってcol1
おりcol2
、ユニークな複合インデックスを作成します。
エラーを追跡しますが、複製時に例外をスローしません。パフォーマンスに関しては、MySQLがこれを認識して更新しないため、同じ値による更新は効率的です。
INSERT INTO table
(col1, col2, col3, col4)
VALUES
(?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
col1 = VALUES(col1),
col2 = VALUES(col2)
このアプローチを使用するアイデアは、phpdelusions.net/pdoのコメントから生まれました。