- PR -

重複したレコードを作りたくない場合のInsertについて

投稿者投稿内容
indigo-x
大ベテラン
会議室デビュー日: 2008/02/21
投稿数: 207
お住まい・勤務地: 太陽の塔近く
投稿日時: 2008-05-04 07:38
引用:

Error401さんの書き込み (2008-05-04 02:36) より:
deleteしてinsert方式は、同時に2レコードinsertされる可能性がある問題が依然として残るのでは?


(私も勘違いしてましたが)
  deleteしてinsert方式の場合は、PKを変えないとダメですね。
  
   (複数の値をPKにするか?orハッシュ値をPKにする?)
べる
ぬし
会議室デビュー日: 2003/09/20
投稿数: 1093
投稿日時: 2008-05-04 10:14
引用:
------------------------------------------

1.同じ値がないかSelect

2.同じ値がない場合は、PKとなる値を採番
 同じ値がある場合は、SelectしたレコードのPKを格納

3.トランザクション開始

4.1の結果によりInsert、またはUpdate

5.トランザクションコミット

------------------------------------------


同じ値がないか確認する列とPKとなる値が格納される列は別ってことですよね。テーブルも別ですか?
1で同じ値がない場合は4でInsert、ある場合はUpdateということでいいんですよね。
UpdateのWhere句では何を見ますか?これらの如何によってクエリ上で工夫できるかもしれませんが、

最後でInsertするときに
INSERT 〜 SELECT 登録する値,,, WHERE NOT EXISTS 同じ値がないかSELECT
のようにし、@@ROWCOUNTが0だったら失敗(やりなおし)とすればいい気がします。

ストアドにでもして1.selectからトランザクション開始してSERIALIZABLEにしてもいいですが。
GENZO
大ベテラン
会議室デビュー日: 2003/11/26
投稿数: 111
お住まい・勤務地: 名古屋
投稿日時: 2008-05-04 20:46
引用:

Error401さんの書き込み (2008-05-04 02:36) より:
deleteしてinsert方式は、同時に2レコードinsertされる可能性がある問題が依然として残るのでは?


考慮漏れでした。同時に2レコードの問題が解決しませんね。
ご指摘ありがとうございます。
未記入
会議室デビュー日: 2008/05/02
投稿数: 3
投稿日時: 2008-05-07 17:15
たくさんのレスありがとうございます。

引用:

末記人さんの書き込み (2008-05-02 21:34) より:
1の前にテーブルをロックするか、連番管理用のテーブルを作成するのはいかがでしょう?

条件により重複が可能というのがよくわかりませんが、帳票などのデータで、同じで連番で複数レコードがある場合があるとかなのでしょうかね?



説明足らずで申し訳ありません。

下記のような商品テーブルと在庫テーブルがあり、
商品テーブルと在庫テーブルの関係は1:0またはNとなります。

区分が「0」の商品を入庫した場合には、在庫テーブルに1レコード追加します。
区分が「1」の商品を入庫した場合には、在庫テーブルの同一商品コードがあれば数量を更新し、
なければ1レコード追加します。


≪商品テーブル≫
商品コード |区分
-------------------------------------
1001    |0
1002    |1
1003    |1

≪在庫テーブル≫
在庫コード |商品コード |数量
-------------------------------------
2001    |1001    |1
2002    |1001    |1
2003    |1002    |5


区分が「1」の商品を入庫した場合で、在庫テーブルに同一商品コードがない時に、
2台の端末からInsertを行い、2レコード出来てしまう事が問題となっています。



引用:

甕星さんの書き込み (2008-05-02 21:55) より:
最初のSelectは不要です。

複数登録してはならないなら、テーブル定義で一意制約をおこないましょう。
そして、Insert文実行。一意制約エラーならUpdate文を実行。

一意制約をかけられないなら、
Update文実行。更新レコード数が0ならInsert文実行。(要テーブルロック)



商品テーブルの区分の値により、複数登録可となるため一意制約は出来ません。
他の端末から読込が出来ないとまずいため、テーブルロックをしない方向で考えています。



引用:

べるさんの書き込み (2008-05-04 10:14) より:
同じ値がないか確認する列とPKとなる値が格納される列は別ってことですよね。テーブルも別ですか?
1で同じ値がない場合は4でInsert、ある場合はUpdateということでいいんですよね。
UpdateのWhere句では何を見ますか?これらの如何によってクエリ上で工夫できるかもしれませんが、

