zoukankan      html  css  js  c++  java
  • 用SQL存储过程生成唯一单据号

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

     

            在一些系统中,经理要生成单据号,为了不使多台客户端生成的单据号重复,一般要在服务端生成这种流水号,本文是在数据库中生成流水号,并且可以生成多种类型的单据号(比如销售单据号,盘点单据号,进货单据号等),利用数据库锁的原理,先看一下SQL语句:

     

    1.  
      CREATE TABLE [dbo].[Lshs](
    2.  
      [MAXLSH] [BIGINT] NULL,
    3.  
      [LSHDate] [DATETIME] NULL,
    4.  
      [LX] [NVARCHAR](6) NULL
    5.  
      ON [PRIMARY]



     

    1.  
      CREATE PROC [dbo].[getlsh]
    2.  
          @lx VARCHAR(6) ,
    3.  
          @lsh VARCHAR(30) OUTPUT
    4.  
      AS
    5.  
          BEGIN
    6.  
       --启动事务处理
    7.  
              DECLARE@tran_point INT         --控制事务嵌套
    8.  
              SET @tran_point = @@trancount   --保存事务点
    9.  
              IF @tran_point = 0
    10.  
                  BEGINTRAN tran_SOF_getmaxdjbh
    11.  
              ELSE
    12.  
                  SAVETRAN tran_SOF_getmaxdjbh
    13.  
       
    14.  
              DECLARE @bh BIGINT
    15.  
              --锁表
    16.  
              --IF EXISTS(SELECT 1 FROM lshs WITH (TABLOCKX) WHERE lx=@lxAND lshdate=CONVERT(VARCHAR(10),GETDATE(),126))
    17.  
           --   BEGIN
    18.  
       --     SELECT  @bh = MaxLsh  + 1 
    19.  
       --     FROM    dbo.Lshs 
    20.  
       --     WHERE   lx = @lx  
    21.  
       --     UPDATE  Lshs
    22.  
       --     SET     MaxLSH = @bh  
    23.  
       --     WHERE   lx = @lx 
    24.  
              --END
    25.  
              --ELSE
    26.  
              --BEGIN
    27.  
       --     UPDATE  Lshs
    28.  
       --     SET     MaxLSH =1,lshdate=CONVERT(VARCHAR(10),GETDATE(),126)
    29.  
       --     WHERE   lx = @lx 
    30.  
              --end
    31.  
       
    32.  
       
    33.  
              --锁行
    34.  
              UPDATE  Lshs
    35.  
              SET    @bh = maxlsh= CASE WHEN lshdate=CONVERT(VARCHAR(10),GETDATE(),126) THEN maxlsh+ELSE end ,lshdate=CONVERT(VARCHAR(10),GETDATE(),126)
    36.  
              WHERE   lx = @lx 
    37.  
              --获取编号
    38.  
              SET @lsh=@lx+REPLACE(CONVERT(VARCHAR(10),GETDATE(),126),'-','')+REPLICATE('0',6-LEN(@bh))+CONVERT(VARCHAR(10),@bh)
    39.  
       
    40.  
              IF @@error <> 0
    41.  
                  BEGIN
    42.  
                      ROLLBACKTRAN tran_SOF_getmaxdjbh
    43.  
                  END
    44.  
       
    45.  
              IF @tran_point = 0
    46.  
                  BEGIN
    47.  
                      COMMITTRAN tran_SOF_getmaxdjbh
    48.  
                      RETURN 0
    49.  
                  END
    50.  
          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,查看执行的时间,有明显的区别

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

  • 相关阅读:
    学习笔记 MYSQL报错注入(count()、rand()、group by)
    学习笔记 HTTP参数污染注入
    学习笔记 MSSQL显错手工注入
    代码审计入门后审计技巧
    字符串的排列
    二叉搜索树与双向链表
    复杂链表的复制
    二叉树中和为某一值的路径
    二叉搜索树的后序遍历序列
    从上往下打印二叉树
  • 原文地址:https://www.cnblogs.com/zengpeng/p/11344694.html
Copyright © 2011-2022 走看看