用SQL存储过程生成唯一单据号

        在一些系统中,经理要生成单据号,为了不使多台客户端生成的单据号重复,一般要在服务端生成这种流水号,本文是在数据库中生成流水号,并且可以生成多种类型的单据号(比如销售单据号,盘点单据号,进货单据号等),利用数据库锁的原理,先看一下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 yz_dic = newDictionary();
       staticDictionary xs_dic = newDictionary();
       staticDictionary cg_dic = newDictionary();
       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,查看执行的时间,有明显的区别

锁行结果如下(本结果只作比较,快慢与硬件有很大关系):

用SQL存储过程生成唯一单据号

锁表:

用SQL存储过程生成唯一单据号


本文名称:用SQL存储过程生成唯一单据号
浏览地址:http://azwzsj.com/article/joecop.html