网络版中的票据号设置问题 |
答題得分者是:malanlk
|
ntjrr
高階會員 發表:240 回覆:312 積分:110 註冊:2005-04-24 發送簡訊給我 |
在上一篇文章中我自己也问错了,我以为是流水号,等到自己真的去操作时才发现,票据号流水号不是一个概念,因为票据号是根据票据产生的,有候一张票据上只有一个项目,有时候有N条项目,那么数据表中的记录条数也就有可能会是一条或者N条了,而这N条记录的票据号是相同的,等做到下一个单据的时候,票据号就等于上一个票据号加1了,我本来在单机版上是做的窗口一打开就取票据号(设了一个ADOQury来查询最大号后再加1)现在在网络版上这样做会有不合理的重号.有一个方法这样办,一开始不取号,到最后要填入的时候查询出最大票据号加1存入。但我还是担心客户机上同时发生查询,还是会查到相同的数字,这个问题如何解决呢?我试着在想,从查询到票据号,到写入时不让别人再查询写入这样行不行呢?当然不是真的不让,而是让别人排队等候,不要有什么错误的提示,因为那等不了多久的,几秒钟而已。这方法如果可行的话,具体如何写代码呢?
------
我的编程起步于ktop,我将永远支持ktop |
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
用 BeginTrans,CommitTrans 就可以避免重複取號情況發生了, 但是要先寫入再查詢 概念如下
GetSerialNoSuccessful := False; TryCount := 0; while (not GetSerialNoSuccessful) and (TryCount因為這段QUERY 是被包在 Transaction 內而且第一道就是 UPDATE, 當同時有數個連線都要UPDATE時, 只有一個連線會搶到UPDATE的權力, 而Transaction 可以保證在 CommitTrans/RollBackTrans 之前, 其他連線都無法UPDATE, 查號的QUERY也在Transaction內, 這樣就不會重號了...... 如果超過MaxTryCount次都沒要到流水號, 就跟使用者說 資料庫忙碌中..... |
ntjrr
高階會員 發表:240 回覆:312 積分:110 註冊:2005-04-24 發送簡訊給我 |
这个是先写后查的一个方法,基本意思我理解了大半,我昨晚想了一下,不知道这样行不行的,再建一个表,里面只有两个字段,一是自动编号,二是任意一内容字段,例如,自动编号为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 發送簡訊給我 |
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 發送簡訊給我 |
|
ntjrr
高階會員 發表:240 回覆:312 積分:110 註冊:2005-04-24 發送簡訊給我 |
因为我没有经验,所以不懂我的想法是不是对,您所说的“用額外的 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 發送簡訊給我 |
引言: 一天可能有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 發送簡訊給我 |
|
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
用額外的 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 發送簡訊給我 |
|
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
|
ntjrr
高階會員 發表:240 回覆:312 積分:110 註冊:2005-04-24 發送簡訊給我 |
|
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |