【问题标题】:Incrementing Character value in T-sql在 T-sql 中增加字符值
【发布时间】:2014-12-01 07:22:33
【问题描述】:

我在一列中有 2 组值,即前 4 个字符是字符,接下来的 4 个字符是数字。 例如:AAAA1234 现在我必须从右端增加值,即当数值达到 9999 时,我必须将字符增加 1 个字符。

示例: 考虑存储在列中的最后一个值是 AAAA9999,那么下一个递增的值应该是序列 AAAB9999,....... AABZ9999,...... BZZZ9999...... ZZZZ9999(最后一个值)。当它达到 ZZZZ9999 时,我必须将值重置为 AAAA0001。

在T-SQL中怎么做???

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:

    这是一个概念性脚本,它可以满足您的需求。您需要对其进行调整以满足您的要求

    DECLARE @test table(TestValue char(8))
    DECLARE @CharPart char(4),@NumPart int
    SET @CharPart = 'AAAA'
    SET @NumPart = 1
    WHILE @NumPart <=9999
    BEGIN
    INSERT INTO @test
    SELECT @CharPart+RIGHT(('0000'+CAST(@NumPart AS varchar(4))),4)
    IF @NumPart = 9999
        BEGIN
        IF SUBSTRING(@CharPart,4,1)<>'Z'
            BEGIN
            SET @CharPart = LEFT(@CharPart,3)+CHAR(ASCII(SUBSTRING(@CharPart,4,1))+1)
            SET @NumPart = 1
            END
        ELSE IF SUBSTRING(@CharPart,4,1)='Z' AND SUBSTRING(@CharPart,3,1) <>'Z'
            BEGIN
            SET @CharPart = LEFT(@CharPart,2)+CHAR(ASCII(SUBSTRING(@CharPart,3,1))+1)+RIGHT(@CharPart,1)
            SET @NumPart = 1
            END 
        ELSE IF SUBSTRING(@CharPart,3,1)='Z' AND SUBSTRING(@CharPart,2,1) <>'Z'
            BEGIN
            SET @CharPart = LEFT(@CharPart,1)+CHAR(ASCII(SUBSTRING(@CharPart,2,1))+1)+RIGHT(@CharPart,2)
            SET @NumPart = 1
            END 
        ELSE IF SUBSTRING(@CharPart,1,1)<>'Z' 
            BEGIN
            SET @CharPart = CHAR(ASCII(SUBSTRING(@CharPart,1,1))+1)+RIGHT(@CharPart,3)
            SET @NumPart = 1
            END
        ELSE IF SUBSTRING(@CharPart,1,1)='Z' 
            BEGIN
            SET @CharPart = 'AAAA'
            SET @NumPart = 1
            INSERT INTO @test
            SELECT @CharPart+RIGHT(('0000'+CAST(@NumPart AS varchar(4))),4)
            BREAK
            END 
        END
    ELSE
        BEGIN
        SET @NumPart=@NumPart+1
        END
    END
    
    SELECT * FROM @test
    

    【讨论】:

      【解决方案2】:

      借助 PATINDEX、SUBSTRING、ASCII 函数,您可以实现您的特殊情况。
      (我已经为您的特殊情况找到了解决方案)。同样,您可以添加自己的附加功能。

      create table #temp(col1 varchar(20))
      
      insert into #temp values('AAAA9999')
      insert into #temp values('AAAZ9999')
      insert into #temp values('AAZZ9999')
      insert into #temp values('AZZZ9999')
      insert into #temp values('ZZZZ9999')
      
      select * from #temp
      select col1,
      case when cast(substring(col1,patindex('%[0-9]%',col1),len(col1)) as int) = 9999 and left(col1,4) <> 'ZZZZ'
      then 
          case 
          when substring(col1,(patindex('%[0-9]%',col1)-1),1) <> 'Z' then left(col1,3)+char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-1),1)) + 1)+right(col1,4)
          when substring(col1,(patindex('%[0-9]%',col1)-2),1) <> 'Z' then left(col1,2)+char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-2),1)) + 1)+right(col1,5)
          when substring(col1,(patindex('%[0-9]%',col1)-3),1) <> 'Z' then left(col1,1)+char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-3),1)) + 1)+right(col1,6)
          when substring(col1,(patindex('%[0-9]%',col1)-4),1) <> 'Z' then char(ASCII(substring(col1,(patindex('%[0-9]%',col1)-4),1)) + 1)+right(col1,7)
          end
          else 'AAAA0001'
      end as outputofcol1
      --patindex('%[0-9]%',col1)-1 as charpos,
      --substring(col1,(patindex('%[0-9]%',col1)-1),1) as substr4,
      --substring(col1,(patindex('%[0-9]%',col1)-2),1) as substr3,
      --substring(col1,(patindex('%[0-9]%',col1)-3),1) as substr2,
      --substring(col1,(patindex('%[0-9]%',col1)-4),1) as substr1
      --ASCII(substring(col1,(patindex('%[0-9]%',col1)-1),1)) as ASC_value
      from #temp 
      

      【讨论】:

        【解决方案3】:

        以下函数应返回所需的值:

            IF OBJECT_ID (N'dbo.ufnGetIndexValue') IS NOT NULL
            DROP FUNCTION dbo.ufnGetIndexValue;
        GO
        
        CREATE FUNCTION dbo.ufnGetIndexValue(@MainString CHAR(8))
        RETURNS CHAR(8)
        AS
        BEGIN
        DECLARE @NumberPart INT
        DECLARE @StringPart CHAR(4)
        DECLARE @Position TINYINT
        DECLARE @char CHAR
        
        SET @NumberPart=CONVERT(INT,SUBSTRING(@MainString,5,8))
        SET @StringPart=SUBSTRING(@MainString,1,4)
        
        IF @NumberPart=9999
        BEGIN
            SET @NumberPart=1111;
            SET @Position=4
            WHILE @Position >= 1
            BEGIN
                SET @char=SUBSTRING(@StringPart,@Position,1)
                IF(@char!='Z')
                BEGIN
                    SET @char=CHAR(ASCII(@char)+1);
                    SET @StringPart = STUFF(@StringPart,@Position,1,@char);
                    BREAK;
                END
                SET @StringPart = STUFF(@StringPart,@Position,1,'A');
                SET @Position-=1;
            END
        END
        ELSE
        BEGIN
            SET @NumberPart+=1;
        END
        
        SET @MainString=@StringPart+CAST(@NumberPart AS CHAR(4));
        RETURN @MainString
        END
        GO
        

        【讨论】:

          【解决方案4】:

          这是一个执行增量的标量选择函数。

          CREATE FUNCTION dbo.inc_serial( @id char(8) )
          RETURNS char(8) BEGIN
          
          select @id = case when SUBSTRING(id,2,1) <> '[' then id else STUFF( id, 1, 2, char(((ascii(id)+1-65)%26)+65) + 'A' ) end from (
          select case when SUBSTRING(id,3,1) <> '[' then id else STUFF( id, 2, 2, char(ascii(right(id,7))+1) + 'A' ) end as id from (
          select case when SUBSTRING(id,4,1) <> '[' then id else STUFF( id, 3, 2, char(ascii(right(id,6))+1) + 'A' ) end as id from (
          select 
              case when right(@id,4) < '9999'
              then concat( left(@id,4), right(concat( '000', (cast(right(@id,4) as smallint)+1) ), 4 ) )
              else concat( left(@id,3), char(ascii(right(@id,5))+1), '0001' ) end as id
          ) t1 ) t2 ) t3
          RETURN @id
          
          END
          

          基本上,代码只是在数字上加一,并重复向左溢出。

          如果您的表始终只有一行要更新(例如选项/标志表):

          UPDATE [table] SET [serial] = dbo.inc_serial( [serial] );
          

          如果您的表有多行,您将需要一个标识或高精度创建时间列,以便我们知道重置后从哪里继续。

          INSERT INTO [table] (serial) VALUES ( dbo.inc_serial((
             select top 1 case when count(*) > 0 then max([serial]) else 'AAAA0000' end AS id 
             from [table] where [id] = ( select max([id]) from [table] )
          )));
          

          为了并发安全,使用 XLOCK,ROWLOCK,HOLDLOCK 来锁定表。 为简单起见,示例中省略了它们。


          如果你不喜欢 udf,你可以内嵌查询。

          第一种情况的内联示例:

          UPDATE [table] SET [serial] = ((
          select case when SUBSTRING(id,2,1) <> '[' then id else STUFF( id, 1, 2, char(((ascii(id)+1-65)%26)+65) + 'A' ) end as id from (
          select case when SUBSTRING(id,3,1) <> '[' then id else STUFF( id, 2, 2, char(ascii(right(id,7))+1) + 'A' ) end as id from (
          select case when SUBSTRING(id,4,1) <> '[' then id else STUFF( id, 3, 2, char(ascii(right(id,6))+1) + 'A' ) end as id from (
          
          select 
              case when right(id,4) < '9999'
              then concat( left(id,4), right(concat( '000', (cast(right(id,4) as smallint)+1) ), 4 ) )
              else concat( left(id,3), char(ascii(right(id,5))+1), '0001' ) end as id
          from (
              select top 1 [serial] as id from [table] with (XLOCK,ROWLOCK,HOLDLOCK)
          ) t0
          ) t1 ) t2 ) t3
          
          ))
          

          该函数也可以写成内联表值函数以获得更好的性能,但代价是使用更复杂,但除非它经常在多行上运行,否则我不会加边框。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-03-28
            • 2019-06-12
            • 1970-01-01
            • 2016-01-31
            • 2021-07-17
            • 1970-01-01
            • 1970-01-01
            • 2022-08-03
            相关资源
            最近更新 更多