如何生成两个字母数字值之间的数字范围?

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/s​​GMB)。 @Rajit 好吧,我怀疑这两种解决方案的性能不会有太大差异,因为您只是在生成一个值列表。对于这样的事情,它可能是非常主观的,您可能会想要选择更容易理解的那个,您可以设想维护和适应其他问题,与系统中已经存在的代码更紧密地保持一致,诸如此类. 亚伦,非常感谢。我真的很感激。

以上是关于如何生成两个字母数字值之间的数字范围?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Typescript中把字母数字值转换为数值范围?

Java 判断整数,整数范围为0-120 之间 和 30-180 的两个正则表达式如何写?不允许小数,字母等非法内容。

选择两个不同字段之间的范围包含给定数字的数据

选择两个不同字段之间的范围包含给定数字的数据

PHP - 在两个静态数字之间创建一组数字范围

Python regex如何在字母和数字之间插入连字符;并删除两个字母之间的连字符