挿入と同じ重複キー更新時


158

私は周りを検索しましたが、それが可能かどうかはわかりませんでした。

私はこのMySQLクエリを持っています:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

フィールドIDには「一意のインデックス」があるため、2つ存在することはできません。同じIDがデータベースに既に存在する場合は、更新します。しかし、本当にこれらすべてのフィールドをもう一度指定する必要がありますか?

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

または:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

私はすでにインサートにすべてを指定しました...

追加のメモ、IDを取得するための回避策を使用したいと思います!

id=LAST_INSERT_ID(id)

誰かが私に最も効率的な方法を教えてくれることを願っています。


9
私の知る限り、各列を書き直す方法a=VALUES(a), b=VALUES(b), ...は、あなたが行く必要がある方法です。それが私のすべてのINSERT ON DUPLICATE KEY UPDATE発言に対してそうする方法です。
Valdogg21 2013年

4
明確にするために、回答されている質問を理解している限り、ここに記載されている内容はすべて正しいです。更新するすべての列を再記述する必要がありますか?はい。25列のテーブルで8列を挿入または更新する場合は、8列を2回指定する必要があります。1回は挿入部分に、もう1回は更新部分に記述します。(更新部分の主キーをスキップすることもできますが、 "a = 1、b = 2、c = 3"文字列をプリフォーマットして2回埋め込むのが最も簡単です。)ただし、残りの17列を再宣言する必要はありません。変更したくない。UPDATEになると、既存の値が保持されます。
Timberline 2013

3
このコメントを追加したのは、この種類のINSERTを使用すると、質問と回答の中で言及されていない列が不明なため、何をテストすべきかを正確に学習した後でテストしたためです。INSERT ON DUPLICATE KEY UPDATEは、UPDATEで言及されていない列を変更せずに残しますが、INSERTでデフォルト値を提供します。REPLACE INTOを使用すると、言及されていない列は常にデフォルト値を取得し、既存の値は取得しません。
Timberline 2013

1
stackoverflow.com/questions/2472229/…を確認してください。あなたが探しているものがあると思います
Chococroc '25 / 11/25

回答:


82

このUPDATEステートメントは、古いフィールドを新しい値に更新できるように指定されています。古い値が新しい値と同じである場合、なぜそれを更新する必要があるのですか?

たとえば あなたの列があればaするgとしてすでに設定されている28、更新する必要はありません。

または、以下を使用することもできます。

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

から取得するにidLAST_INSERT_ID; 同じために使用しているバックエンドアプリを指定する必要があります。

LuaSQLの場合、conn:getlastautoid()は値をフェッチします。


6
これは単なる例です。実際のクエリには違いがあります。私の質問は非常に単純です:挿入と同じ場合、すべてのフィールド(および最初の例の値)を本当に指定する必要がありますか?すべてを挿入したい、または一意の値が一致する場合は、すべて更新します。
Roy

1
「追加のメモ、IDを取得するために回避策を使用したい!」
アガメムス2014年

1
@Tresdin、私が知る限り、それは単なる構文上の砂糖です。個人的には、a = VALUES(a)、b = VALUES(b)などを使用している人を見たことがありません。列に複数の値を割り当てることができますか?ただし、事前にデータを連結しない理由がわかりません。
DuckPuncher

54
テーブルtblに行(1,10)が既にある場合:INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)はa = 2を設定します。一方でINSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=a=を設定します10
BUIベトナムタン

2
なぜすべての賛成票か。これは動作しません。@BùiViệtThànhが指摘したように、a=a更新を使用しても何も起こりません。(元の質問のクエリも実際には何も更新しませんが、更新はOPが意図したもののようです。)
Roger Dueck

41

あなたが望むものであるかもしれないSQLにMySQL固有の拡張があります- REPLACE INTO

ただし、「ON DUPLICATE UPDATE」と同じようには機能しません

  1. 新しい行と衝突する古い行を削除してから、新しい行を挿入します。テーブルに主キーがない限り問題ありませんが、もしそうであれば、他のテーブルがその主キーを参照している場合

  2. 古い行の値を参照できないため、同等のことはできません

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

回避策を使用してIDを取得したいと思います。

これは機能するはずです。last_insert_id()は、主キーが自動インクリメントされている限り、正しい値を持つ必要があります。

ただし、すでに述べたように、他のテーブルでその主キーを実際に使用する場合REPLACE INTO、一意のキーを介して競合した古い行が削除されるため、おそらく受け入れられません。

次のようにして入力を減らす前に誰かが提案しました:

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);

それで、replace intoクエリの前に主キーに固執することはできませんか?
Roy

いいえ-その行は削除され、新しい主キーを持つ新しい行が作成されます。
Danack 2013年

