增加T-sql中的字符值

时间:2014-12-01 07:22:33

标签: sql sql-server tsql

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

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

如何在T-SQL中实现?

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

借助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 

enter image description here

答案 2 :(得分:0)

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

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

))

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

答案 3 :(得分:0)

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

    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