こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

オラクルのレコードカウントの制御(改訂版)

プログラム初心者です。
オラクル(11g)で、10個のPCで10人が、あるテーブルのデータを更新していく、システムを作成(VB2005)しています。

画面は2パターンです。
・データ一覧表示画面
・上記からデータを選択し、編集する用の画面

例として下記のようなデータがあります。

テーブルC 日付順 番号はユニーク

日付_番号_ステータス__レコードカウント(Rc)
1/3____1_____Q________0
1/5____2_____R________1
1/6____5_____G________2 (Rc=2)
1/7____7_____P________3 (Rc=3)
1/20__ 9_____R________4
2/1___10_____U________5


動作の流れ

1.まず、この全データをSQLで読込みます。
SELECT * FROM テーブルC ORDER BY 日付 ASC
これが一覧表示画面(VB2005のグリッドビュー等)に表示されます。

2.A氏が例えば1/6のレコードを編集したいとします。
そこで、1/6(日付)の列をマウスで選択。このとき列番号(レコードカウント)は2になります。
編集画面フォームに、同じSQL(SELECT * FROM テーブルC ORDER BY 日付 ASC)
と、列番号(レコードカウント)2を、引渡しします。

3.編集画面で、引き渡されたSQLを実行し、そのデータを全てDatatblに格納します。
そのデータの3つめ(レコードカウント2)を参照します。
datatabl.Rows(2).Item("ステータス")
データは参照後、ある条件によりステータスが変わります。
ステータスが、"Z"になった場合、そのレコードは、テーブルAに移動されます。

4.編集後は、他のテーブルに移動する場合でも、テーブルCがそのまま更新される場合でも、
自動的に次の列のレコードに移行し編集を行う。
今回は例として、1/6のレコードのステータスが"Z"になって
A氏は次に1/7(Rc3)のデータを編集作業に入ります。

5. 4.を実行するため、また同じSQLを実行。
SQL実行後、レコードカウントを次に移行すればいいのですが(Rc=2に1を加え、Rc=3とする)、
Rc=3を見ても、1/20のデータが抽出されて しまいます。 (1/6のレコードが移動してなくなってしまった場合)


また、他の例として、上記と同様に、A氏が1/6のデータを編集中に、他の人が1/3,1/5のデータを編集移動
してしまった場合、レコードカウントがぐちゃぐちゃになってしまいます。

こうした多くの人がデータを編集、更新・移動する場合のテーブルを、日付順に作業させていく場合は、どのような制御
(またはオラクルの機能)をすれば、可能なのかご指導頂ければ助かります。

投稿日時 - 2008-07-01 01:14:21

QNo.4142250

すぐに回答ほしいです

質問者が選んだベストアンサー

とりあえず、なんとかなったようでよかったです。
新しく質問立てて頂いていいかと思います。
(なにぶん、今は仕事ではOracleを使っていませんので、細かいところをすぐ試すわけにはいかないです。もっと早く回答できる人もいるでしょう)
ちなみに、文字列の大小はコードで比較されますが、ASCIIでもUNICODEでも'8'<'A'だと思いますよ。
番号がVARCHAR2で常に10桁(10文字)入っているなら、TO_CHARを使わずにそのまま結合してはだめだったのかな、と思いました。

投稿日時 - 2008-07-03 18:10:55

お礼

なるほど、オラクルで、文字コード加味してくれ、そのままいけるのであれば、とても楽です。
そちらも試してみますね。

本当にありがとうございました!

投稿日時 - 2008-07-03 18:58:07

このQ&Aは役に立ちましたか?

8人が「このQ&Aが役に立った」と投票しています

回答(5)

ANo.4

クエリはOracleが手元にない状態で書いたものだったので、ちょっとひやっとしましたが、構文としては正しかったようです。念のため、11gで確認しましたので、実テーブルに即した内容に書き換えたものを。。

SELECT * FROM T_JOB
WHERE (TO_CHAR(DAY,'yyyyMMdd') || NO) IN
(SELECT MIN(TO_CHAR(DAY,'yyyyMMdd') || NO) FROM T_JOB
WHERE (TO_CHAR(DAY,'yyyyMMdd') || NO)>
(SELECT TO_CHAR(DAY,'yyyyMMdd') || NO FROM T_JOB WHERE NO='<oldNo>'))

日付も一緒にパラメータにできるようなので、以下の方がわかりやすければ。。
1) SELECT * FROM T_JOB
WHERE (TO_CHAR(DAY,'yyyyMMdd') || NO) IN
(SELECT MIN(TO_CHAR(DAY,'yyyyMMdd') || NO) FROM T_JOB
WHERE (TO_CHAR(DAY,'yyyyMMdd') || NO)>'<yyyyMMdd+No>')

