この春プログラミング業界に就職しました新入社員です。
皆様のお知恵を拝借したく、質問させていただきました。
先日、以下のような処理を任されました。
・顧客ID,注文NO,商品NO,処理日時からなる注文テーブルに、
・同一商品の送付対象となる顧客のIDリスト(csvファイル)を元に、
・新しい注文を追加する
注文テーブルの主キーは顧客IDと注文IDを連結したものであり、
顧客001が過去に3回注文しているとすると、
今回追加すべきレコードは001,4,商品NO,処理日時となります。
急ぎの仕事でしたので格好良さは度外視し、
注文テーブルの注文NOの最大値を求め、
select文で顧客のIDリストと注文テーブルを連結して注文NOを1から最大値まで順に指定して表示し、
顧客IDごとに今回入力すべき注文NOを把握して、
IDリストのcsvファイルをエクセルで編集して顧客ID,注文NO,商品NOからなる
テーブルを作って注文テーブルに流し込み、
注文テーブルの処理日時がNULLになっているレコードを指定してgetdate()で現在時刻を放り込みました。
今回は幸いにして注文NOの最大値が3と小さく、
対象の顧客も1000人ほどと少なかったためにごり押しできましたが、
今後も同じような処理をする必要があるときに同じ手が通じるかどうか、
というかミスが怖くて二度と使いたくありません。
もっとスマートな方法があるのではないかと調べてはみたのですが、
顧客IDごとに注文IDの最大値+1を求めてinsertする辺りの処理が探し当てられず、
ここで質問させていただいた次第です。
よい方法をご存知の方がおられましたら、ご教授いただけましたら幸いです。
No.4ベストアンサー
- 回答日時:
指摘したことの繰り返しになりますが、ユーザーA,Bがいてほぼ同時に作られたSQLを実行した場合、後続のSQLは先行のSQLの終了を待ちません。
すなまち、同じMAX値を取得します。作られたSQLシングルユーザーで間をおいて実行している限りは正常に動くでしょう。しかし、マルチユーザーで負荷が高く鳴った時はエラーを起こす可能性をもっています。潜在的バグですね。
お礼が遅くなりました。
基本的にDBの更新は誰もアクセスしていないことを確認した上で行いますので、とりあえずは大丈夫なようです。
けれど、覚えておくべきことですね。ありがとうございました。
No.3
- 回答日時:
トランザクション処理をする場合、
INSERT INTO ... SELECT MAX(注文NO) FROM 注文テーブル
が同時実行されない保証はありません。
そうすると、後続のINSERT文は重複キーエラーを起こしてしまいます。
保証しようすれば、注文テーブルをテーブル単位にロックをかける必要がありますから却ってパフォーマンスが劣化します。
現在のRDBMSはほとんど行レベルロック機能を持っていますからロック粒度を小さくして同時実行性を増やしたほうが全体のスループットが増すと思います。
二度目のご回答ありがとうございます。
あの後、更に調べを進めまして、どうにか実行可能なSQL文を組むことができました。
insert into 注文テーブル(顧客ID,注文NO,商品NO,処理日時)
select 注文テーブル.顧客ID,
MAX(注文テーブル.注文NO)+1,
1234,
getdate()
FROM 注文テーブル inner join IDリスト
on 注文テーブル.顧客ID = IDリスト.顧客ID
GROUP BY 注文テーブル.顧客ID
1234は仮の商品NOです。
目的の動作をすることはテスト用サーバで確認できました。
ただ情けないことに、今回いただいたお言葉である『同時実行』や『重複キーエラー』といったものについて理解が不足しております。
ですので、上記のSQL文には何か重大な欠陥があるかもしれません。
もしnora1962様からご覧になられて不適切な箇所がありましたら、ご教授いただけますでしょうか。
何度もお手間を取らせてしまい、大変心苦しいのですが、ご検討いただけましたら幸いです。
No.2
- 回答日時:
自分も深くは理解していないため、
間違っているかもしれませんが、
INSERT INTO
注文テーブル
( 顧客ID
,注文回数
,商品NO
,処理日時
) VALUES (
'hoge1'
,(SELECT
MAX(注文回数)
FROM
注文テーブル
WHERE
顧客ID = 'hoge1'
GROUP BY
顧客ID) + 1
,'hoge2'
,GETDATE()
);
こういった感じに書いて、hoge1とhoge2を指定すれば
一度で最大数+1を取得してInsertができると思います。
テストはしていないので、
間違っていたら済みません。
すばやいご回答ありがとうございます。
初心者という言葉が免罪符になるとは思っていませんが、なにぶん知識・経験ともに不足しておりますので見当違いの解釈をしているかもしれません。
その際は「頭の悪いやつだなあ」と笑って流してやっていただければ幸いです。
挙げていただいたSQL文について、hoge2に関しては単一の商品NOですので直接指定できます。
問題はhoge1のほうでして、対象となる顧客IDの羅列から、一つずつ取り出してhoge1に指定し、一気に実行する方法がわからないのです……。
No.1
- 回答日時:
顧客IDごとに採番レコードを持つ採番テーブルを使用するのはどうですか。
トランザクション開始
SELECT 連番値 FROM 採番テーブル WHERE 顧客ID='該当顧客ID' FOR UPDATE
INSERT INTO 注文テーブル VALUES ( '該当顧客ID', 連番値+1 ... )
UPDATE 採番テーブル SET 連番値 = 連番値 + 1
トランザクション COMMIT
すばやいご回答ありがとうございます。
初心者という言葉が免罪符になるとは思っていませんが、なにぶん知識・経験ともに不足しておりますので見当違いの解釈をしているかもしれません。
その際は「頭の悪いやつだなあ」と笑って流してやっていただければ幸いです。
注文テーブルの更新は、通常アプリからの操作によってなされております。
今回は大量発注ということで、1000人以上に同じ操作をアプリ上でするよりもDBに直接追加したほうが早いとクライアントから依頼をいただきました。
今後同様の依頼がある可能性を考慮すると、これまでになかった採番テーブルを新たに追加し、更に採番テーブルを常に最新の状態に保つため、注文テーブルへのINSERT命令発行と同時に採番テーブルを更新するトリガーを組む必要があると思うのですが、これは今回の『注文の追加』という依頼の範囲を若干オーバーしているように思えてなりません。
単に私が臆病なだけかもしれませんが……新入社員が上司に進言しても差し支えない範囲の処理でしょうか?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・【大喜利】【投稿~11/12】 急に朝起こしてきた母親に言われた一言とは?
- ・好きな和訳タイトルを教えてください
- ・うちのカレーにはこれが入ってる!って食材ありますか?
- ・好きな「お肉」は?
- ・あなたは何にトキメキますか?
- ・おすすめのモーニング・朝食メニューを教えて!
- ・「覚え間違い」を教えてください!
- ・とっておきの手土産を教えて
- ・「平成」を感じるもの
- ・秘密基地、どこに作った?
- ・【お題】NEW演歌
- ・カンパ〜イ!←最初の1杯目、なに頼む?
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・チョコミントアイス
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・あなたの習慣について教えてください!!
- ・ハマっている「お菓子」を教えて!
- ・高校三年生の合唱祭で何を歌いましたか?
- ・【大喜利】【投稿~11/1】 存在しそうで存在しないモノマネ芸人の名前を教えてください
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・家の中でのこだわりスペースはどこですか?
- ・つい集めてしまうものはなんですか?
- ・自分のセンスや笑いの好みに影響を受けた作品を教えて
- ・【お題】引っかけ問題(締め切り10月27日(日)23時)
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
シングルクォーテーションとダ...
-
ACCESS で マクロの中でフィ...
-
複雑なinsert文の書き方について
-
ADOでレコードを閉じるタイミン...
-
Access VBAでクエリーのレコー...
-
顧客IDを入力すると顧客名や住...
-
Access 縦(行)のデータを横(列)...
-
Access フォームのデータがテー...
-
Accessにて検索結果の一覧表示...
-
データベースの1要素に複数デー...
-
Access レコードロックについて...
-
Access クエリ このレコードセ...
-
ワードでの単純作業の効率化に...
-
Access 既に開いているフォー...
-
Excel 2019 のピボットテーブル...
-
エクセルVBAで5行目からオート...
-
「直需」の意味を教えてください
-
INSERT INTO ステートメントに...
-
Accessでテーブルの値をテキス...
-
Oracle 2つのDate型の値の差を...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Access VBAでクエリーのレコー...
-
シングルクォーテーションとダ...
-
Access フォームのデータがテー...
-
Access IF文でテーブルに存在し...
-
データベースの1要素に複数デー...
-
ACCESS で マクロの中でフィ...
-
ACCESS2003 Aアクロバットを介...
-
Access 既に開いているフォー...
-
顧客IDを入力すると顧客名や住...
-
ワードでの単純作業の効率化に...
-
Access クエリ このレコードセ...
-
VBAで検索条件式の書き方教えて...
-
テーブル1 2 3 の結合
-
ワードで保存するファイル名の...
-
Accessデータベースで行と列を...
-
access フォーム間で情報が引...
-
Access レコードロックについて...
-
文字化け、記号の含まれるフィ...
-
別のDBからテーブルをコピーす...
-
ACCESS 顧客データ 購入履歴...
おすすめ情報