您好,登錄后才能下訂單哦!
在一些系統中,經理要生成單據號,為了不使多臺客戶端生成的單據號重復,一般要在服務端生成這種流水號,本文是在數據庫中生成流水號,并且可以生成多種類型的單據號(比如銷售單據號,盤點單據號,進貨單據號等),利用數據庫鎖的原理,先看一下SQL語句:
CREATE TABLE [dbo].[Lshs]( [MAXLSH] [BIGINT] NULL, [LSHDate] [DATETIME] NULL, [LX] [NVARCHAR](6) NULL ) ON [PRIMARY]
CREATE PROC [dbo].[getlsh] @lx VARCHAR(6) , @lsh VARCHAR(30) OUTPUT AS BEGIN --啟動事務處理 DECLARE@tran_point INT --控制事務嵌套 SET @tran_point = @@trancount --保存事務點 IF @tran_point = 0 BEGINTRAN tran_SOF_getmaxdjbh ELSE SAVETRAN tran_SOF_getmaxdjbh DECLARE @bh BIGINT --鎖表 --IF EXISTS(SELECT 1 FROM lshs WITH (TABLOCKX) WHERE lx=@lxAND lshdate=CONVERT(VARCHAR(10),GETDATE(),126)) -- BEGIN -- SELECT @bh = MaxLsh + 1 -- FROM dbo.Lshs -- WHERE lx = @lx -- UPDATE Lshs -- SET MaxLSH = @bh -- WHERE lx = @lx --END --ELSE --BEGIN -- UPDATE Lshs -- SET MaxLSH =1,lshdate=CONVERT(VARCHAR(10),GETDATE(),126) -- WHERE lx = @lx --end --鎖行 UPDATE Lshs SET @bh = maxlsh= CASE WHEN lshdate=CONVERT(VARCHAR(10),GETDATE(),126) THEN maxlsh+1 ELSE 1 end ,lshdate=CONVERT(VARCHAR(10),GETDATE(),126) WHERE lx = @lx --獲取編號 SET @lsh=@lx+REPLACE(CONVERT(VARCHAR(10),GETDATE(),126),'-','')+REPLICATE('0',6-LEN(@bh))+CONVERT(VARCHAR(10),@bh) IF @@error <> 0 BEGIN ROLLBACKTRAN tran_SOF_getmaxdjbh END IF @tran_point = 0 BEGIN COMMITTRAN tran_SOF_getmaxdjbh RETURN 0 END END
語句中注釋的是鎖表的方式,未注釋是用Update語句,是鎖行的操作,鎖表的操作要更占時間,當一個表中有很多個類型時,就會排隊,等一種類型生成后,釋放表,才能繼續生成下一種類型,鎖行只鎖相同類型的,相對來說類型越多,這種優勢越明顯。并且在短時間內生成的單據號越多,鎖行的優勢也越明顯。
下來,我們可以用這樣的代碼來測試一下:
classProgram { staticDictionary<string, string> yz_dic = newDictionary<string, string>(); staticDictionary<string, string> xs_dic = newDictionary<string, string>(); staticDictionary<string, string> cg_dic = newDictionary<string, string>(); staticvoid GetID() { Console.WriteLine("begin"); void BuildLsh(object obj) { //定義一個時間對象 var oTime = newStopwatch(); oTime.Start(); //記錄開始時間 using (var con = newSqlConnection("DataSource=.;Initial Catalog=testlsh;Persist Security Info=True;UserID=sa;Password=******;")) { var cmd = newSqlCommand(); cmd.Connection = con; cmd.CommandText = "getlsh"; cmd.CommandType =System.Data.CommandType.StoredProcedure; var lxnum = DateTime.Now.Millisecond % 3; var lx = "YZ"; switch (lxnum) { case 0: lx = "YZ"; break; case 1: lx = "XS"; break; case 2: lx = "CG"; break; } cmd.Parameters.Add(newSqlParameter() { ParameterName ="@lx", Value = lx }); var par = newSqlParameter(); par.ParameterName = "@lsh"; par.Direction =System.Data.ParameterDirection.Output; par.SqlDbType =System.Data.SqlDbType.VarChar; par.Size = 30; cmd.Parameters.Add(par); con.Open(); cmd.ExecuteReader(); var lsh = par.Value.ToString(); switch (lxnum) { case 0: yz_dic.Add(lsh,obj.ToString()); break; case 1: xs_dic.Add(lsh,obj.ToString()); break; case 2: cg_dic.Add(lsh,obj.ToString()); break; } } oTime.Stop(); //記錄結束時間 //輸出運行時間。 Console.WriteLine($"---{obj}---程序的運行時間:{ oTime.Elapsed.TotalMilliseconds}毫秒"); } for (int i = 0; i < 2000; i++) { new System.Threading.Thread(BuildLsh).Start(i); } } publicstaticvoid Main() { GetID(); } }
可以切換存付過程中的鎖表和鎖列的兩段SQL,查看執行的時間,有明顯的區別
鎖行結果如下(本結果只作比較,快慢與硬件有很大關系):
鎖表:
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。