- PR -

Insertが先 Updateが先 

投稿者投稿内容
末記人
大ベテラン
会議室デビュー日: 2005/12/05
投稿数: 233
お住まい・勤務地: あわにこ
投稿日時: 2008-07-23 20:43
こんばんは

MySQLの場合 2番は変更のない更新の場合0件になってしまうので論外ですね。
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2008-07-23 21:06
引用:

私は、3番ですね。



引用:

少なくとも1の発想はなかったので驚きです。



3 の場合、Select の結果「既存レコードがない」と分かったとして、Insert するまでの間に別のセッションから Insert されたらどーしましょ?

自セッションの Insert は失敗しますよね?
そしたらもっかい Select して Update みたいにリカバリします?

それとも Select する前にロックします?
それもまた、システムのパフォーマンスに悪影響を与える可能性が高いやも。

なんてのもあって

引用:

要は、ユーザー操作とデータの関係を、もっと詳細に詰めて下さい。



てことになると思います。


[ メッセージ編集済み 編集者: 渋木宏明(ひどり) 編集日時 2008-07-23 21:20 ]
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2008-07-23 23:13
引用:

のん吉さんの書き込み (2008-07-23 17:59) より:
@Insertで重複エラーをキャッチしたらUpdateをする


この手法はPostgreSQLだと問題が起きます。
エラーが発生したトランザクションはロールバックしか出来ないので。
それ以外のDBの場合は、この選択肢の中で確実に更新出来るのはこれだけです。

引用:

AUpdateで件数が0件だったら、Insertをする


複数のトランザクション間でINSERTが競合する可能性があります。

引用:

BSelectでデータがあるかチェックしてから、InsertかUpdateをする



これも複数のトランザクション間でINSERTが競合する可能性があります。


こういった更新を(PostgreSQLでも問題なく)確実に行うには、

1. 親レコードもしくはテーブルを排他ロックして更新を行う

2. SELECTした結果に応じてINSERT/UPDATEを使い分ける
  ⇒ 主キー制約違反が起きたらトランザクションを再試行

3. MERGEなどのINSERT/UPDATEの複合構文を使う

のどれかになると思います。

私は、親レコードのロックが利用可能な場合は1を使い、
そうでない場合は2を使うことが多いです。
rain
ぬし
会議室デビュー日: 2006/10/19
投稿数: 549
投稿日時: 2008-07-23 23:54
引用:

あしゅさんの書き込み (2008-07-23 23:13) より:
引用:

のん吉さんの書き込み (2008-07-23 17:59) より:
@Insertで重複エラーをキャッチしたらUpdateをする


この手法はPostgreSQLだと問題が起きます。
エラーが発生したトランザクションはロールバックしか出来ないので。


発生条件がわからなかったので「環境によっては」なんて曖昧な表現を使ってしまったのですが、PostgreSQL特有の事象だったんですね。
1年前から持っていた疑問が解けました。ありがとうございます。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2008-07-24 00:10
select for update使いますね。
で、レコードがあれば、更新、なければ挿入です。
そして、挿入時にナチュラルキー等で一意制約違反になった場合、
素直にエラー扱いにすることが多いです。
べる
ぬし
会議室デビュー日: 2003/09/20
投稿数: 1093
投稿日時: 2008-07-24 00:25
引用:
現在、VisualStudio2005 C#で開発をおこなっています。

DBはSQL Serverということでいいんですかね。

このテーブルのレコードがDeleteされることはあるんでしょうか、それにもよりますよねきっと。

「私はいつもこうやる」というわけではないですが、出てない方法として、

Insert Select not exists(〜)して件数が0件だったらUpadateする。これなら、
件数チェックで0件だったらから、Insertしたつもりが他でInsertされてた、
みたいな事が起きません。

ただ、速さを気にしないなら1番もありだし、終始ロックすれば2番もありだし、です。

MySqlだとREPLACE文というのがありますね。

というわけでJittaさんの意見に一番近いです。

[追記]ん、私の方法はrainさんの最初のレスの方法と原理的には一緒か。

[ メッセージ編集済み 編集者: べる 編集日時 2008-07-24 00:29 ]
Liquid_Force
大ベテラン
会議室デビュー日: 2003/08/28
投稿数: 102
投稿日時: 2008-07-24 00:44
こんばんわ、もう回答いらないかもしれませんが
私も3番ですね。

かつのりさんと一緒で、基本はSELECT FOR UPDATEを使います。
場合によってはNOWAITも一緒に指定して処理を待たずに
エラーを意図的に返したりもします〜。
さかもと
ぬし
会議室デビュー日: 2004/05/14
投稿数: 586
投稿日時: 2008-07-24 08:02
さかもとです。

引用:

3 の場合、Select の結果「既存レコードがない」と分かったとして、Insert するまでの間に別のセッションから Insert されたらどーしましょ?



この場合は例外として処理します。
小規模システムで、同時実行が運用でカバーされているケースではCount→Insert or Updateの判断で済ますことが多いですが、認識不足でしょうか・・・?
もちろんビジネスロジックでトランザクションの。。。という設計は必要ですが本件とは少し外れますので省略します。


引用:

それとも Select する前にロックします?
それもまた、システムのパフォーマンスに悪影響を与える可能性が高いやも。



要件によっては(SQL Serverの場合ですが) WITH(XXXLOCK) ですね。
パフォーマンスへの影響などは別途検討しますが、業務的な排他制御が必要なケースではスキーマの変更も含め厳格にロックします。

何にせよ、要件次第という条件はつきますが。


DBMSの特性によって違うとは知りませんでした・・・勉強になります。




_________________
------------------------------------------
拝啓、さかもとと申します♪

スキルアップ/キャリアアップ(JOB@IT)