ようこそ ゲスト さん、新規登録(無料)して気になる疑問を解決しませんか?

質問

QNo.3820750 Insert Into Select での重複について
質問者:aoman DB:SQLServer2000

こんにちは
お世話になっております。

トランザクションのテーブルA から ワークのテーブルBへInsert Into Select を使用してデータを格納しているのですが、その際に重複が発生してしまいます。

テーブルA 主キーあり
テーブルB 主キーなし

Delete B

Insert into B select 項目1,項目2,・・・
from A With(Nolock)
where 日付項目 = 20080101

テーブルAの主キー項目は全てテーブルBへInsertしており、Insert完了後のテーブルBの中身を見ると、まれに全く同じデータが2件出来ていることがあります。

このInsert into selectが実行されている間に、テーブルAに対して登録更新が行われることもあります。

色々と試してはいるのですが、原因が特定できずに困っております。
もし何かお気づきになられる方がいらっしゃいましたら、ご教示下さい。

よろしくお願い致します。
困り度:
  • 困っています
質問投稿日時:
08/02/29 18:08
この質問に対する回答は締め切られました。

回答良回答10pt

ANo.5 >>・すでに存在するレコードA1をテーブルAにInsertして(これがCOMMITするまではエラーにならないとして)、COMMITしないうちにSELECTされる
>⇒十分にあり得るとは思いますが、この様な場合は、既に存在するレコードA1と新たにInsertされようとした重複するレコードA1’がNolockでSelectした場合2件抽出されることになるのでしょうか?

理論上はあり得ると思いますね。

ただROLLBACKのログがない、ということですから、これもあり得ないということでしょうか。
エラーになったらROLLBACKされるはずですよね。

となると、いよいよわかりません。
回答者:agricap
種類:アドバイス
どんな人:経験者
自信:参考意見
回答日時:
08/03/10 11:44
この回答へのお礼ご回答ありがとう御座います。

>ただROLLBACKのログがない、ということですから、これもあり得ないということでしょうか。
>エラーになったらROLLBACKされるはずですよね。
そうですね。これもあり得ないことになってしまいます。

色々ありがとうございました。
まだ、あきらめずに調べて行きたいと思います。

回答

ANo.4 ということは、
「Insert into B select 項目1,項目2,・・・
from A With(Nolock)
where 日付項目 = 20080101」
の対象になるようなレコードが、これを実行中にテーブルAにInsertされる可能性がある、ということですね?


・すでに存在するレコードA1をテーブルAにInsertして(これがCOMMITするまではエラーにならないとして)、COMMITしないうちにSELECTされる、とか

・2つのトランザクションからともにレコードA1をテーブルAにInsertしようとして、この2つがCOMMITしないうちにSELECTされる、とか

・1つのトランザクションが
レコードA1をテーブルAにInsert
→SELECTされる
→InsertをROLLBACK
→レコードA1をテーブルAにInsert
→InsertをCOMMIT
→再びSELECT
とか・・・

はありえないですかね?
回答者:agricap
種類:アドバイス
どんな人:経験者
自信:参考意見
回答日時:
08/03/07 21:14
この回答へのお礼ご回答ありがとう御座います。

またまた、お礼が遅くなり申し訳御座いません。

>これを実行中にテーブルAにInsertされる可能性がある、ということですね?
⇒その通りです。Insert・Updateがあり得ます。

>・すでに存在するレコードA1をテーブルAにInsertして(これがCOMMITするまではエラーにならないとして)、COMMITしないうちにSELECTされる
⇒十分にあり得るとは思いますが、この様な場合は、既に存在するレコードA1と新たにInsertされようとした重複するレコードA1’がNolockでSelectした場合2件抽出されることになるのでしょうか?

>・2つのトランザクションからともにレコードA1をテーブルAにInsertしようとして、この2つがCOMMITしないうちにSELECTされる
⇒テーブルAにInsertを行うトランザクションは1つしかない為、この様なことは無いと思います。

>1つのトランザクションが
>レコードA1をテーブルAにInsert
>→SELECTされる
>→InsertをROLLBACK
>→レコードA1をテーブルAにInsert
>→InsertをCOMMIT
>→再びSELECT
⇒これは、Insert into Selectが流れている間に、一連の流れが行われるということでしょうか。それとも2度Insert into Selectが実行されるということでしょうか。
前者の場合、あり得るとは思いますが、事実上テーブルAの登録する際にROLLBACKはかかっておりません。
(ROLLBACKの際にはエラー情報としてログが出力されるが、そのログが存在していない為)
後者の場合、テーブルAのSELECTを行う前にテーブルBはクリアされる為、重複はしないと思われます。

色々お手間掛けまして申し訳御座いません。
よろしくお願い致します。

回答

ANo.3 すみませんが、わかりません。
Nolockが少し気になります。
他のロックに変えたら発生しないということはないですかね。
回答者:agricap
種類:アドバイス
どんな人:経験者
自信:参考意見
回答日時:
08/03/06 19:21
この回答へのお礼ご回答ありがとう御座います。

>Nolockが少し気になります。
>他のロックに変えたら発生しないということはないですかね。

私もNolockが一番怪しいのではと考えておりますが、どうしてもSQLの修正を行う前に原因を特定する必要がありましたので、Nolockを外したり等のテストを実際のSQLで行うことが出来ず、また、テスト環境で同様のSQLを実施したりしておりますが、現象(重複)の再現すら出来ずに行き詰っていた次第です。

すいません。本当は色々変更して試せれば良いのですが諸事情により本番のSQLを変更しての確認が出来ず、またテスト環境での再現が出来ていないことが最大のネックになっております。

私が今考えているのは、Nolockを使用することにより、テーブルAに更新が行われた際に、テーブルBへのINSERTが同時に処理され、未コミットデータがまずSELECTにて抽出され、INSERT。その同じタイミングで更新がコミットされ、そのコミットされたデータが再び、SELECTにて抽出され、INSERTされているのではと考えていますが、この様なことは実際にありえるのか、調査しているところです。

もし、何かご存知でしたら、ご教示頂けると幸いです。

回答

ANo.2 「このInsert into selectが実行されている間に、テーブルAに対して登録更新が行われることもあります」
とのことですが、これはどのような登録・更新でしょうか?
「select 項目1,項目2,・・・
from A With(Nolock)
where 日付項目 = 20080101」
の内容が変わることもあるような更新でしょうか?
回答者:agricap
種類:補足要求
どんな人:経験者
自信:参考意見
回答日時:
08/03/03 20:53
この回答へのお礼ご回答ありがとう御座います。

お礼が遅くなってしまい申し訳御座いません。

>これはどのような登録・更新でしょうか?
登録は単純に新規データの登録が行われます。
更新はテーブルAに持っているフラグ項目の更新です。0→1など

>内容が変わることもあるような更新でしょうか?
この内容が変わることはありません。

よろしくお願い致します。

回答

ANo.1 単純に同じSQLが2回実行されれば、同じレコードが2つできるのではないですか?
回答者:agricap
種類:回答
どんな人:経験者
自信:参考意見
回答日時:
08/03/01 02:04
この回答へのお礼ご回答ありがとう御座います。

この重複は1度だけの実行で発生しています。

すいません。質問の内容が曖昧でした。少しだけ質問内容に補足させて頂きますが、Insert Into Select で登録される件数が大体10,000件程度なのですが、このうち数件だけ、まれに重複していることがあります。
何らかのタイミングでこの様になるとは思うのですが・・・

よろしくお願いします。