SQLServer存储过程生成任意进制的顺序号流水号

SQLServer存储过程生成任意进制的顺序号流水号

需求背景:在设计流水码的时候,之前用10进制,一旦数据量过大,位数就就得很多,而且光秃秃一串数字也不美观。后来想起车牌规则,就想将流水码也换成类似的。由于1,0和I,O很相似,所有去掉I,O,换成34进制。

功能:设计了一张表,用来存储十进制的数字,每次调用的时候加1,在方法返回的参数中,转为34进制。即,用sqlserver 实现带英文字母的流水码。

 

代码1:

ALTER    PROCEDURE [dbo].[TentoSerial] 
@num int, 
@ret nvarchar(10) output 
AS

declare @StringXL nvarchar(50) 
declare @CharXL nvarchar(10) 
declare @modnum int

set @StringXL='0123456789ABCDEFGHJKLMNPRSTUVWXYZ' 
set @CharXL=''

if @num>35936 
    begin 
        raiserror('流水号已超过上限35936',16,-1) 
        return -1 
    end 
if (@num<=32) 
begin 
    set @CharXL=SUBSTRING(@stringxl,@num+1,1) 
end 
while @num>32 
    begin 
        set @modnum=@num%33 
        set @num=@num/33 
        set @CharXL=SUBSTRING(@stringxl,@modnum+1,1)+@CharXL 
        if (@num<=32) 
            begin 
                set @CharXL=SUBSTRING(@stringxl,@num+1,1)+@CharXL 
            end 
    end 
select @ret=right('000'+@CharXL,3) 
return 0

 

代码2:

SQL Server中实现34进制流水码

-- 34 进制
CREATE FUNCTION [dbo].[int2Char](@id bigint)
RETURNS char(4)
AS
BEGIN;
    DECLARE @char_return varchar(10) = '', @v_count int;
    WHILE @id > 0
         SELECT
            @v_count = @id % 34, @id = @id / 34,
            @char_return = CASE
                    WHEN @v_count < 10 THEN RTRIM(@v_count)
                    WHEN @v_count >=10 AND @v_count <18 THEN CHAR(55+@v_count)
                    WHEN @v_count >= 18 AND @v_count< 23 THEN CHAR(55+@v_count + CASE WHEN  @v_count > 17    THEN 1  ELSE 0 END)
                    ELSE CHAR(55+@v_count + CASE WHEN  @v_count >= 23    THEN 2  ELSE 0 END)
                END + @char_return
    RETURN(RIGHT('0000000000' + @char_return, 4))
END;

 

代码3:

--下面函数实现在sqlserver中产生带字母流水号,如ABC的下一个数为ABD, 而ABCD递增一个数为ABCE 。


create  function dbo.f_IncIdent(@p_AIdent varchar(36))
returns varchar(20) 
 as    
 begin 
   declare  @v_cChars varchar(40), 
   @v_J integer, 
   @v_K Integer, 
   @v_result varchar(40); 
    set @v_cChars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
   set @v_Result = @p_AIdent; 
   set @v_j= Len(@p_AIdent) 
   while @v_J >1 
   begin 
     SET @v_K = CHARINDEX(SUBSTRING(@P_AIdent,@V_J,1), @V_cChars); 
     if @V_K < Len(@V_cChars) 
     begin 
       set @v_result=substring(@v_result,1,@v_J-1)+SUBSTRING(@V_cChars,@v_k+1,1)+substring(@v_result,@v_J+1,40) 
       SET @V_K=Len(@P_AIdent) 
       while @v_k>@V_J 
       begin 
           set @v_result=substring(@v_result,1,@v_k-1)+substring(@v_cChars,1,1)+substring(@v_result,@v_k+1,40) 
           set @v_k=@v_k-1; 
       end; 
       return @v_result; 
       break; 
     end; 
    set  @v_J=@v_J-1; 
   end; 
  return @v_result; 
 end;

 

 

代码4:

以如下表为例,表如下图:

sql server 表行序号 sql server 生成序号_Code

 

 

 

方法1的实施思路:

取号全部在 RUL_Sequence 表中操作,不用管流水号所在表中的最大流水号。可以自由配置流水号规则。