最後でInsertするときに
INSERT 〜 SELECT 登録する値,,, WHERE NOT EXISTS 同じ値がないかSELECT
のようにし、@@ROWCOUNTが0だったら失敗(やりなおし)とすればいい気がします。

ストアドにでもして1.selectからトランザクション開始してSERIALIZABLEにしてもいいですが。




同じ値がないか確認する列とPKとなる値が格納される列は別で、テーブルは同じです。
上記の在庫テーブルでいうと、同じ値がないか確認する列は「商品コード」でPKは「在庫コード」になります。
※ただし、商品コードは、商品テーブルの区分により同じ値があることがあります。

NOT EXISTSの条件で指定する列がPKではないため、
他の端末からSelectした時にロック待ちになってしまわないでしょうか?
ゆうじゅん
ぬし
会議室デビュー日: 2004/01/16
投稿数: 347
投稿日時: 2008-05-07 18:48
在庫コードを「商品コード」+「連番」にするか、「連番」の項目を追加して

区分が0の場合
1)在庫テーブルを商品コードでgroup by検索し、MAXで連番の最大値を取得
2)1)で取得された最大値+1を連番としてInsert
3)一意制約エラーが発生した場合は1)からリトライ

区分が1の場合
1)連番は1固定にしてInsert
2)一意制約エラーが発生した場合はUpdate

これじゃだめですかね?

[ メッセージ編集済み 編集者: ゆうじゅん 編集日時 2008-05-07 18:52 ]
未記入
会議室デビュー日: 2008/05/02
投稿数: 3
投稿日時: 2008-05-07 19:38
引用:

ゆうじゅんさんの書き込み (2008-05-07 18:48) より:
在庫コードを「商品コード」+「連番」にするか、「連番」の項目を追加して

区分が0の場合
1)在庫テーブルを商品コードでgroup by検索し、件数をカウント
2)1)で取得された件数+1を連番としてInsert
3)一意制約エラーが発生した場合は1)からリトライ

区分が1の場合
1)連番は1固定にしてInsert
2)一意制約エラーが発生した場合はUpdate

これじゃだめですかね?



レスありがとうございます。

商品コードと在庫コードはバーコードとして出力するため桁数が決まっており、
商品コード+連番となると桁数がオーバーしてしまうため出来ません…。

※確かに商品コードと区分から在庫コード(PK)が作れれば出来そうですが…
べる
ぬし
会議室デビュー日: 2003/09/20
投稿数: 1093
投稿日時: 2008-05-07 21:47
在庫コードの採番方法がわかりませんが、オートナンバーとして
コード:
insert into 在庫テーブル (商品コード,数量)
select 商品コード,1
from 商品テーブル
 where 
   @商品コード=商品コード
  and 
   (区分=0 OR not exists 
    (select * from 在庫テーブル
     where 商品コード=@商品コード)
   )

if @@rowcount=0 
begin
update 在庫テーブル set 数量=数量+1
where @商品コード=商品コード
end



最大値+1だとすれば
コード:
insert into 在庫テーブル (在庫コード,商品コード,数量)
select
(case when 
(select count(*) from 在庫テーブル)=0 then 2001
else
(select max(在庫コード)+1 from 在庫テーブル) end),
商品コード,1
・
・

等とすればよいでしょうか。

引用:
NOT EXISTSの条件で指定する列がPKではないため、
他の端末からSelectした時にロック待ちになってしまわないでしょうか?

内部の動きには詳しくないですが、PKでないWHEREがあるとそうなるんですか。
上記のINSERT文ひとつの間ロックされるのはしょうがないですよね?
じゃないとどうやっても2レコード出来てしまう問題は消えませんよね。
indigo-x
大ベテラン
会議室デビュー日: 2008/02/21
投稿数: 207
お住まい・勤務地: 太陽の塔近く
投稿日時: 2008-05-08 08:42
質問内容が同時実行性(排他)とPKの問題がごっちゃに
なっているような気がします。
PKを決めれば同時実行性の問題もクリアになってくると
思います。

私はゆうじゅんさんのやり方がよいと思います。
 フィールド長の問題は別テーブルで
   コンバートをかければよいのでは。。。

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