如何生成两个字母数字值之间的数字范围?
Posted
技术标签:
【中文标题】如何生成两个字母数字值之间的数字范围?【英文标题】:How to generate a range of numbers between two alphanumeric values? 【发布时间】:2020-06-11 00:00:10 【问题描述】:我有两个字母数字作为用户输入,例如 WAC01001 和 WAC01012。
如何使用 SQL 查询在单独的行中生成这两个值之间的数字?我想要这个
ID
---------
WAC01001
WAC01002
WAC01003
WAC01004
WAC01005
WAC01006
WAC01007
WAC01008
WAC01009
WAC01010
WAC01011
WAC01012
同样,对于 WAC01 和 WAC12,预期结果是
ID
-----
WAC01
WAC02
WAC03
WAC04
WAC05
WAC06
WAC07
WAC08
WAC09
WAC10
WAC11
WAC12
这些输入值的长度大小不一。其中一些在字母后有前导零,而另一些则没有。
【问题讨论】:
我想你可能会在这里遇到XY Problem。作为一个终生的软件开发人员,我无法想象这是一个真正的需求。您只是想生成列表,还是实际使用这些值创建行?将使用什么语言来收集用户的输入? 你好 Dale,这些是我们除了现场服务工程师输入的速记序列号,稍后通过查询我们需要单独插入每个序列号和其他信息。谢谢。 【参考方案1】:这并不像看起来那么容易。字符串前缀的长度可能会有所不同;并且数字部分可能有前导零。
这是一种使用递归查询的方法。这个想法是首先将 sring 前缀与数字后缀分开(我假设字符串中第一个数字之后的所有内容),然后生成一系列数字,最后连接回字符串(相对于原始字符串的长度) .
declare @str1 nvarchar(max) = 'WAC01001';
declare @str2 nvarchar(max) = 'WAC01012';
with cte as (
select
n,
cast(substring(@str1, n, len(@str1)) as int) num,
cast(substring(@str2, n, len(@str2)) as int) end_num,
left(@str1, n - 1) prefix
from (select patindex('%[0-9]%', @str1) n) x
union all
select
n,
num + 1,
end_num,
prefix
from cte
where num < end_num
)
select concat(
prefix,
replicate('0', len(@str1) - n - len(num) + 1),
num
) res
from cte
order by num
如果您需要处理超过 100 个增量,则需要在查询末尾添加 option (maxrecursion 0)
。
Demo on DB Fiddle:
|资源 | | :------- | | WAC01001 | | WAC01002 | | WAC01003 | | WAC01004 | | WAC01005 | | WAC01006 | | WAC01007 | | WAC01008 | | WAC01009 | | WAC01010 | | WAC01011 | | WAC01012 |【讨论】:
谢谢你GMB,这正是我想要的,并测试了几个场景,它按预期工作。【参考方案2】:DECLARE @v1 varchar(32) = 'WAC01001',
@v2 varchar(32) = 'WAC01012';
-- where does the string switch to a numeric sequence?
DECLARE @pos int = PATINDEX('%[0-9]%', @v1);
;WITH y AS
(
SELECT
c = 2+LEN(@v1)-@pos, -- where sequence starts
prefix = LEFT(@v1, @pos-1), -- fixed portion at beginning
s = SUBSTRING(@v1, @pos, 32), -- start of numeric sequence
e = SUBSTRING(@v2, @pos, 32) -- end of numeric sequence
), nums AS
(
-- recursive CTE to generate all the numbers in the sequence
SELECT n = TRY_CONVERT(int, s), e FROM y
UNION ALL SELECT n + 1, e FROM nums WHERE n < e
)
SELECT q.ID FROM nums CROSS APPLY
(
SELECT ID = prefix + RIGHT(REPLICATE('0', c)
+ CONVERT(varchar(11), CONVERT(int, nums.n)), LEN(s))
FROM y
) AS q
ORDER BY q.ID OPTION (MAXRECURSION 32767);
如果范围内的值永远不能超过 100 个,则可以省略 OPTION (MAXRECURSION 32767)
。如果该范围内的值可能超过 32K,则必须将 32767 更改为 0,这样查询将不会很快。
【讨论】:
谢谢你Aaron,这是有效的,我测试了几个场景。由于我不是 SQL 专家,因此在性能方面我不太确定使用哪一个(Aaron V/sGMB)。 @Rajit 好吧,我怀疑这两种解决方案的性能不会有太大差异,因为您只是在生成一个值列表。对于这样的事情,它可能是非常主观的,您可能会想要选择更容易理解的那个,您可以设想维护和适应其他问题,与系统中已经存在的代码更紧密地保持一致,诸如此类. 亚伦,非常感谢。我真的很感激。以上是关于如何生成两个字母数字值之间的数字范围?的主要内容,如果未能解决你的问题,请参考以下文章