通过一个表和一个存储过程来实现序列号生成。

/* 存储过程  dbo.[Proc_GetSeqence]  */
CREATE PROCEDURE [dbo].[Proc_GetSeqence]
          @SeqCode varchar(60),                -- 规则代码
          @ReturnNum Varchar(40) OUTPUT,    -- 返回的流水号
          @MessageCode varchar(800) OUTPUT    -- 异常消息等

AS
/* Exec Proc_GetSeqence 'OrderNo','',''
*****************************************************************
作者:        XXXXXXXXX
日期:        2013/06/30 
功能描述:    获取数据表的主键流水号(INV, ASN, SO...)
    
主要思路:    1.取得最新流水号信息
            2.把所有固定的规则信息替换成具体值,其他保持不变
            eg:
            规则为:        ASN<YYYY><YY><MM><XXX>ASN
            当前日期为:    20130630
            当前流水号为:12
            最终流水号为:ASN201306013ASN
            
******************************************************************
*/


/*
* SET NOCOUNT ON 的作用:
* 不返回受影响行数
* 存储过程中包含的一些语句并不返回许多实际的数据,则该设置由于大量减少了网络流量,因此可显著提高性能。
* */
SET NOCOUNT ON

DECLARE @SeqNowNumStr VARCHAR(20)    --当前值字符类型    
DECLARE @SeqNowNum BIGINT            --当前值    
DECLARE @year CHAR(4)                --年 YYYY
DECLARE @month CHAR(2)                --月 MM
DECLARE @day CHAR(2)                --日 DD
DECLARE @Length INT                    --流水号长度
DECLARE @DataFormat VARCHAR(50)        --流水号规则
DECLARE @IniValue INT                --归零值
DECLARE @ResetType VARCHAR(10)        --归零方式
DECLARE @LastDate    CHAR(8)            --日期最大值            
DECLARE @WorkFLowStr VARCHAR(20)     --前一次调用流水号时的日期值
DECLARE @DataNow CHAR(8)            --当前日期
DECLARE @i INT                        --转换变量,作用参照代码上下文
    

