我在一列中有两组值,即前4个字符是字符,接下来的4个字符是数字。 例如:AAAA1234 现在我必须从右端增加值,即当数值达到9999时,我必须将字符增加1个字符。
示例: 考虑存储在列中的最后一个值是AAAA9999,然后下一个递增的值应该是序列AAAB9999,....... AABZ9999,..... BZZZ9999 ..... ZZZZ9999(最后一个值)。当它达到ZZZZ9999时,我必须将值重置为AAAA0001。
如何在T-SQL中实现?
答案 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
答案 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