線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:4298
推到 Plurk!
推到 Facebook!

資料庫與多人訂位問題?為什麼還會訂到同一個位子

答題得分者是:aftcast
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#1 引用回覆 回覆 發表時間:2008-11-17 16:31:48 IP:210.202.xxx.xxx 訂閱
請教各位大大:

小弟試寫了一個訂位系統,為了避免多人使用,我設了一個訂位權利的鑰匙,誰先取得鑰匙,誰就可以先訂位
但多人同時執行的時候,偶爾還是會訂到同一個位子,小弟我這種方法感覺還是有漏洞,這種類似的問題有其他解決的方式嗎?

主程式訂位片段:
[code cpp]
//先看看有沒有人已經正在進行訂位(看別人是否已取得鑰匙)
if Check_Lock('S') then begin
showmessage('目前有人訂位中!!請稍候執行');
exit;
end;

try
fm_MainMenu.ADOConnection1.BeginTrans;
//先取得鑰匙(欄位update成1)
if not Lock_Key('S',1) then begin
exit;
end;

try

//尋找空位(不能自由選擇,完全由系統從第一排空位開始找起到最後一排)
//將該空位設成已訂位

//釋放鑰匙(欄位update成0)
if not Lock_Key('S',0) then begin
exit;
end;

fm_MainMenu.ADOConnection1.CommitTrans;
ShowMessage('訂位成功');

except
on E: Exception do begin
fm_MainMenu.ADOConnection1.RollbackTrans;
ShowMessage('訂位失敗, 異常訊息->' E.Message);
Exit;
end;
end;
finally
if fm_MainMenu.ADOConnection1.InTransaction then
fm_MainMenu.ADOConnection1.RollbackTrans;
end;
[/code]

副程式
[code cpp]
function Check_Lock(S:string): boolean; //判斷該table是否已被鎖住 回傳false:沒鎖 true:鎖
begin
result := false;
try
with fmPublic do
begin
//select [BIN_KEY]
QPub1.SQL.Clear;
if S='S' then
QPub1.SQL.Text:='select BIN_KEY from BIN_KEY'
QPub1.Open;
if S='S' then begin
if QPub1.FieldByName('BIN_KEY').AsInteger=0 then exit;
end;
end;
except
exit;
end;
result := true;
end;