/*
* SET XACT_ABORT ON 的作用:
* 存储中的某个地方出了问题,整个事务中的语句都会回滚
* */
SET XACT_ABORT ON
BEGIN TRY

    /* 初始化变量 */
     SET @MessageCode='999'
     SET @ReturnNum = '0'
     SET @Length=0
     SET @SeqNowNum =0;
     SET @DataNow=CONVERT(CHAR(8),GETDATE(),112) --得到 20130704 的时间格式
     SET @year=SUBSTRING(@DataNow,1,4)
     SET @month =SUBSTRING(@DataNow,5,2)
     SET @day =SUBSTRING(@DataNow,7,2)
     
     Set @i=1 
     
     /***********如果有并发的正在运行,最多等待0.06秒,然后继续运行 Start*******/           
     BEGIN TRANSACTION 
         wait:
         Update dbo.RUL_Sequence Set [IsRunning]='2' where SeqCode=@SeqCode and IsRunning='1'
        If @@Rowcount=0    
        Begin
            Waitfor Delay '00:00:01'
            Set @i=@i+1
            If @i<6 goto wait
        End
      
     COMMIT TRANSACTION   
     /***********如果有并发的正在运行,最多等待0.06秒,然后继续运行 End*******/           

    Select @Length = [Length],@SeqNowNum=NowSeqValue,@LastDate=DateMax,@DataFormat=DataFormat
        ,@ResetType=ResetType,@IniValue =InitValue
            From RUL_Sequence where SeqCode=@SeqCode
                        if @SeqNowNum=0  --当前值正常情况下不可能是0
                        begin
                           Set @MessageCode='100'  --当前值 错误代码
                           select @MessageCode
                           return
                        END
                        --@ResetType=1 不归零 2 按日归零   3 按月归零   4按年归零
    If (@ResetType=2 and @DataNow<>@LastDate  AND @IniValue>0)
        OR (@ResetType=3 and @year+@month<>SUBSTRING(@LastDate,1,6) AND @IniValue>0)
        OR (@ResetType=4 and @year<>SUBSTRING(@LastDate,1,4) AND @IniValue>0 )
       BEGIN
           SET @SeqNowNum=@IniValue
       END 
     SET  @i=@Length --@i 此时表示流水号的总长度
          
     /***********拼流水号格式 Start*******/           
     SET @WorkFLowStr='<'
     WHILE @Length>0 
     BEGIN
         SET @WorkFLowStr=@WorkFLowStr+'X'
         SET @Length=@Length-1
     END  
     SET @WorkFLowStr=@WorkFLowStr+'>' 
     /***********拼流水号格式 End*******/
     
     set @SeqNowNumStr=CONVERT(VARCHAR(20),@SeqNowNum)
     SET @Length=@i-len(@SeqNowNumStr)  --@Length 要补零的位数(eg:@SeqNowNumStr=148 当前流水号是五位,最后流水号为00148,00 就是需要补的两位)
     
     /***********补零操作 Start*******/           
     WHILE @Length>0 
     BEGIN
         SET @SeqNowNumStr='0'+@SeqNowNumStr
         SET @Length=@Length-1
     END
     /***********补零操作 End*******/           
     
     SET @ReturnNum=REPLACE( @DataFormat,'<YYYY>',@year);            -- 把规则中<YYYY>替换成相应年
     SET @ReturnNum=REPLACE( @ReturnNum,'<MM>',@month);                -- 把规则中<MM>替换成相应月
     SET @ReturnNum=REPLACE( @ReturnNum,'<DD>',@day);                -- 把规则中<DD>替换成相应日
     SET @ReturnNum=REPLACE( @ReturnNum,@WorkFLowStr,@SeqNowNumStr);-- 把规则中的形如<XXX>的替换成相应流水号,
     
     
     /***********更新当前流水值为最大流水号、上一个流水号生成时间和运行标记(运行标记置为"1"(没有运行) ) Start*******/
    Begin transaction
     
     UPDATE RUL_Sequence SET NowSeqValue=@SeqNowNum+1,DateMax=@DataNow,ISRUNNING='1', EditTime=Getdate()
     WHERE IsRunning='2' AND  SeqCode=@SeqCode
     --SELECT * FROM RUL_Sequence WHERE IsRunning='2' AND SeqCode=@SeqCode
    -- PRINT @SeqNowNum+1
    Commit transaction 
     /***********更新当前流水值为最大流水号、上一个流水号生成时间和运行标记(运行标记置为"1"(没有运行) ) End*******/
   PRINT @ReturnNum
    RETURN

END TRY

--错误捕获
BEGIN   CATCH   
     ROLLBACK TRANSACTION  
  set @MessageCode='行号='+cast(ERROR_LINE() as varchar(10))+'错误信息'+ERROR_MESSAGE() 
      +'['+ERROR_PROCEDURE()+']'
   IF @@ROWcount<=0 
      set @MessageCode='无此编号规则'+@MessageCode
    SELECT  @MessageCode
END   CATCH

 

执行存储过程获得流水号:

Exec Proc_GetSeqence 'OrderNo','',''

sql server 表行序号 sql server 生成序号_Code_02

 

 

方法2的实施思路:

取流水号所在表中的最大流水号 + 1

待研究问题:Shopping_Pay_Basic表中的流水号字段PayOrderNo遇到并发时,是否会影响用户操作?

注:第一种实现只管取号,可能造成浪费,但没有并发问题。

--得到新编号的函数
ALTER FUNCTION [dbo].[GetNextPayOrderNo](@Type char(2))
RETURNS char(16)
AS
BEGIN
    DECLARE @dt CHAR(8)
    SELECT @dt=CONVERT(CHAR(8),GETDATE(),112)
    RETURN(
        SELECT @Type + (@dt+RIGHT(1000001+ISNULL(RIGHT(MAX(PayOrderNo),6),0),6)) 
        FROM Shopping_Pay_Basic WITH(XLOCK,PAGLOCK)
        WHERE PayOrderNo like @Type + @dt+'%')
END
-- SELECT  dbo.GetNextPayOrderNo('TT')

 

执行函数获得流水号:

SELECT dbo.GetNextPayOrderNo('AA')

 

 

  

···

 

人生只若初见................