SQLはテーブルにデータを更新または挿入するためのさまざまな方法を提供します:
いつ何を使用するべきか:
INSERTを使用します。UPDATEを使用します。MERGEまたはUPSERTを使用する方が効果的です。これにより、複雑さが軽減され、SQLリクエストの数が減ります。例(PostgreSQL):
INSERT INTO employees(id, name, salary) VALUES (1, 'Ivan', 100000) ON CONFLICT (id) DO UPDATE SET salary = EXCLUDED.salary;
パフォーマンスと整合性への影響:
UPSERTをユニークフィールドに対して実行しようとした際に、同じトランザクションで同じ行を同時に挿入・更新した場合、どうなりますか?
正しい答え: 同じ行を2回変更しようとするため、ロック/衝突エラーが発生する可能性があります。トランザクションは完了しないか、DBMSに応じてdeadlockエラーが発生することになります。
例:
BEGIN; INSERT INTO users(id, name) VALUES (1, 'Oleg') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name; UPDATE users SET name = 'Petr' WHERE id = 1; COMMIT;
不適切な順序や分離レベルの場合、変更の衝突が発生する可能性があります。
ストーリー №1
データ移行のロジックの中で、SELECTを行い、その後INSERTまたはUPDATEを行う組み合わせを使用していました。結果、他のトランザクションが早くデータを設定し、「duplicate key」エラーが発生することがありました。これにより、夜間のデータエクスポートが頻繁に失敗し、ロジックをUPSERTに変更する必要がありました。
ストーリー №2
MySQLプロジェクトで、ON DUPLICATE KEY UPDATEを不適切に適用しており、複合ユニークキーを正しく考慮していませんでした。その結果、一部のデータが更新されず、重複が発生しました。この問題は本番環境でのみ確認できました。
ストーリー №3
プロジェクトでユーザーデータの同期にUPSERTの代わりにUPDATEを使用していました。接続の障害が発生した際に、新しいユーザーの更新がスキップされるため、一部のデータが失われることがありました。分析の結果、完全にMERGE/UPSERTを導入しました。