用SQL存储过程生成唯一单据号
在一些系统中,经理要生成单据号,为了不使多台客户端生成的单据号重复,一般要在服务端生成这种流水号,本文是在数据库中生成流水号,并且可以生成多种类型的单据号(比如销售单据号,盘点单据号,进货单据号等),利用数据库锁的原理,先看一下SQL语句:
CREATETABLE[dbo].[Lshs]([MAXLSH][BIGINT]NULL,[LSHDate][DATETIME]NULL,[LX][NVARCHAR](6)NULL)ON[PRIMARY]
CREATEPROC[dbo].[getlsh]@lxVARCHAR(6),@lshVARCHAR(30)OUTPUTASBEGIN--启动事务处理DECLARE@tran_pointINT--控制事务嵌套SET@tran_point=@@trancount--保存事务点IF@tran_point=0BEGINTRANtran_SOF_getmaxdjbhELSESAVETRANtran_SOF_getmaxdjbhDECLARE@bhBIGINT--锁表--IFEXISTS(SELECT1FROMlshsWITH(TABLOCKX)WHERElx=@lxANDlshdate=CONVERT(VARCHAR(10),GETDATE(),126))--BEGIN--SELECT@bh=MaxLsh+1--FROMdbo.Lshs--WHERElx=@lx--UPDATELshs--SETMaxLSH=@bh--WHERElx=@lx--END--ELSE--BEGIN--UPDATELshs--SETMaxLSH=1,lshdate=CONVERT(VARCHAR(10),GETDATE(),126)--WHERElx=@lx--end--锁行UPDATELshsSET@bh=maxlsh=CASEWHENlshdate=CONVERT(VARCHAR(10),GETDATE(),126)THENmaxlsh+1ELSE1end,lshdate=CONVERT(VARCHAR(10),GETDATE(),126)WHERElx=@lx--获取编号SET@lsh=@lx+REPLACE(CONVERT(VARCHAR(10),GETDATE(),126),'-','')+REPLICATE('0',6-LEN(@bh))+CONVERT(VARCHAR(10),@bh)IF@@error<>0BEGINROLLBACKTRANtran_SOF_getmaxdjbhENDIF@tran_point=0BEGINCOMMITTRANtran_SOF_getmaxdjbhRETURN0ENDEND
语句中注释的是锁表的方式,未注释是用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>();staticvoidGetID(){Console.WriteLine("begin");voidBuildLsh(objectobj){//定义一个时间对象varoTime=newStopwatch();oTime.Start();//记录开始时间using(varcon=newSqlConnection("DataSource=.;InitialCatalog=testlsh;PersistSecurityInfo=True;UserID=sa;Password=******;")){varcmd=newSqlCommand();cmd.Connection=con;cmd.CommandText="getlsh";cmd.CommandType=System.Data.CommandType.StoredProcedure;varlxnum=DateTime.Now.Millisecond%3;varlx="YZ";switch(lxnum){case0:lx="YZ";break;case1:lx="XS";break;case2:lx="CG";break;}cmd.Parameters.Add(newSqlParameter(){ParameterName="@lx",Value=lx});varpar=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();varlsh=par.Value.ToString();switch(lxnum){case0:yz_dic.Add(lsh,obj.ToString());break;case1:xs_dic.Add(lsh,obj.ToString());break;case2:cg_dic.Add(lsh,obj.ToString());break;}}oTime.Stop();//记录结束时间//输出运行时间。Console.WriteLine($"---{obj}---程序的运行时间:{oTime.Elapsed.TotalMilliseconds}毫秒");}for(inti=0;i<2000;i++){newSystem.Threading.Thread(BuildLsh).Start(i);}}publicstaticvoidMain(){GetID();}}
可以切换存付过程中的锁表和锁列的两段SQL,查看执行的时间,有明显的区别
锁行结果如下(本结果只作比较,快慢与硬件有很大关系):
锁表:
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。