この春プログラミング業界に就職しました新入社員です。
皆様のお知恵を拝借したく、質問させていただきました。

先日、以下のような処理を任されました。

・顧客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する辺りの処理が探し当てられず、
ここで質問させていただいた次第です。
よい方法をご存知の方がおられましたら、ご教授いただけましたら幸いです。

このQ&Aに関連する最新のQ&A

A 回答 (4件)

指摘したことの繰り返しになりますが、ユーザーA,Bがいてほぼ同時に作られたSQLを実行した場合、後続のSQLは先行のSQLの終了を待ちません。

すなまち、同じMAX値を取得します。

作られたSQLシングルユーザーで間をおいて実行している限りは正常に動くでしょう。しかし、マルチユーザーで負荷が高く鳴った時はエラーを起こす可能性をもっています。潜在的バグですね。
    • good
    • 0
この回答へのお礼

お礼が遅くなりました。
基本的にDBの更新は誰もアクセスしていないことを確認した上で行いますので、とりあえずは大丈夫なようです。
けれど、覚えておくべきことですね。ありがとうございました。

お礼日時:2011/05/10 06:41

トランザクション処理をする場合、


INSERT INTO ... SELECT MAX(注文NO) FROM 注文テーブル
が同時実行されない保証はありません。
そうすると、後続のINSERT文は重複キーエラーを起こしてしまいます。
保証しようすれば、注文テーブルをテーブル単位にロックをかける必要がありますから却ってパフォーマンスが劣化します。
現在のRDBMSはほとんど行レベルロック機能を持っていますからロック粒度を小さくして同時実行性を増やしたほうが全体のスループットが増すと思います。
    • good
    • 0
この回答へのお礼

二度目のご回答ありがとうございます。
あの後、更に調べを進めまして、どうにか実行可能な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様からご覧になられて不適切な箇所がありましたら、ご教授いただけますでしょうか。

何度もお手間を取らせてしまい、大変心苦しいのですが、ご検討いただけましたら幸いです。

お礼日時:2011/04/26 10:27

自分も深くは理解していないため、


間違っているかもしれませんが、

INSERT INTO
 注文テーブル
( 顧客ID
 ,注文回数
 ,商品NO
 ,処理日時
) VALUES (
 'hoge1'
 ,(SELECT
   MAX(注文回数)
  FROM
   注文テーブル
  WHERE
   顧客ID = 'hoge1'
  GROUP BY
   顧客ID) + 1
 ,'hoge2'
,GETDATE()
);

こういった感じに書いて、hoge1とhoge2を指定すれば
一度で最大数+1を取得してInsertができると思います。

テストはしていないので、
間違っていたら済みません。
    • good
    • 0
この回答へのお礼

すばやいご回答ありがとうございます。
初心者という言葉が免罪符になるとは思っていませんが、なにぶん知識・経験ともに不足しておりますので見当違いの解釈をしているかもしれません。
その際は「頭の悪いやつだなあ」と笑って流してやっていただければ幸いです。

挙げていただいたSQL文について、hoge2に関しては単一の商品NOですので直接指定できます。
問題はhoge1のほうでして、対象となる顧客IDの羅列から、一つずつ取り出してhoge1に指定し、一気に実行する方法がわからないのです……。

お礼日時:2011/04/25 16:40

顧客IDごとに採番レコードを持つ採番テーブルを使用するのはどうですか。



トランザクション開始
SELECT 連番値 FROM 採番テーブル WHERE 顧客ID='該当顧客ID' FOR UPDATE
INSERT INTO 注文テーブル VALUES ( '該当顧客ID', 連番値+1 ... )
UPDATE 採番テーブル SET 連番値 = 連番値 + 1
トランザクション COMMIT
    • good
    • 0
この回答へのお礼

すばやいご回答ありがとうございます。
初心者という言葉が免罪符になるとは思っていませんが、なにぶん知識・経験ともに不足しておりますので見当違いの解釈をしているかもしれません。
その際は「頭の悪いやつだなあ」と笑って流してやっていただければ幸いです。

注文テーブルの更新は、通常アプリからの操作によってなされております。
今回は大量発注ということで、1000人以上に同じ操作をアプリ上でするよりもDBに直接追加したほうが早いとクライアントから依頼をいただきました。
今後同様の依頼がある可能性を考慮すると、これまでになかった採番テーブルを新たに追加し、更に採番テーブルを常に最新の状態に保つため、注文テーブルへのINSERT命令発行と同時に採番テーブルを更新するトリガーを組む必要があると思うのですが、これは今回の『注文の追加』という依頼の範囲を若干オーバーしているように思えてなりません。
単に私が臆病なだけかもしれませんが……新入社員が上司に進言しても差し支えない範囲の処理でしょうか?