2) SELECT * FROM
(SELECT DAY,NO FROM T_JOB
WHERE (TO_CHAR(DAY,'yyyyMMdd') || NO)>'<yyyyMMdd+No>'
ORDER BY DAY,NO)
WHERE ROWNUM=1

ちなみに、クエリの解釈は書かれた内容であっていますよ。

投稿日時 - 2008-07-03 02:26:56

お礼

ありがとうございます。上記参考に動作しました!
他、24時間対応を調べて、値10桁対応(90と110では 90が優先されてしまっていた)も直して下記で一度完成形です。とても勉強になりました。

SELECT * FROM T_JOB WHERE (TO_CHAR(DAY,'YYYYMMDDHH24MISS') || TO_CHAR(NO,'9999999999'))
IN (SELECT MIN(TO_CHAR(DAY,'YYYYMMDDHH24MISS') || TO_CHAR(NO,'9999999999'))
FROM T_JOB WHERE (TO_CHAR(DAY,'YYYYMMDDHH24MISS') || TO_CHAR(NO,'9999999999'))
>
(SELECT TO_CHAR(DAY,'YYYYMMDDHH24MISS') || TO_CHAR(NO,'9999999999')
FROM T_JOB WHERE NO = '" & Noold & "'))

ここでまた新仕様がでてきて、番号(定義は文字列です)の頭(10桁目固定)が、
8の場合、8
9の場合、9
Aの場合、10
Bの場合、11
~~
Yの場合、29
と、認識してほしいという要望がでてしまいました・・・。
8000000002
A000000001
だったら、A000000001を大きい番号で判断したいということですね。(DATABASEは変更してはいけません)

これにより、少々文が長くなってきたので、この辺はVBの変数を使い解析しやすくしたいと思ってます。

これについては大変助かりましたので、また質問を新しく立てたいと思いますが如何でしょうか。
ありがとうございました。

投稿日時 - 2008-07-03 16:46:57

ANo.3

そうですか。ユニークなのは番号だけでしたか。
それであれば、本来は番号だけをキーにし、番号順に処理することにして、次の番号を検索しにいくのがシンプルですが、拝見する限りは日付順に処理したそうですね。
であれば、日付+番号順に処理することとし、次のレコードは
(1)日付が同じで番号が前回編集番号より大きいか、日付が前回編集番号のレコードの日付よりも大きいものを日付・番号順で取り出してROWNUM=1で1件だけ取る
(2)SELECT 番号 FROM テーブルC
WHERE (TO_CHAR([日付],'yyyyMMdd') || [番号]) IN
(select MIN((TO_CHAR([日付],'yyyyMMdd') || [番号])) FROM テーブルC
WHERE (TO_CHAR([日付],'yyyyMMdd') || [番号])
> (SELECT (TO_CHAR([日付],'yyyyMMdd') || [番号]) FROM テーブルC WHERE [番号]=前回編集した番号))
などの方法になるでしょう。

排他制御の仕組みは1つ入れればいいと思います。DBの質問なのにアプリケーションロックをすすめるのもどうかとは思いましたが、DBロックにする場合は慎重に検討してください。

投稿日時 - 2008-07-02 12:09:43

補足

SQL実行してみましたが、勉強不足で構文がうまくできないようです・・・。
とりあえず大きく3つに分けられるとおもいます。

1
SELECT * FROM T_JOB WHERE (TO_CHAR([日付],'yyyyMMdd') || NO)

2
SELECT MIN(TO_CHAR(DAY,'yyyyMMdd') || NO) FROM T_JOB WHERE (TO_CHAR(DAY,'yyyyMMdd') || NO)

3
SELECT (TO_CHAR(DAY,'yyyyMMdd') || NO) FROM T_JOB WHERE NO = '" & NOold & "'"

3だけは通りました。1,2がどこか違うようです。
大きい分け方も違うんでしょうね・・・。WHERE部分の連結がよくわかってないかもしれません。
ちなみに、NO:番号 DAY:日付で、最終的には、テーブルの全てを抽出したいです。(なので頭はSELECT *です。)

投稿日時 - 2008-07-02 19:00:36

お礼

初心者な自分にはなかなか高度なSQLで、解析に時間がかかりました・・・。頑張って解析しました。

(2)のケースですが、これは、(1)と同じことをしているのでしょうか?
要するに、 日付と番号を連結して大小比較実現した感じでしょうか。

