問題提出自項目中的老代碼:一個Bill表,存儲所有的表單信息,比如:員工入職單,離職單等等。(別噴,我知道要分多個表。但領導的意願你是沒辦法違背的)表單的單據號是以四個字母+年月日+數字順序號來表示。每次取新單據號時要從Bill表里(按生成規則)查詢出最大的那個單據號,再拆分出來,再給順序號加1,組合好后再寫回。哈哈這就是老代碼。
隨着軟件行業的進步,各種技巧層出不窮。而針對順序號生成的方法也有好巨大改進。
[Oracle]仿Oracle Sequence的自定義年份Sequence(適合任何數據庫)
這里其中的一篇。看過這個之后就想自己動手也寫一個。於是:
順序號表
1 --id序列表 2 create table SEQUENCES 3 ( 4 id VARCHAR2(20) not NULL PRIMARY KEY,--標識 5 minvalue NUMBER default 1,--最小值 6 maxvalue NUMBER default 9999999999999999999999999999,--最大值 7 currentvalue NUMBER DEFAULT 1,--當前值 8 increaseby NUMBER default 1,--增量 9 CYCLE CHAR(1) default '0' --是否循環 10 )
生成函數
1 --獲取 select NextValue('abc') from dual; 2 create or replace function NextValue(arg varchar2) return number IS 3 PRAGMA AUTONOMOUS_TRANSACTION; 4 Result number; 5 x NUMBER; 6 a NUMBER; 7 i NUMBER; 8 c Char(1); 9 BEGIN 10 IF TRIM(arg) IS NULL THEN--防止值入空字符串 11 RAISE_Application_Error(-6502,'param "arg" is not valide.',TRUE); 12 END IF; 13 <<top>> 14 SELECT COUNT(1) INTO x FROM Sequences WHERE ID = arg; 15 IF x = 0 THEN 16 BEGIN 17 BEGIN 18 INSERT INTO Sequences (ID) VALUES(arg);--防止並發同時插入相同的Id值。需要將id設為主鍵 19 EXCEPTION 20 WHEN OTHERS THEN 21 GOTO top; 22 END; 23 COMMIT; 24 RETURN 1; 25 END; 26 ELSE 27 BEGIN 28 SELECT s.currentvalue + s.increaseby,s.maxvalue,s.minvalue,s.cycle INTO Result,a,i,c FROM Sequences s WHERE ID = arg FOR update;--for update將鎖定此行記錄 29 IF RESULT < a THEN--未超出最大值 30 BEGIN 31 UPDATE Sequences SET currentvalue = Result WHERE ID = arg; 32 COMMIT; 33 return(Result); 34 END; 35 ELSE 36 BEGIN 37 IF c = '0' THEN--不循環 38 BEGIN 39 RAISE_Application_Error(-6502,'out of range.',TRUE); 40 END; 41 ELSE 42 BEGIN--循環 43 UPDATE Sequences SET currentvalue = i WHERE ID = arg; 44 COMMIT; 45 RETURN i; 46 END; 47 END IF; 48 END; 49 END IF; 50 END; 51 END IF; 52 END;
是的,我使用了參數。這樣就使得這個表更加有用,而非只單獨處理一種類型順序號。同時對並發進行了處理。讓你只可能取得一個值,而不會出現重復的值。當然所有的result都沒有進行格式化,而是直接輸出。在PLSQL中進行函數test,打開兩個窗口,單步調試,可以看到在insert或select for update時都會阻塞其它session對此表的操作。這樣可以使用result的結果唯一。
如果將表的名稱的傳入,那么你可以扔掉sequence了。