お礼日時:2011/04/25 16:21

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

関連するカテゴリからQ&Aを探す

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

QINSERT時に発番を行いたい

ATBLからBTBLへのINSERT時発番を行いたい

<環境>
SQLSERVER 2008

<ATBL>
CDvarchar(3)KEY
NOintKEY
NAMEvarchar(10)

<BTBL>
CDvarchar(3)KEY
NOintKEY
NAMEvarchar(10)

<BTBLのデータ>
CD   NO  NAME
0011太郎
0013次郎
0015三郎

BTBLをATBLへINSERTしたいのですが、この時、NOを1から再付番したいのです。
INSERTクエリで一発で更新できますでしょうか?
ご教授お願いします。

Aベストアンサー

row_numberで1からの連番ふれるよ

INSERT INTO ATBL(CD, NO, NAME)
SELECT CD, row_number() OVER(ORDER BY CD) AS NO, NAME FROM BTBL

参考URL:http://msdn.microsoft.com/ja-jp/library/ms186734.aspx

QINSERT時にデータ登録とmaxの発番がしたい

<環境>
SQLSERVER 2012

入力フォームに、入力した後で、DBに登録した際に、
依頼Noに、既にあるデータのMAX+100の値を登録したいです。

依頼NoにMAX+100の連番をふることは以下の方法でできたのですが、

INSERT INTO テーブル1(依頼No)
SELECT MAX(依頼No)+100 AS NEW_ID FROM テーブル1



入力フォームのデータと登録と同時に、依頼Noを振りたいのですができません。
以下のように書いてみましたが、
根本的に間違っていると思うので、いい方法をご教授いただけたらと思います。


※iraibi は入力フォームで、依頼日を入力した値です。

INSERT INTO テーブル1 (依頼No,依頼日) VALUES ('SELECT MAX(依頼書No)+100 AS NEW_ID FROM テーブル1','" & iraibi & "')

宜しくお願いいたします。

Aベストアンサー

INSERT INTO テーブル1(依頼No, 依頼日)
SELECT MAX(依頼No)+100, '2013/05/13' FROM テーブル1

QMAX値を条件にデータを取得するには?

SQL文で困っています。
ご教授下さい。


下記のようなデータがあった場合、それぞれの区分毎に
年月が最大(最新)のデータを取得したいです。
(実際には1レコードにその他項目があり、それらも取得します。)
<検索対象データ>
区分 年月   金額
-----------------------------
A   200412  600
A   200503  560
B   200311  600
B   200508  1000
B   200504  560
C   200508  400
C   200301  1100


<取得したいデータ>

区分 年月   金額
-----------------------------
A   200503  560
B   200508  1000
C   200508  400

よろしくお願いします。

Aベストアンサー

テーブル名をXXXとすると次のようなSQLでよいと思います。(最善の方法かどうかは自信がないですが)

select B.* from (select 区分, max(年月) as 年月 from XXX group by 区分) As A
inner join XXX as B on A.区分 = B.区分 and A.年月 = B.年月
order by B.区分

QSQLで特定の項目の重複のみを排除した全項目を取得する方法

私は仕事上でデータベースを扱っていて、タイトルのような処理を行う必要があるのですが、いかんせん方法がわからずネット上を検索しても同様だったためここで質問させていただきます。

質問点を簡単に説明いたしますと、
たとえばAというテーブルがあって、

項目名1 項目名2 項目名3 項目名4
 A    あ    ア    亜
 A    い    ア    以
 A    う    ア    宇
 B    え    イ    江
 B    お    イ    尾

上のような構造になっている場合に「項目名1」について重複している項目を排除し、結果として


項目名1 項目名2 項目名3 項目名4
 A    あ    ア    亜
 B    え    イ    江

上のようなデータを取得したいのです。
この時に、Aの重複を排除して取得するレコードは1~3行目のどれでもよいです。
また、データを取得する際には必ずそのレコードの「全項目」を取得したいのでDistinctはうまく使えませんでした。

どなたか詳しい方、方法を教えてくださると幸いです。回答お待ちしております。

私は仕事上でデータベースを扱っていて、タイトルのような処理を行う必要があるのですが、いかんせん方法がわからずネット上を検索しても同様だったためここで質問させていただきます。

質問点を簡単に説明いたしますと、
たとえばAというテーブルがあって、

項目名1 項目名2 項目名3 項目名4
 A    あ    ア    亜
 A    い    ア    以
 A    う    ア    宇
 B    え    イ    江
 B    お    イ    尾

上のよ...続きを読む

Aベストアンサー

比較可能で一意性のある値をもてる項目6をテーブルに追加して、

select T.* from T, (select Item1,min(Item6) as Item6 from T group by item1) W where T.item6=W.item6;

――ってやるのが、一番手っ取り早いと思います。
他のところに影響がでないのであればですが。
oracleならrowidを使うとか、レコードの更新時刻を突っ込むとか。

Q3つの表の外部結合

表A、B、Cの3つがあり、Aのすべての行を出力したいと考えています。
外部結合を用いるのだとは思うのですが、3つの表に対して行う場合の
書き方がわからず困っています。
ご教授いただけないでしょうか?
select * from a,b,c
where a.商品ID =b.商品ID (+) and b.商品ID (+) =c.商品ID (+)
としてみましたが、うまくいきませんでした。

Aベストアンサー

ansi構文の趣旨からいえば、結合条件と絞り込み条件は分けて書くので・・

select *
from a
left join b on (a.商品ID =b.商品ID)
left join c on (b.商品ID =c.商品ID)
where a.年月 = 任意の値

と書くのが一般的でしょうね。

QSQL 複数テーブルのupdate

こんばんは。
複数テーブルの複数カラムをupdateしたいのですが、
うまくいかず困っています。
どなたか助けてください>_<

テーブルA(tableA)のoptionAというカラムと、
テーブルB(tableB)のoptionBというカラムを両方更新したいんです。

やりたい内容のイメージとしてはこんな感じです↓
update tableA a, tableB b
set a.optionA='OK', b.optionB='OK'
where a.student_id=b.student_id and a.name='山田';

どなたかご指導お願いいたします。

Aベストアンサー

where条件に別テーブルの結合を必要とするので、同時に更新したい・・・
ということならば、以下のようなupdate文を2回発行ではだめなんでしょうか?

update tableB b
set b.optionB='OK'
where exists(
select * from tableA a
where a.student_id=b.student_id and a.name='山田'
);

update tableA a
set a.optionA='OK'
where a.name='山田';

Qエクセルで特定の列にある日付データの中から、指定した日付範囲を抽出する方法を教えてください

例えばこんなデータなのですが、

2002/07/01 99 25 36
2002/07/05 55 33 25
2002/08/01 80 20 51

日付の入っている列を検索して、2002/07/01から2002/07/31
の範囲ならば、その行のデータを別のシートに抽出したいのですが、
日付の範囲は関数でどうすればよいのか分かりません。ACCESSなら
between 9999/99/99 and 9999/99/99って感じで簡単にできるの
ですが。。。エクセルの関数だとIF関数の論理式にAND関数を使って
>=2002/07/01
<=2002/07/31
と入力したので、2002/07/05ならば、いずれもTRUEなので、値を返して
くれると考えたのですが、なぜかダメでした。
(↓こういう入力をしました)
=IF(AND(Sheet2!A1>=2002/7/1,Sheet2!A1<=2002/7/31),Sheet2!A1,"該当月ありません")

Aベストアンサー

こんばんは!

=IF(AND(Sheet2!A1>=2002/7/1,Sheet2!A1<=2002/7/31),Sheet2!A1,"該当月ありません")

おしいですね(^^;
日付をそのまま入力してしまうと数式上では
 2002/7/31 → 2002÷7÷31 になってしまいます。
ダブルクォーテーションで括って(文字列)入力して
それを数値化しましょう!

=IF(AND(Sheet2!A1>="2002/7/1"*1,Sheet2!A1<="2002/7/31"*1),Sheet2!A1,"該当月ありません")

文字列 ="10" に対して 乗算 *1 としても数式が
なり立ち文字列の数字が数値化されます。="10"*1

------------余談--------------------------
ご質問の条件例の場合特例で 2002/7でればOKなので

=IF(TEXT(Sheet2!A1,"yyyym")="20027",Sheet2!A1,"該当月ありません")

でも可能ですね!

こんばんは!

=IF(AND(Sheet2!A1>=2002/7/1,Sheet2!A1<=2002/7/31),Sheet2!A1,"該当月ありません")

おしいですね(^^;
日付をそのまま入力してしまうと数式上では
 2002/7/31 → 2002÷7÷31 になってしまいます。
ダブルクォーテーションで括って(文字列)入力して
それを数値化しましょう!

=IF(AND(Sheet2!A1>="2002/7/1"*1,Sheet2!A1<="2002/7/31"*1),Sheet2!A1,"該当月ありません")

文字列 ="10" に対して 乗算 *1 としても数式が
なり立ち文字列の数字が数値化されます。="10"*1

--...続きを読む

Q