日付________番号
2008/01/01__0034 ・・・I
2008/01/01__0036 ・・・II
2008/01/01__0035 ・・・III

これを||で一つの数字に連結 (日付も数字に変換して、日付番号ともに、大小比較できるようにしている)
200801010034
200801010036
200801010035

比較の二つ両方を連結し、結論としては、上記SQLで
200801010035(MINで抽出されたもの) > 2008010100034(前回更新したもの)
↑これが実行されるかんじで、
Iの次にはIIIが抽出されるという感じになるのでしょうか?

投稿日時 - 2008-07-02 17:46:39

ANo.2

悲観的ですか。。アプローチを2つ紹介しますので、ニーズに合う方を考えてみてください。
1)データベースによるロック
トランザクションモードでSELECT文に FOR UPDATE NOWAIT句をつければ、更新ロックがかけられ、そのレコードは解放(COMMIT/ROLLBACK)されるまでは他の人が更新することはできなくなります。すでに他の人が更新ロックを取得していれば、エラーになります。
・選択したデータに対しては確実に他の人の更新を阻止できます。
・他人の状況は刻々と変わりますので、選択できるかどうかはSELECT FOR UPDATEの結果を見ないとわかりません。
・更新してもしなくても確実に解放するようにします
・誰かがデータを選択したまま放置した場合、そのデータはずーっと誰も更新できない状態になります。
2)アプリケーションによるロック
データに更新者を収録するフィールドを設け、選択した場合はこのフィールドをまず更新しにいく方法です。更新できればデータ編集の権利を得たことになります。
・一覧を読み込んだ時点で今誰が作業中かを表示させられます。
 一覧更新ボタンをつけるなど割り切れば、読み込んだときに作業中のものはDBをつつかなくても「選択不可」とできます。
・次に編集するレコードを取得するのがDBロックよりも簡単です。
・まず一度更新しにいくので、手間がかかります。またうまくやらないと不整合(編集していないのに編集中)が生じるリスクはあります。
・更新してもしなくても、編集終了時には更新者をクリアする必要があります。

投稿日時 - 2008-07-02 00:47:09

お礼

ありがとうございます!
とてもわかりやすい解説助かります!
ちなみに、上記はどちらか片方でよいものなのでしょうか?(両方まではやる必要なし?!)


日付で一番近いものを順に抽出するように、以下のようにSQLを組み替えました。

SELECT 番号 FROM テーブルC WHERE 日付
IN ( SELECT MIN(日付) FROM テーブルC WHERE 日付 >= 前回編集した日付 )

主キーは番号です。

日付 番号
2008/1/1 07
2008/1/5 09
2008/1/5 04
2008/1/5 03
2008/1/5 05

この場合更新後、次のデータに行った場合、番号09の後に、また09を抽出してしまいます。
番号09の値を二度抽出しないようにするには、番号の値を保持しておかなければ行けないでしょうか?なにか他の方法がありましたらご教授お願いしたいです。

投稿日時 - 2008-07-02 11:20:30

ANo.1

全部はコメントしきれませんが、毎回読み込むたびに変わる可能性のあるもの(=Rc)をパラメータとして渡そうとしているところをまず改めてみてはどうでしょうか。
「番号はユニーク」と書いてありますし、日付もユニークならば、番号または日付をパラメータにすべきです。編集を行う行が特定されれば、編集画面ではそのデータだけを読み込めばいいわけです。
更新後の次の行を自動的に割り出したければ、その時点で存在する次の日付のデータの番号を求めて、その番号のものを読み込めばよいです。
SELECT 番号 FROM テーブルC
WHERE 日付 IN
(SELECT MIN(日付) FROM テーブルC
WHERE 日付>[今編集した日付])

なお、今回の情報だけではどこまでロックを掛けたいのか判断できませんでした。一覧に読み込んだものが選択できることを保証するのか、選択したものを更新できることを保証するのか、更新の競合も許すのかで対応は変わってくるはずです。

投稿日時 - 2008-07-01 02:21:51

お礼

ありがとうございます。大変勉強になりました。
その方法、SQLで試してみようと思います。
また、ロックもいまいち不明部分があるのですが、現状のやりたいことは、

一覧画面では、他の人が開いているレコードは、選択できないようにする。
編集画面でも、他の人が開いているレコードは、更新できないようにする。

ただ一覧画面で、A氏B氏が同時に、同レコードを選択した場合、編集画面にいってしまう気がします。
その際、編集画面での更新は早いもの勝ちというようにしたいです。(後の人は更新不可能)

投稿日時 - 2008-07-01 11:55:38

あなたにオススメの質問