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

网络版中的票据号设置问题

答題得分者是:malanlk
ntjrr
高階會員


發表:240
回覆:312
積分:110
註冊:2005-04-24

發送簡訊給我
#1 引用回覆 回覆 發表時間:2005-08-21 19:00:47 IP:222.184.xxx.xxx 未訂閱
在上一篇文章中我自己也问错了,我以为是流水号,等到自己真的去操作时才发现,票据号流水号不是一个概念,因为票据号是根据票据产生的,有候一张票据上只有一个项目,有时候有N条项目,那么数据表中的记录条数也就有可能会是一条或者N条了,而这N条记录的票据号是相同的,等做到下一个单据的时候,票据号就等于上一个票据号加1了,我本来在单机版上是做的窗口一打开就取票据号(设了一个ADOQury来查询最大号后再加1)现在在网络版上这样做会有不合理的重号.有一个方法这样办,一开始不取号,到最后要填入的时候查询出最大票据号加1存入。但我还是担心客户机上同时发生查询,还是会查到相同的数字,这个问题如何解决呢?我试着在想,从查询到票据号,到写入时不让别人再查询写入这样行不行呢?当然不是真的不让,而是让别人排队等候,不要有什么错误的提示,因为那等不了多久的,几秒钟而已。这方法如果可行的话,具体如何写代码呢?
------
我的编程起步于ktop,我将永远支持ktop
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#2 引用回覆 回覆 發表時間:2005-08-22 00:18:40 IP:61.219.xxx.xxx 未訂閱
用 BeginTrans,CommitTrans 就可以避免重複取號情況發生了, 但是要先寫入再查詢    概念如下
GetSerialNoSuccessful := False;
TryCount := 0;
while (not GetSerialNoSuccessful) and (TryCountBeginTrans;
  try
    UPDATE〈TABLE〉SET serial_no=MAX(serial_no)+1 WHERE....
    SELECT MAX(serial_no) FROM〈TABLE〉WHERE.... 
    CommitTrans;
    GetSerialNoSuccessful := True;
  except
    RollBackTrans;
    inc(TryCount);
    Sleep(50);
  end;
end;
因為這段QUERY 是被包在 Transaction 內而且第一道就是 UPDATE, 當同時有數個連線都要UPDATE時, 只有一個連線會搶到UPDATE的權力, 而Transaction 可以保證在 CommitTrans/RollBackTrans 之前, 其他連線都無法UPDATE, 查號的QUERY也在Transaction內, 這樣就不會重號了...... 如果超過MaxTryCount次都沒要到流水號, 就跟使用者說 資料庫忙碌中.....
ntjrr
高階會員


發表:240
回覆:312
積分:110
註冊:2005-04-24

發送簡訊給我
#3 引用回覆 回覆 發表時間:2005-08-22 18:15:50 IP:222.184.xxx.xxx 未訂閱
这个是先写后查的一个方法,基本意思我理解了大半,我昨晚想了一下,不知道这样行不行的,再建一个表,里面只有两个字段,一是自动编号,二是任意一内容字段,例如,自动编号为1,另个栏位名叫查询编号,内容也叫“查询编号”就是了,我在另一个收费记录库里录入记录时,先查询这个表的自动编号字段(就等于是票据号),然后清空这条记录,再写入一条记录。就完成操作。目的,利用自动编号永远不会重复的特性,取得票据号,删除本条记录,再写入内容,自动编号为加1,以便下一次再取。优点,数据表中永远只有一条记录,查询及写入很快。不知道这个方法有没有什么情况下会出错? 大概的代码我先写一下,当然不一定对:
 
begin
  BeginTrans;
try
  adotable1.open;
  adotable1.frist;
  票据号:=adotable1.findbyname('id').asingeter;
 adotable1.delete;
adotable1.append;
adotalbe1.intest;
adotable1.findbyname('编号查询').asstring:='xxx';
adotable1.post
CommitTrans;
except
    RollBackTrans;
end;     
------
我的编程起步于ktop,我将永远支持ktop
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#4 引用回覆 回覆 發表時間:2005-08-22 19:05:02 IP:203.69.xxx.xxx 未訂閱
BeginTrans 並不一定排除讀取的部份(要看連線時的設定(IsolationLevel)), 所以先讀後寫也可能會發生重複讀到同ㄧ個號碼, 要到同時刪除時才會有一個被踢掉, 但是... 也有可能都順利刪完... A讀到編號10 B讀到編號10 A刪除 A新增(編號-->11) A Commit B刪除 B新增(編號-->12) B Commit 用你編號 Table 不需要刪除也是可以達到相同的效果, 先寫後讀, 寫的時候用 UPDATE〈TABLE〉SET serial_no=serial_no+1 就可以了, 這樣也是永遠只有ㄧ筆
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#5 引用回覆 回覆 發表時間:2005-08-22 19:17:52 IP:203.69.xxx.xxx 未訂閱
用額外的 Table 來記編號本來就是比較好的作法, 也比較有彈性 用兩個欄位, SerialNo, YearMonth 紀錄每月最大編號(也可以改成每週), 在Update 時只要加ㄧ個Where 條件就可以了. 發表人 - malanlk 於 2005/08/22 19:20:42
ntjrr
高階會員


發表:240
回覆:312
積分:110
註冊:2005-04-24

發送簡訊給我
#6 引用回覆 回覆 發表時間:2005-08-22 20:41:47 IP:222.184.xxx.xxx 未訂閱
因为我没有经验,所以不懂我的想法是不是对,您所说的“用額外的 Table 來記編號本來就是比較好的作法, 也比較有彈性”这样就确定了我的想法是正确的,我下面就按这个思路做了。您另外所说的在新建的表中先写后读,也是我想过的,本来我也想过,反正读写顺序都不影响取得票据号这个方法,但您所说的先写更适合与在事务中抢先锁定数据,确保数据的唯一与正确性了。您所说的按周的月的意思我没能理解,因为票据是按每个病人的一次收费为准的,也就是一张票据对应着病人的一张处方。一天可能有N张处方的,我能没理解和周或者月的最大号有什么关系。 经过您的指点,我的初步做法如下:  
 
var
GetSerialNoSuccessful:Boolean;
TryCount:integer ;
begin
GetSerialNoSuccessful := False;
TryCount := 0;
while (not GetSerialNoSuccessful) and (TryCount<5) do
begin
 datamoduleform.ADOConnection1.BeginTrans;
  try
    adoquery1.close;
  adoquery1.sql.clear;
  adoquery1.sql.add(' UPDATE tb_pjh SET pjh=pjh 1 ') ;
  adoquery1.execsql;
     datamoduleform.ADOConnection1.CommitTrans;
    GetSerialNoSuccessful := True;
  except
     datamoduleform.ADOConnection1.RollBackTrans;
    inc(TryCount);//这句没能理解意思,是不是写下试验的次数,如果大于5后就不等了?
    Sleep(50);//这句是不是等待下一次尝试开始的间隔时间?单位是千分之一秒?
  end;
end;
end;
上面的代码运行成功了,票据号能每次修改成了,那么我要把这个票据号查询出来的话,是把SQL语句写在一起吗,写在一起的话,那么最后用OPEN还是用EXECSQL呢,不写在一起的话,是不是在EXECDQL后再执行一次,ADOQEURY1.close adoquery1.sql.clear sql.add(.....) sql.open? 發表人 - ntjrr 於 2005/08/22 21:38:48
------
我的编程起步于ktop,我将永远支持ktop
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#7 引用回覆 回覆 發表時間:2005-08-23 00:31:01 IP:61.219.xxx.xxx 未訂閱
引言: 一天可能有N张处方的,我能没理解和周或者月的最大号有什么关系..
流水號也可以有格式的, 01234567 是一種, 2005-06-1234 也是一種, 但是後者標示出了年月, 如果要查歷史資料之類的 這就是一種簡單明瞭的 Key...
var
  GetSerialNoSuccessful:Boolean;
  TryCount:integer ;
begin
  GetSerialNoSuccessful := False;
  TryCount := 0;
  while (not GetSerialNoSuccessful) and (TryCount<5) do
  begin
    datamoduleform.ADOConnection1.BeginTrans;
    try
      adoquery1.close;
      adoquery1.sql.clear;
      adoquery1.sql.add('UPDATE tb_pjh SET pjh=pjh 1') ;
      adoquery1.execsql;
      
      adoquery1.sql.clear;
      adoquery1.sql.add('SELECT pjh FROM tb_pjh') ;
      adoquery1.execsql;
      //因為只有ㄧ筆, 應該可以直接取值
      result := adoquery1.Fields[0].AsInteger;
      //用 Open 比較正統, 也可以檢查是否有取到資料
      //adoquery1.Open;
      //if adoquery1.RecordCount<>1 then
      //  result := adoquery1.Fields[0].AsInteger
      //else
      //  出狀況了...不過既然有 except 就跳過去再試吧
      datamoduleform.ADOConnection1.CommitTrans;
      GetSerialNoSuccessful := True;
    except
      datamoduleform.ADOConnection1.RollBackTrans;
      inc(TryCount); 
      //inc(X) 是函數 相當於 X := X+1; 
      //就是累積失敗重試次數, 會走到這裡通常都是Update 碰上了
      Sleep(50); // 等0.05秒再試 
    end;
  end;
end;
ntjrr
高階會員


發表:240
回覆:312
積分:110
註冊:2005-04-24

發送簡訊給我
#8 引用回覆 回覆 發表時間:2005-08-23 06:59:54 IP:222.184.xxx.xxx 未訂閱
“流水号也是可以加格式的”我正好要问这一个问题,如何加呢?例如第一种格式00000001,第二种格式年月日加号码。
------
我的编程起步于ktop,我将永远支持ktop
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#9 引用回覆 回覆 發表時間:2005-08-23 07:45:08 IP:203.69.xxx.xxx 未訂閱
用額外的 Table 來記編號本來就是比較好的作法, 也比較有彈性 用兩個欄位, SerialNo, YearMonth 加 WHERE 條件 adoquery1.sql.add('UPDATE tb_pjh SET pjh=pjh 1 WHERE YearMonth="200508"') ; adoquery1.execsql; .... adoquery1.sql.clear; adoquery1.sql.add('SELECT pjh, YeraMonth FROM tb_pjh WHERE YearMonth="200508"') ;
流水號就是 Format('%s%d',[adoquery1.Fields[1].AsString,adoquery1.Fields[0].AsInteger]);
程式啟動時檢查 SELECT pjh FROM tb_pjh WHERE YearMonth="200508" 如果 RecordCount=0 就 INSERT INTO tb_pjh (pjh, YearMonth) VALUES (0,"2005-08") 第一種流水號 FormatFloat('0000000000',pjh); 發表人 - malanlk 於 2005/08/23 07:59:10
ntjrr
高階會員


發表:240
回覆:312
積分:110
註冊:2005-04-24

發送簡訊給我
#10 引用回覆 回覆 發表時間:2005-08-23 09:23:07 IP:222.184.xxx.xxx 未訂閱
这个yearmonth现在为200508,应该是动态获得当前月吧?动态获得的话代码加在哪一段呢
------
我的编程起步于ktop,我将永远支持ktop
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#11 引用回覆 回覆 發表時間:2005-08-23 09:58:39 IP:203.69.xxx.xxx 未訂閱
設一個 變數 sYearMonth: String, 當按下 [新增單據]按鈕時就可以: sYearMonth := FormatDateTime('yyyyMM',Date);
ntjrr
高階會員


發表:240
回覆:312
積分:110
註冊:2005-04-24

發送簡訊給我
#12 引用回覆 回覆 發表時間:2005-08-23 12:02:29 IP:222.184.xxx.xxx 未訂閱
用了format后是不是基本上数据的类型就是字符型的了吧?我要把票据号字段改成字符型吧,如果改成字符型后,在查询上还是一样的吧?比如我要查询00001到00005的票据号,还是可以的吧?
------
我的编程起步于ktop,我将永远支持ktop
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#13 引用回覆 回覆 發表時間:2005-08-23 13:08:59 IP:203.69.xxx.xxx 未訂閱
tb_pjh 這個 Table 的流水號還是整數 其他就改成字符段吧... 查詢時再組就可以了.
系統時間:2024-06-25 13:58:00
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!