function Lock_Key(S:string ; lock:integer): boolean; //lock BIN_KEY
begin
result := false;
try
with fmPublic do
begin
//update [BIN_KEY]
QPub1.SQL.Clear;
if S='S' then
QPub1.SQL.Text:='update BIN_KEY set BIN_KEY=''' inttostr(lock) ''' '
QPub1.ExecSQL;
end;

except
fm_MainMenu.ADOConnection1.RollbackTrans;
showmessage('系統鎖住資料表 [BIN_KEY] 失敗!!');
exit;
end;
result := true;
end;
[/code]
st33chen
尊榮會員


發表:15
回覆:591
積分:1201
註冊:2005-09-30

發送簡訊給我
#2 引用回覆 回覆 發表時間:2008-11-17 17:58:28 IP:122.116.xxx.xxx 未訂閱
您好,

我用 "delphi read and lock" 去 google 了一下,
篇數還很多, 我沒仔細看, 您參考一下.
------
IS IT WHAT IT IS
我是 李慕白 請倒著唸.
又想把老話拿出來說, 請用台語發音 : 專家專家全是ROBOT CAR (滷肉腳啦);
都已接手這麼久了, 績效還是那麼爛, 講話還那麼大聲.
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#3 引用回覆 回覆 發表時間:2008-11-17 20:07:15 IP:60.248.xxx.xxx 訂閱
請問是什麼資料庫?

多人使用是指同一個軟體在不同的電腦同時使用嗎?
建議不要使用adoconnection裡的transaction,改用資料庫裡的transaction指令或是sql指令來lock table 或是row。
其實還是要先了解db是什麼才比較好解決。

若是用access這類的,那就要靠lock file的技巧來解決了。
------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2008-11-17 20:59:28, 註解 無‧
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#4 引用回覆 回覆 發表時間:2008-11-17 21:36:03 IP:61.223.xxx.xxx 訂閱
 aftcast大大:

我使用的資料庫是Oracle 10g.......
恩~~~多人使用指的是同一套軟體裝在不同電腦同時使用.....
===================引 用 aftcast 文 章===================
請問是什麼資料庫?

多人使用是指同一個軟體在不同的電腦同時使用嗎?
建議不要使用adoconnection裡的transaction,改用資料庫裡的transaction指令或是sql指令來lock table 或是row。
其實還是要先了解db是什麼才比較好解決。

若是用access這類的,那就要靠lock file的技巧來解決了。
編輯記錄
lkkplayer 重新編輯於 2008-11-17 21:37:54, 註解 無‧
herbert2
尊榮會員


發表:58
回覆:640
積分:894
註冊:2004-04-16

發送簡訊給我
#5 引用回覆 回覆 發表時間:2008-11-17 21:54:03 IP:211.72.xxx.xxx 訂閱
感覺上, 您的寫法有幾個問題:
(1) 永遠只有一個 Client (A) 可以訂位, 其他 Client (B....Z) 都須等待.
A 若被旁騖耽擱, B....Z 會等得『起肖』! (高鐵購票系統翻版?)
(2) 當您釋放鑰匙時, Client B....Z 之一便可能馬上取得鑰匙, 而與 A 訂到同一座位.
因 Server 同時與 Client A.....Z 連線, A 一釋放, 其它 Client 便稱隙溜進來了.
(3) 應該比 (2) 先執行, 才可避免一座多賣. 但 (1) 仍是大問題.

===================引 用 lkkplayer 文 章===================
請教各位大大:

小弟試寫了一個訂位系統,為了避免多人使用,我設了一個訂位權利的鑰匙,誰先取得鑰匙,誰就可以先訂位
但多人同時執行的時候,偶爾還是會訂到同一個位子,小弟我這種方法感覺還是有漏洞,這種類似的問題有其他解決的方式嗎?

主程式訂位片段:
[code cpp]
//先看看有沒有人已經正在進行訂位(看別人是否已取得鑰匙)
if Check_Lock('S') then begin
showmessage('目前有人訂位中!!請稍候執行'); // (1) ?
end;

try
fm_MainMenu.ADOConnection1.BeginTrans;
//先取得鑰匙(欄位update成1)
if not Lock_Key('S',1) then begin
exit;
end;

try

//尋找空位(不能自由選擇,完全由系統從第一排空位開始找起到最後一排)
//將該空位設成已訂位

//釋放鑰匙(欄位update成0) (2) ?
if not Lock_Key('S',0) then begin
exit;
end;

fm_MainMenu.ADOConnection1.CommitTrans; // (3) ?
ShowMessage('訂位成功');
except
on E: Exception do begin
fm_MainMenu.ADOConnection1.RollbackTrans;
ShowMessage('訂位失敗, 異常訊息->' E.Message);
Exit;
end;
end;
finally
if fm_MainMenu.ADOConnection1.InTransaction then
fm_MainMenu.ADOConnection1.RollbackTrans;
end;
[/code]

副程式
[code cpp]
function Check_Lock(S:string): boolean; //判斷該table是否已被鎖住 回傳false:沒鎖 true:鎖
begin
result := false;
try
with fmPublic do
begin
//select [BIN_KEY]
QPub1.SQL.Clear;
if S='S' then
QPub1.SQL.Text:='select BIN_KEY from BIN_KEY'
QPub1.Open;
if S='S' then begin
if QPub1.FieldByName('BIN_KEY').AsInteger=0 then exit;
end;
end;
except
exit;
end;
result := true;
end;

function Lock_Key(S:string ; lock:integer): boolean; //lock BIN_KEY
begin
result := false;
try
with fmPublic do
begin
//update [BIN_KEY]
QPub1.SQL.Clear;
if S='S' then
QPub1.SQL.Text:='update BIN_KEY set BIN_KEY=''' inttostr(lock) ''' '
QPub1.ExecSQL;
end;

except
fm_MainMenu.ADOConnection1.RollbackTrans;
showmessage('系統鎖住資料表 [BIN_KEY] 失敗!!');
exit;
end;
result := true;
end;
[/code]
編輯記錄
herbert2 重新編輯於 2008-11-17 21:56:11, 註解 無‧
hotswin
中階會員


發表:72
回覆:92
積分:52
註冊:2003-11-06

發送簡訊給我
#6 引用回覆 回覆 發表時間:2008-11-18 02:06:17 IP:220.134.xxx.xxx 訂閱
我的做法是在主機放一個訂位的順序表
看誰先insert 一筆進來的,優先權歸它
如果房間都被訂滿了,就不在給其他insert
------
xinjier禮品贈品
pceyes
尊榮會員


發表:70
回覆:657
積分:1140
註冊:2003-03-13

發送簡訊給我
#7 引用回覆 回覆 發表時間:2008-11-18 06:41:03 IP:220.141.xxx.xxx 訂閱
在戶政事務所,會有一張紙,顯示新的身分證字號,各櫃台要登記新生兒時,會抄錄並註明姓名,然後劃線槓掉,有時阿公和兒子、媳婦喬不定,登記一半,還會先吵一架,然後回去開會,戶籍員要用立可百將表再塗掉。
比照這方法,另設一個temp Table,含位置表,當要劃位時,先刪掉temp table那位置,先刪先贏,其他人就看不到了,萬一客人放棄訂位,還可以再寫回去,給下一個櫃台訂位。
------
努力會更接近成功
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#8 引用回覆 回覆 發表時間:2008-11-18 12:20:16 IP:59.115.xxx.xxx 訂閱
仔細的看了一下你的程式碼後,發現你的一些觀念不太正確。
我之前在電信公司上班,且兼資料庫管理師職務,處理的資料庫系統非常的大且複雜,所以給你幾個良心的經驗建議。
1/ 你對 transaction的機制不太熟。資料庫transaction本身就有自己lock的機制。所以你自己又用了一個Check_Lock('S')之類的功能等於是脫褲子放屁,而且還讓效能變得很差。因為你的那功能若是正常,則表示同時間內所有的交易都將停止,這樣太過嚴了! 而資料庫本身的transaction的機制則智慧不少,它通常可以讓數個交易一起進行,但又不會錯誤。這是高階資料庫的必備功能,比如說oracle或是mssql這類的,那麼貴就是貴在這裡。所以,你不必在自己設個table然後update…check...等等等的。

2/ Begintrans之前就不用搞Check_Lock之類的東西。緊接著就是select 哪裡有空的,update 某些資料,然後commit。

3/ rollback的用法時機你可能有錯,你似乎認為在begintrans的過程中若有某資料update失敗之類的,會造成expect? 我不是很確定,但正統來說不該是在expect之內rollback。而是要在update的下一行使用 conn.Errors.Count 的值來得知是否有誤,若是回傳大於0則表有錯,那麼就立刻下rollback。以此推下去,每個update的後面都要check一下。
for pseudo code example:
conn.execute('update table set col = 1' , i ); //conn.execute 的用法看下面幾點的說明
if ( i = 0 or conn.Errors.Count > 0 ) than rollback ,ShowMessage('訂位錯誤…'),exit
conn.execute('update table2 set col = 123' , i );
if ( i = 0 or conn.Errors.Count > 0 ) than rollback ,ShowMessage('訂位錯誤…'),exit

4/ 請儘量不要使用ADO「原始」元件以外的元件,哪些哪? TADOQuery之類的…原始的只有幾個常用的 TADoconnection, TADOCommand, TADoDataset,這三個,其他的都是被borland又包起來的元件為了是讓過去使用TQuery?之類的人習慣。這裡元件有時候會無法把原元件的功能全展現,堪至出錯。你寫的下面幾行就是其一

  1. QPub1.SQL.Clear;
  2. if S='S' then
  3. QPub1.SQL.Text:='update BIN_KEY set BIN_KEY=''' inttostr(lock) ''' '
  4. QPub1.ExecSQL;
  5. end;

5/所有在begintrans下的update指令都會自然造成該row 或是table鎖住直到commit,這是一種保護。但是select的部份則不一定會鎖住資料,這必需要看系統的isolate level設為多少,或者你可以自行在ado裡的property設定,但建議不要去動它。不過當你真的都搞不定時,就試一下這行吧cnn1.IsolationLevel = adXactRepeatableRead 它是connection的屬性。

6/ 在begintrans 下面的程式碼裡,一定不能含有任何與user可以互動的程式,比如說問他確定嗎? 因為萬一使用者因故離開,那就卡在哪裡了! 不過也不用太過度的緊張,好的資料庫可以設定timeout的時間,不過,還是別這樣搞就是!

7/ 有些時候update的資料會是0筆,這不算是異常,可能是沒有符合條件之類的,故我都是用conn.Execute(query,i); 來update資料。其中query就像是'update BIN_KEY set BIN_KEY=1' ,而i值就很重要了!! i 值可以告訴你update有沒有成功,當i >0 時表成功的影響幾列資料,0當然就是沒有任何資料被update。
所有的select 或是 update 的指令,通通可以用TAdoconnction.execute( )來完成!



小結一下: 把你的Check_Lock ,Lock_Key 二個function都刪了。但是在begintrans裡關於select 空位,update某資料的後面要加入conn.Errors.Count 的判別,然後rollback。你是否會擔心多個交易搞在一起的混亂情形? 不用擔心!! 資料庫會自己搞定。當然,某些情形下可能要自己調整 isolate level,但這是進階的工作,暫不談。


如果改了還是有問題,那麼就該懷疑你的ado provider是否支持begintrans的功能,我是指provider不是你的ocrale,oracle當然本身是有的!
補充: 使用transaction來解決你的問題是肯定可行的!! 因為是最正統的作法! 但經常的時候也可以選別種模式,比如上篇pceyes說的戶政事務所的故事 ^_^ 也是不錯的做法。

希望你把我每一個條列的內容都深思一下,再去修改程式~

以上所述都是指高階的db才有的,如orcale或mssql之類的。其他如access之類的單機db則不適用!
------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2008-11-18 12:28:27, 註解 無‧
aftcast 重新編輯於 2008-11-18 12:31:08, 註解 無‧
aftcast 重新編輯於 2008-11-18 12:33:12, 註解 無‧
aftcast 重新編輯於 2008-11-18 12:34:43, 註解 無‧
aftcast 重新編輯於 2008-11-18 12:37:45, 註解 無‧
aftcast 重新編輯於 2008-11-18 12:43:20, 註解 無‧
aftcast 重新編輯於 2008-11-18 12:56:56, 註解 無‧
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#9 引用回覆 回覆 發表時間:2008-11-18 18:54:18 IP:210.202.xxx.xxx 訂閱
aftcast大大:

1.假如找到位子(select)與訂位(update)之間又加了一些蠻長的程式碼,最後才訂位(update),會造成找到同一個位子嗎?
2.adXactRepeatableRead是什麼意思?好像還有其他選項說......看起來感覺我應該選Readcommit?使用者只能讀我commit後的東西。
3.看來要好好看有關資料庫的東西。完全不熟......


===================引 用 aftcast 文 章===================
仔細的看了一下你的程式碼後,發現你的一些觀念不太正確。
我之前在電信公司上班,且兼資料庫管理師職務,處理的資料庫系統非常的大且複雜,所以給你幾個良心的經驗建議。
1/ 你對 transaction的機制不太熟。資料庫transaction本身就有自己lock的機制。所以你自己又用了一個Check_Lock('S')之類的功能等於是脫褲子放屁,而且還讓效能變得很差。因為你的那功能若是正常,則表示同時間內所有的交易都將停止,這樣太過嚴了! 而資料庫本身的transaction的機制則智慧不少,它通常可以讓數個交易一起進行,但又不會錯誤。這是高階資料庫的必備功能,比如說oracle或是mssql這類的,那麼貴就是貴在這裡。所以,你不必在自己設個table然後update…check...等等等的。

2/ Begintrans之前就不用搞Check_Lock之類的東西。緊接著就是select 哪裡有空的,update 某些資料,然後commit。

3/ rollback的用法時機你可能有錯,你似乎認為在begintrans的過程中若有某資料update失敗之類的,會造成expect? 我不是很確定,但正統來說不該是在expect之內rollback。而是要在update的下一行使用 conn.Errors.Count 的值來得知是否有誤,若是回傳大於0則表有錯,那麼就立刻下rollback。以此推下去,每個update的後面都要check一下。
for pseudo code example:
conn.execute('update table set col = 1' , i ); //conn.execute 的用法看下面幾點的說明
if ( i = 0 or conn.Errors.Count > 0 ) than rollback ,ShowMessage('訂位錯誤…'),exit
conn.execute('update table2 set col = 123' , i );
if ( i = 0 or conn.Errors.Count > 0 ) than rollback ,ShowMessage('訂位錯誤…'),exit

4/ 請儘量不要使用ADO「原始」元件以外的元件,哪些哪? TADOQuery之類的…原始的只有幾個常用的 TADoconnection, TADOCommand, TADoDataset,這三個,其他的都是被borland又包起來的元件為了是讓過去使用TQuery?之類的人習慣。這裡元件有時候會無法把原元件的功能全展現,堪至出錯。你寫的下面幾行就是其一

  1. QPub1.SQL.Clear;
  2. if S='S' then
  3. QPub1.SQL.Text:='update BIN_KEY set BIN_KEY=''' inttostr(lock) ''' '
  4. QPub1.ExecSQL;
  5. end;

5/所有在begintrans下的update指令都會自然造成該row 或是table鎖住直到commit,這是一種保護。但是select的部份則不一定會鎖住資料,這必需要看系統的isolate level設為多少,或者你可以自行在ado裡的property設定,但建議不要去動它。不過當你真的都搞不定時,就試一下這行吧cnn1.IsolationLevel = adXactRepeatableRead 它是connection的屬性。

6/ 在begintrans 下面的程式碼裡,一定不能含有任何與user可以互動的程式,比如說問他確定嗎? 因為萬一使用者因故離開,那就卡在哪裡了! 不過也不用太過度的緊張,好的資料庫可以設定timeout的時間,不過,還是別這樣搞就是!

7/ 有些時候update的資料會是0筆,這不算是異常,可能是沒有符合條件之類的,故我都是用conn.Execute(query,i); 來update資料。其中query就像是'update BIN_KEY set BIN_KEY=1' ,而i值就很重要了!! i 值可以告訴你update有沒有成功,當i >0 時表成功的影響幾列資料,0當然就是沒有任何資料被update。
所有的select 或是 update 的指令,通通可以用TAdoconnction.execute( )來完成!



小結一下: 把你的Check_Lock ,Lock_Key 二個function都刪了。但是在begintrans裡關於select 空位,update某資料的後面要加入conn.Errors.Count 的判別,然後rollback。你是否會擔心多個交易搞在一起的混亂情形? 不用擔心!! 資料庫會自己搞定。當然,某些情形下可能要自己調整 isolate level,但這是進階的工作,暫不談。


如果改了還是有問題,那麼就該懷疑你的ado provider是否支持begintrans的功能,我是指provider不是你的ocrale,oracle當然本身是有的!
補充: 使用transaction來解決你的問題是肯定可行的!! 因為是最正統的作法! 但經常的時候也可以選別種模式,比如上篇pceyes說的戶政事務所的故事 ^_^ 也是不錯的做法。

希望你把我每一個條列的內容都深思一下,再去修改程式~

以上所述都是指高階的db才有的,如orcale或mssql之類的。其他如access之類的單機db則不適用!
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#10 引用回覆 回覆 發表時間:2008-11-19 01:26:00 IP:59.115.xxx.xxx 訂閱
1/ 把那一堆找位子到訂位的一整段包在transaction的碼貼出來。
2/ adXactRepeatableRead 比Readcommit 層級更嚴(從字面上你會以為Readcommit比較嚴對吧?),很難簡短的說清楚,所以用我說的就是了ok?


===================引 用 lkkplayer 文 章===================
aftcast大大:

1.假如找到位子(select)與訂位(update)之間又加了一些蠻長的程式碼,最後才訂位(update),會造成找到同一個位子嗎?
2.adXactRepeatableRead是什麼意思?好像還有其他選項說......看起來感覺我應該選Readcommit?使用者只能讀我commit後的東西。
3.看來要好好看有關資料庫的東西。完全不熟......



------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#11 引用回覆 回覆 發表時間:2008-11-19 22:02:37 IP:61.223.xxx.xxx 訂閱
aftcast大大:

附上我的程式碼......請您指點一下。

[code cpp]
//找空位
SEAT_NO:=GET_SEAT(StrToInt(ROW_NO),RadioGroup1.ItemIndex);
if SEAT_NO='Error' then
begin
fm_MainMenu.ADOConnection1.RollbackTrans;
ShowMessage('目前無空位');
exit;
end;
ADOQuery1.First;
i:=0;
NAME:='00000000';
while not ADOQuery1.Eof do
begin
if trim(ADOQuery1.FieldByName('NAME').AsString) <> NAME then
begin
i:=i 1;
QTmp2.Close;
QTmp2.SQL.Text:='SELECT NAME,SUM(QTY) AS QTY FROM MENU_INFO_TMP WHERE NAME=''' trim(ADOQuery1.FieldByName('NAME').AsString) ''' GROUP BY NAME ';
QTmp2.Open;
QTmp1.Close;
QTmp1.SQL.Text:='SELECT MAX(CHK_DATE) AS CHK_DATE, MIN(STOREIN_DATE) AS STOREIN_DATE FROM MENU_INFO_TMP WHERE NAME=''' trim(ADOQuery1.FieldByName('NAME').AsString) ''' ';
QTmp1.Open;
QTmp.Close;
QTmp.SQL.Text:='Insert Into SET_DET(SEQ_NO,RECV_TIME,DET_NO,NAME,QTY,STOREIN_DATE,CHK_DATE) '
'Values'
'(''' seq ''','
'TO_DATE(''' sysdate ''',''YYYYMMDDHH24MISS''),'
'''' IntToStr(i) ''','
'''' trim(ADOQuery1.FieldByName('NAME').AsString) ''','
''''','
'''' trim(ADOQuery1.FieldByName('UNITS').AsString) ''','
'''' trim(QTmp2.FieldByName('QTY').AsString) ''','
'TO_DATE(''' FormatDateTime('YYYYMMDD',QTmp1.FieldByName('STOREIN_DATE').AsDateTime) ''',''YYYYMMDD''),'
'TO_DATE(''' FormatDateTime('YYYYMMDD',QTmp1.FieldByName('CHK_DATE').AsDateTime) ''',''YYYYMMDD''))';
QTmp.ExecSQL;
end;
ADOQuery1.Next;
end;
QTmp.Close;
QTmp.SQL.Text:='Insert Into SET_MAS(SEQ_NO,RECV_TIME,SEAT_NO,DET_COUNT,ROW_NO,USER_ID) '
'Values'
'(''' seq ''',TO_DATE(''' sysdate ''',''YYYYMMDDHH24MISS''),'
'''' SEAT_NO ''',''' inttostr(i) ''',''' ROW_NO ''',''' fm_MainMenu.UserName ''')';
QTmp.ExecSQL;
//訂位
QTmp.Close;
QTmp.SQL.Text := 'Update SEAT_BAS Set SEAT_STATE=''M'' Where SEAT_NO=''' SEAT_NO ''' ';
QTmp.ExecSQL;
[/code]
===================引 用 aftcast 文 章===================
1/ 把那一堆找位子到訂位的一整段包在transaction的碼貼出來。
2/ adXactRepeatableRead 比Readcommit 層級更嚴(從字面上你會以為Readcommit比較嚴對吧?),很難簡短的說清楚,所以用我說的就是了ok?


===================引 用 lkkplayer 文 章===================
aftcast大大:

1.假如找到位子(select)與訂位(update)之間又加了一些蠻長的程式碼,最後才訂位(update),會造成找到同一個位子嗎?
2.adXactRepeatableRead是什麼意思?好像還有其他選項說......看起來感覺我應該選Readcommit?使用者只能讀我commit後的東西。
3.看來要好好看有關資料庫的東西。完全不熟......




christie
資深會員


發表:30
回覆:299
積分:475
註冊:2005-03-25

發送簡訊給我
#12 引用回覆 回覆 發表時間:2008-11-20 10:09:32 IP:122.117.xxx.xxx 訂閱
Hi,請試試看!
//找空位
SEAT_NO:=GET_SEAT(StrToInt(ROW_NO), RadioGroup1.ItemIndex);
if SEAT_NO='Error' then
begin
fm_MainMenu.ADOConnection1.RollbackTrans;
ShowMessage('目前無空位');
exit;
end;

// 設一個temp Table: SEAT_BANK 含位置表
Qtmp.SQL.Text:=Format('Delete from SEAT_BANK where SEAT_NO=''%S''',[seat_no]);
Qtmp.ExecSQL;
IF Qtmp.RowsAffected > 0 then
BEGIN
ADOQuery1.First;
i:=0;
NAME:='00000000';
while not ADOQuery1.Eof do
begin
if trim(ADOQuery1.FieldByName('NAME').AsString) <> NAME then
begin
i:=i 1;
QTmp2.Close;
QTmp2.SQL.Text:='SELECT NAME,SUM(QTY) AS QTY FROM MENU_INFO_TMP WHERE NAME=''' trim(ADOQuery1.FieldByName('NAME').AsString) ''' GROUP BY NAME ';
QTmp2.Open;
QTmp1.Close;
QTmp1.SQL.Text:='SELECT MAX(CHK_DATE) AS CHK_DATE, MIN(STOREIN_DATE) AS STOREIN_DATE FROM MENU_INFO_TMP WHERE NAME=''' trim(ADOQuery1.FieldByName('NAME').AsString) ''' ';
QTmp1.Open;
QTmp.Close;
QTmp.SQL.Text:='Insert Into SET_DET(SEQ_NO,RECV_TIME,DET_NO,NAME,QTY,STOREIN_DATE,CHK_DATE) '
'Values' '(''' seq ''','
'TO_DATE(''' sysdate ''',''YYYYMMDDHH24MISS''),'
'''' IntToStr(i) ''','
'''' trim(ADOQuery1.FieldByName('NAME').AsString) ''','
''''','
'''' trim(ADOQuery1.FieldByName('UNITS').AsString) ''','
'''' trim(QTmp2.FieldByName('QTY').AsString) ''','
'TO_DATE(''' FormatDateTime('YYYYMMDD',QTmp1.FieldByName('STOREIN_DATE').AsDateTime) ''',''YYYYMMDD''),'
'TO_DATE(''' FormatDateTime('YYYYMMDD',QTmp1.FieldByName('CHK_DATE').AsDateTime) ''',''YYYYMMDD''))';
QTmp.ExecSQL
end;
ADOQuery1.Next;
end;

QTmp.SQL.Text:='Insert Into SET_MAS(SEQ_NO,RECV_TIME,SEAT_NO,DET_COUNT,ROW_NO,USER_ID) '
'Values'
'(''' seq ''',TO_DATE(''' sysdate ''',''YYYYMMDDHH24MISS''),'
'''' SEAT_NO ''',''' inttostr(i) ''',''' ROW_NO ''',''' fm_MainMenu.UserName ''')';
QTmp.ExecSQL