これは、大規模なデータセットでのプロセスを遅くし、100K以上の行を持つcsvをインポートするのではないでしょうか?
ムハンマドオメルアスラム

24

他に方法はありません。すべてを2回指定する必要があります。1つは挿入、2つ目は更新の場合です。


9
これは誤りであり、受け入れられた答えではありません。@Danackからの答えが正解です。
ウェインワークマン

2
@WayneWorkmanの質問をもう一度読んでください。これが正解です。
Roy

24

ここにあなたの問題の解決策があります:

私はあなたのような問題を解決しようとしました&単純な側面からテストすることを提案したいと思います。

次の手順に従ってください:単純なソリューションから学びます。

ステップ1:次のSQLクエリを使用してテーブルスキーマ作成します

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

データを持つテーブル<code> user </ code>

手順2:次のSQLクエリを使用して、2つの列のインデックスを作成し、データの重複を防止します。

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

または、次のようにGUIから2列のインデックスを作成します。 GUIからインデックスを作成 インデックスを作成する列を選択してください テーブル<code> user </ code>のインデックス

ステップ3:存在する場合は更新し、ない場合は次のクエリを使用して挿入します。

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

上記のクエリを実行した後のテーブル<code> user </ code>


1
このウォークスルーをありがとう-私を理解させてくれて、高く評価してくれて、ありがとう!
Michael Nilsson、

パスワードをプレーンテキストで保存することは、データベースレベルでパスワードが重複しないようにするため、ひどい考えです。
OrangeDog 2018年

10

スクリプト言語を使用してSQLクエリを準備できる場合に備えて、のSET代わりにを使用して、field = valueペアを再利用できます(a,b,c) VALUES(a,b,c)

PHPの例:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

テーブルの例:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

4
値をエスケープするか、値を使用して準備済みステートメントを実行する必要があります。
Chinoto Vokro 2017年

1
@ChinotoVokro-同意する。これは単に「できる方法」を示しているだけで、例ではクエリ関数も使用していません。
Chris

1
これは、キーの配列と値の配列を指定するのに適した代替手段です。ありがとう。
Mark Carpenter Jr

5

あなたが使用して検討する必要がありREPLACE INTO構文が、重複したPRIMARY / UNIQUEキー時に、警告が表示され、それがDELETES行をし、INSERTS新しいものを。

すべてのフィールドを再指定する必要はありません。ただし、パフォーマンスの低下を考慮する必要があります(テーブルの設計によって異なります)。

警告:

  • AUTO_INCREMENT主キーがある場合、新しい主キーが与えられます
  • インデックスはおそらく更新する必要があります

インデックスが別のテーブルの外部キーでもある場合、制約違反があります。これは、削除部分によってトリガーされます。
user3290180

4

遅いのはわかりますが、誰かがこの答えを手伝ってくれることを願っています

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

以下のチュートリアルをここで読むことができます:

https://mariadb.com/kb/en/library/insert-on-duplicate-key-update/

http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/


いい答えだ。ただし、新しい行を参照するためのVALUES()の使用は、MySQL 8.0.20で非推奨になりました。リンクで詳述されている新しいアプローチは、行のエイリアスを使用することです。この例では、エイリアスですnewINSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473

@ user697473エラーが発生する:「SQL構文にエラーがあります。MariaDBサーバーのバージョンに対応するマニュアルで、 'AS new ON DUPLICATE KEY UPDATE a = new.a'の近くで使用する正しい構文を確認してください。 、そしてこの句を実装する前に、クエリが機能していた(明らかに重複キーが発生した場合を除く)。何か案は?
アーロン

@aaron-すみません、素晴らしいアイデアはありません。MySQL 8.0.20は4月末にリリースされたばかりです。おそらくMariaDBはまだ追いついていません。それがエラーを引き起こしている理由を説明することができます。
user697473

@ user697473ええ、元の回答で実際にa = VALUES(a)メソッドを試してみましたが、とにかくうまくいきました。
アーロン

-7

そのような場合はinsert ignoreを使用できます。重複したレコードを取得すると無視されますINSERT IGNORE ...; -ON DUPLICATE KEYなし


私はそれが存在しないときにそれを挿入したかった、そうでなければそれを更新したかった。それを無視しないでください。
ロイ

以前の値/同じ値で更新している場合に更新する理由、その新しい値の場合はそれで問題ない、挿入しない場合はそれを無視する作業を挿入
pitu

2
最も可能性が高いのは、値が変更される可能性があり、レコードが存在しない場合はレコードを作成し、アプリケーションに戻ってデータベースに戻る往復のオーバーヘッドなしに、変更された場合は既存のレコードを更新するためです。再び。
mopsyd 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.