2ちゃんねる ★スマホ版★ ■掲示板に戻る■ 全部 1- 最新50  

■ このスレッドは過去ログ倉庫に格納されています

ASP+SQL Serverで切実に困ってます。

1 :デフォルトの名無しさん:01/09/14 20:58
自分で本やWebを調べたんですけどわかんなくて・・・
たぶんすっごい基本的なことだと思うんで知ってる人いたらタスケテ。

ASP+MS SQL Serverで作ったWebのサービスに異常発生。
調べてみたらユニークでなきゃいけないIDがかぶってました。
どうやら
1.一番大きいIDを引っ張ってくる。

2.それに+1する

3.+1したIDをつけてINSERT。

っていうところで1→3が終わる前に他のユーザが1の処理を
はじめるとIDがかぶるってことがわかりました。
(ためしに自分で2台同時にボタンおしたらかぶりました)

会社でほかにわかる人がいないので凄い基本っぽい気がする
この問題が解決できません(涙
ロックとかトランザクションって調べたんですけどいまいち意味がわかってない
せいかうまくいきません。
17日の正午までメンテナンス中っていうページに差し替えて
しのいであるんだけどこのままじゃ・・・。

2 :デフォルトの名無しさん:01/09/14 21:14
ADOを使っているとして、

処理1の前に
conn.BeginTrans
して、
処理1-3が成功したら
conn.CommitTrans
どこかで失敗したら
conn.RollbackTrans
する。(connはConnectionオブジェクト)

でもちゃんと勉強した方がいいよ。トランザクションも知らずにDB
アプリを組むのは無謀ですよ。

3 :デフォルトの名無しさん:01/09/14 22:01
こたえてくれてありがとうございます。

上のような感じにやってるんですが、その「失敗したか」
っていうのをCommitTransする前にどうやって判断したらいいかがわからなくて・・・。

4 :1:01/09/14 22:29
CON.BeginTrans()

SQL = "SELECT TOP 1 bk_id FROM book ORDER BY bk_id DESC"
SET RS = CON.Execute(SQL)

If RS.EOF Then
newbk_id = 1
Else
newbk_id = RS("bk_id")+1
End If

SET rsInsert = Server.CreateObject("ADODB.Recordset")
rsInsert.Open "book", CON, 3, 3

rsInsert.AddNew
rsInsert("bk_id") = newbk_id
rsInsert.Update
rsInsert.Close

SQL = "SELECT TOP 1 bk_id FROM book ORDER BY bk_id DESC"
SET RS2 = CON.Execute(SQL)

if RS2("bk_id")+1 = newbk_id then
CON.CommitTrans
else
CON.RollBackTrans
response.redirect "error.asp"
end if

こんな感じなんですがものすごくだめな気がします>オレ
だれかお助けを・・・もちろん土日も会社です(今も)
うっ

5 :デフォルトの名無しさん:01/09/14 23:29
SQLServer初心者だけど検索してみた。
http://www.acc.gr.jp/wwwdata/pinsql/19981228/836.html

本当は、設計が悪いといいたけどそういわれてもこまるでしょう。
bookをテーブルロックしたら副作用が出るかもしれないけど
その辺は全体をみないとわからないからとりあえず。

6 :デフォルトの名無しさん:01/09/15 10:12
レコード作成してる糞処理をストアドにして
Application.Lock
ストアド実行
Application.Unlock
でダメ?俺もASP知らないけど、カウンタのサンプルでApplication.Lockって使ってたよ

7 :<>:01/09/15 15:56
> 会社でほかにわかる人がいないので凄い基本っぽい気がする
> この問題が解決できません(涙
どんな会社だよ。本当か?
教えてもいいけどさ、つか他の人がロジックまで丁寧に書いてくれてる
からそれ以上言わないけどさ、
根本的に、誰も排他制御も知らん会社で仕事してる事が一番の問題
な気がするぞ。転職すれ。

君のコードで言うと
> SET RS2 = CON.Execute(SQL)
これをやったときに、RS2のプロパティかメソッドで何らかエラーが
拾えるはずだが(調べるのメンドイからそこは割愛するが、ワリィ)
その結果をによってCommitTransもしくはRollback
って感じじゃないのかね?

8 :デフォルトの名無しさん:01/09/15 17:26
SQLServerならadLockOptimistic(3)でなくadLockPessimistic(2)でやってみれば?
他がオープン中のときは、処理が終わるまで待たされるようになる。
sSQL = "SELECT TOP 1 bk_id FROM book ORDER BY bk_id DESC"
Call rs.Open("book", CON, 3, 2)
If rs.EOF = True Then
  lNewId = 1
Else
  lNewId = rs("bk_id") + 1
End If
rs.AddNew
rs("bk_id") = lNewId
rs.Update
rs.Close

SQLServerなど信用できんというならば、ファイルロックを使うかな。
排他でオープンしようとするとエラーになるのでOnErrorResumeNextでErr.Number
をチェックして、オープンできるまでぐるぐる繰り返す。

OSのファイル処理など信用できんというならば、、、知らん。

9 : :01/09/15 17:42
1.一番大きいIDを引っ張ってくる。
SELECT MAX(bk_id) FROM book WITH(ROWLOCK)

10 : :01/09/15 18:27
DBの人はセマフォ知らないのか?

11 :1:01/09/15 18:49
Applicationってやつで他の奴が処理中だったら待つように作ってみたんだけど
「10人同時に押したら10人分まつのか!」っていわれてやり直しに・・・。
結局IDがかぶっててもいいように作り変えることになりました。
なんか負けた感が強いです。

12 :デフォルトの名無しさん:01/09/15 18:50
「DBの人」って?
Dドラゴン
Bボール
ですか?クリリンとか

13 :デフォルトの名無しさん:01/09/15 19:17
>>1
上司も上司に言い切られるあなたも逝って良し。
>>10
DBの人というかVBの人かな。俺も良く分からん。
ミューテックスとかセマフォとかVBSから使えるのだろうか。
Application.Lockてのがそれっぽいですが。
MSDNより引用
> <%
>   Application.Lock
>   Application("Counter") = Application("Counter") + 1
>   Application.UnLock
> %>
> Lock すると、他のユーザーからの操作をロックし、
> そのアクセスしたユーザーだけが値を変更でき、
> UnLock すると、ロック状態を解除できるというわけです。
> こうすることで、ユーザによる値の変更を競合なく
> おこなっていくことができるようになるわけですね

14 : :01/09/16 00:33
SQLサーバ側のオートナンバーじゃだめなのかな。
アクセスであるやつ。

15 :デフォルトの名無しさん:01/09/16 03:44
>>14
IDENTITYですな。

>>10
セマフォ? なぜここでセマフォが?
単なるトランザクション処理でしょ。

16 :1:01/09/16 17:05
>8
その方法、ズシリときた!とてもいい感じがします。
次からそれでいこう。

>9
ROWLOCKってのはいつからいつまでLOCKするんですか?
もしかして
SELECT MAX(bk_id)+1 As new_id FROM book WITH(ROWLOCK)
とかして次にすぐInsertすれば解決なのかな?

>13
たしかに・・・。
ASPはじめてへぼいBBSが作れたと思った次の仕事がこれで
ようするにおれじゃまだむり・・・。
上司(というか社長)も自分はできるっていってるけど
ロックもトランザクションもやってなかったから知らないと思う。
(それを言うと知ってるって言う)

Webのデザインの会社なんで他に知ってる人いない・・・
おれがちょっと掲示板ぐらいだったら・・・って言ったら
全部やらされてて辛いっス。

>14
なんかオートナンバーは遅くなるからいかん
っていわれた。
おれはよくわかんない。


なんかIDがダブってもいいように作れって言われてるんですけど
なんかその場しのぎな感じがして嫌です。
UniqなIDを確実に作る処理って絶対必要な気がして。

17 :8=13:01/09/16 23:04
訂正
×Call rs.Open("book", CON, 3, 2)
○Call rs.Open(sSQL, CON, 3, 2)

>>1
IDがダブったらシステム的に話になりません。
オートナンバーは書き込んでみないと何番になったか分からんから、
その値で他のテーブルも更新しようとすると、もう一度読み直さなきゃきゃならんので、
俺的には自分で発番するほうが好き。発番用の管理テーブルを作ったりする場合も有り。

素人でも作れてしまうのが、VBの良いところでもあり、悪いところでもあるかな。
将来、VBが分かるようになってきたからといって、勘違いして複雑なシステムを請け負わないように。

18 :9:01/09/16 23:41
BEGIN TRAN
SELECT MAX(bk_id) As new_id FROM book WITH(UPDLOCK)
...
INSERT INTO book SELECT ... でMAX(bk_id)+1を追加
COMMIT

*COMMITするまでBOOKはSELECT文でさえLOCKされます。

19 :デフォルトの名無しさん:01/09/16 23:45
>>1
SQL Server だと、常套手段はこれ。

1 主キー値に IDENTITY 属性をつけて、SQL サーバ側で採番させる。
2 INSERT 直後に @@IDENTITY で番号を取得。

>>16
IDENTITY は、それほど遅くないですよ。ためしに IDENTITY 有り・無しで
テーブルを作って INSERT の速度を測ってみると良いです。

20 :デフォルトの名無しさん:01/09/16 23:49
SET LOCK_TIMEOUT ミリ病

21 :8=13:01/09/16 23:55
うう、@@IDENTITY知らんかった。
SELECT @@IDENTITY AS 'Identity'
で取れるのか。
素朴な疑問、IDENTITYが桁あふれしたらどうなるの?

22 :デフォルトの名無しさん:01/09/17 00:36
とりあえず、ウェブ用のアクセスカウンタから作ったら?

23 :1:01/09/17 08:10
>18
なるほど。超納得。

>19
おお。その常套手段っての知りたかった。
超基本っぽいことだから普通はどうやるのかなって。
IDENTITY関連調べてみます。

でもどっちがいいのかな。
つーか両方知ってなきゃいけないのか。

それともういっこ微妙に関係ないんですけど
普通、エラーがおきたときの情報なんかはどうするんですか
それ用のテーブルに残すとしたらどんな情報を残しとくんすか、常識的には。

24 :デフォルトの名無しさん:01/09/17 09:26
>>23
私はローカルなファイルに書き出してますね。エラー時には、そもそも DB への
接続が死んでる可能性があるから DB には書きません。

内容は

1 タイムスタンプ
2 エラーを起こしたファイルの名前と行 (Internal Server Error の場合には
 ASPError あたりから拾える)
3 Query String を含んだ URL と POST データ
4 SQL 文を動的に作っている場合には動的に作っている SQL 文の全体、
 ストアドプロシージャ呼び出しの場合には、ストアドプロシージャ名とパ
 ラメタの値

あたりを列挙。後でユーザから報告が上がってきたときに、どこが原因なのか
突き止められる程度に情報を残しておけば良いです。

25 :Error401:01/09/17 09:30
>でもどっちがいいのかな。

IDENTITYは、トランザクションをロールバックしたときに、元の値に
戻るのかな?

だとすれば、どちらも一緒。

違う場合(多分こっちだと思うけど)、

・連番の保証をするなら、SELECTでロックかけ、自前で番号を振る
・連番保証の必要が無い場合は、IDENTITYを使う

という使い分けをします。

>普通、エラーがおきたときの情報なんかはどうするんですか

トランザクションの内部で、自立したトランザクション(外側のトランザクション
に関係なく、コミット・ロールバックできるトランザクション)が使えない場合は、
テーブルにエラー情報を吐き出すことは出来ません。SQL Serverで使えるか
どうかは知りませんけど。

>それ用のテーブルに残すとしたらどんな情報を残しとくんすか、常識的には。

常識的には、自分が知りたいものを残しておきます、フツー(藁

26 :デフォルトの名無しさん:01/09/17 10:56
自分の場合、こういう処理を行う場合には一度キュー
に書いた後テーブルに書くという手段をとっている
のですが、こういう事をするのは少数派なのかな?

27 :2:01/09/17 12:12
これでいいんじゃないの?

CON.BeginTrans()

On Error Goto DBError

SQL = "SELECT MAX(bk_id) max_id FROM book"
SET RS = CON.Execute(SQL)

If RS.EOF Then
newbk_id = 1
Else
newbk_id = RS("max_id")+1
End If

rs.close

Conn.Execute "Insert into book (bk_id) values (' + newbk_id + ")"

CON.CommitTrans
Exit Sub

DBError:
CON.RollBackTrans
response.redirect "error.asp"
end if

28 :デフォルトの名無しさん:01/09/17 13:10
あの、便乗質問なんですけどSQL文を実行してループさしてテーブル形式で出力すると
出力位置が大きく下に出力するんですけど。
なにか心当たりあるひと教えてくれません

29 :デフォルトの名無しさん:01/09/17 13:13
>>27
スバラシイ!
さすが、VB使い!

30 :デフォルトの名無しさん:01/09/17 13:19
>>27
これ以上、恥をさらさないほうがいいよ。馬路で。

31 :デフォルトの名無しさん:01/09/17 13:42
>>27が使ってるサーバはCode Red対策をしていないに一票。
いいかげん、ウザイんだよ。

32 :デフォルトの名無しさん:01/09/17 18:41
>>27
ASP の VBScript には On Error GoTo ないぞ(正確には On Error GoTo 0,
On Error Resume Next だけ使える)。

33 :27:01/09/17 19:12
>>32
そーか、ごめん。
IIS+ASPにはもう何年も触ってないのに適当なこと書いちゃった。

そうすると、この場合のエラー処理はどうするんだろう。On Error Resume Next
しておいて、ConnectionオブジェクトのErrorsプロパティをチェックすれば
できるのかな?

34 :デフォルトの名無しさん:01/09/17 20:25
>>27
つーか、そんな枝葉じゃなくて、根本的なトコロが間違ってるのよ。

35 :デフォルトの名無しさん:01/09/17 20:32
>>27
まじで、痛すぎるよ。
カキコ控えてROMに徹したほうが身のためだよ。

36 :1:01/10/04 18:30
ええとIDENTITYで重複だめにするとエラーに行くんすよね。
先の処理をまっててくれる⇒ROCKにしたい所存。

↑こういう現状です。

>9
で言ってるようにWITH(UPDLOCK)ってのがつかえないんすが・・・

>Error401
調べ中に見つけたんすけどロールバックされたら@@IDENTITYは元に戻らないそうです。

37 :デフォルトの名無しさん:01/10/04 19:11
なぜ、別に一個 1rowだけのMAXナンバーを格納したTableを作らんの?
そして、そのtableに
update dummy set dummy = max + 1 where dummy = max;
としてupdateを発行しないの?他のセッションが同時期におなじupdate文を
発行したなら、dummy = maxのrowは存在しなくなるから変更は行われない。
変更行がなかった場合にはもう一度maxの取得をして変更できるまで繰り返す。
update文の実行はアトミックであることが保証されてるからmax + 1もアトミックな
値が返るぞ。

38 :デフォルトの名無しさん:01/10/04 19:28
>>30 >>31 >>35
人を批判する前に自分の考えを示しな。
27の方が多少なりともえらいぞ。
ちなみにおれはASP使いじゃないからしらん。

39 :Error401:01/10/04 19:51
>>37
うーん、update dummy文が1文1トランザクションだとすると、連番保証が
なくなるので、IDENTITYを使うのと変わらないし、
更新対象のupdate文と同じトランザクションに入れるのなら、
select max(id) + 1と変わらない気がするんですけど・・・。

>>38
えーと、>>27のコードは、本質的に>>1となんら変わりがないので
批判されてるんじゃないかと思います。
解決法はすでに出尽くしてると思うし。

40 :デフォルトの名無しさん:01/10/04 21:08
>>38
ASPとかそうゆう問題じゃないんだよ。
それがわからないお前も逝ってよし。
さすが、Error401氏

41 :27:01/10/05 11:40
27です。あがってたとは知りませんでした。

>>39
すいません。後で気が付いたんですが、その通りですね。

それで、昔同じようなことをしたのを思い出しました。そのときの解決法は
>>37のようにMAX値保存テーブルを作成し、ストアドプロシージャで

1. 現在のMAX値を更新用カーソルで取得する
2. インクリメントしたMAX値をそのカーソルを使用して更新する

を1トランザクションで実行し、成功したら更新したMAX値を返す、という方法を
取りました。

42 :1:01/10/05 12:25
>37 >39 >27
ありがとうございます。なんとなくわかってきたかも。
発番用のテーブルを用意するってのはそういうことだったんすね。13

そんで今わからないのがその重複しちゃ困るIDで同時に
もう1個のテーブルをINSERTまたはUPDATEしてるんですけど
「そのカーソルだと複数にまたがったトランザクションできません」
みたいなエラーがでます。
(ロックタイプ、カーソルは 3.3 です。)

17 KB
■ このスレッドは過去ログ倉庫に格納されています

★スマホ版★ 掲示板に戻る 全部 前100 次100 最新50

read.cgi ver 05.02.02 2014/06/23 Mango Mangüé ★
FOX ★ DSO(Dynamic Shared Object)