//訂位
QTmp.SQL.Text := 'Update SEAT_BAS Set SEAT_STATE=''M'' Where SEAT_NO=''' SEAT_NO ''' ';
QTmp.ExecSQL;
END
ELSE
BEGIN
ShowMessage('Allocate SEAT_BANK failed, 請再試一次')
END;
// IF Qtmp.RowsAffected > 0
===================引 用 pceyes 文 章===================
在戶政事務所,會有一張紙,顯示新的身分證字號,各櫃台要登記新生兒時,會抄錄並註明姓名,然後劃線槓掉,有時阿公和兒子、媳婦喬不定,登記一半,還會先吵一架,然後回去開會,戶籍員要用立可百將表再塗掉。
比照這方法,另設一個temp Table,含位置表,當要劃位時,先刪掉temp table那位置,先刪先贏,其他人就看不到了,萬一客人放棄訂位,還可以再寫回去,給下一個櫃台訂位。
------
What do we live for if not to make life less difficult for each other?
編輯記錄
christie 重新編輯於 2008-11-20 10:13:54, 註解 無‧
christie 重新編輯於 2008-11-20 11:25:10, 註解 無‧
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#13 引用回覆 回覆 發表時間:2008-11-21 11:39:49 IP:210.202.xxx.xxx 訂閱
christie大大:

用您的建議測試結果是ok的,我另外也測試aftcast大大的建議結果也ok,
不過我個人認為這個必須以長時間來觀察才准.....目前想採用兩位的混合版......
我就不信還會再發生........在此跟您與aftcast致上我的謝意...^^"
christie
資深會員


發表:30
回覆:299
積分:475
註冊:2005-03-25

發送簡訊給我
#14 引用回覆 回覆 發表時間:2008-11-21 12:42:24 IP:122.117.xxx.xxx 訂閱
可否提供aftcast
的版本 ?
Many Thanks.
===================引 用 lkkplayer 文 章===================
christie大大:

用您的建議測試結果是ok的,我另外也測試aftcast大大的建議結果也ok,
不過我個人認為這個必須以長時間來觀察才准.....目前想採用兩位的混合版......
我就不信還會再發生........在此跟您與aftcast致上我的謝意...^^"
------
What do we live for if not to make life less difficult for each other?
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#15 引用回覆 回覆 發表時間:2008-11-21 13:18:30 IP:210.202.xxx.xxx 訂閱
christie大大:

我只是把connection的屬性手動設成RepeatableRead,並且把我之前Check_Lock的東西都拿掉,其餘的就是您看到的東西了說。
不曉得這個對您有幫助嗎?我沒試的就是每個SQL下命(update),檢測是否有error然後rollback.......

===================引 用 christie 文 章===================
可否提供aftcast
的版本 ?
Many Thanks.
===================引 用 lkkplayer 文 章===================
christie大大:

用您的建議測試結果是ok的,我另外也測試aftcast大大的建議結果也ok,
不過我個人認為這個必須以長時間來觀察才准.....目前想採用兩位的混合版......
我就不信還會再發生........在此跟您與aftcast致上我的謝意...^^"
系統時間:2024-05-09 1:26:14
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!