生成范围之间的数字
Generate Numbers between Range
假设我有 2-6
并且通过使用下面的程序我能够生成范围
DECLARE @range VARCHAR(10) = '2-6'
DECLARE @startRange INT = PARSENAME(REPLACE(@range, '-', '.'), 2)
DECLARE @lastRange INT = PARSENAME(REPLACE(@range, '-', '.'), 1)
SELECT DISTINCT Number = number
FROM master..spt_values
WHERE number BETWEEN @startRange AND @lastRange
现在我的输入是 2-6,9-12,15-20
我知道通过使用 ,
作为分隔符拆分记录并将上述查询用作函数,我可以实现目标。但是,我想使用 CROSS APPLY 来避免函数。
我试过
DECLARE @range VARCHAR(50) = '2-6,9-12,15-20'
SELECT
value
FROM STRING_SPLIT(@range,',')
但是如何通过 master..spt_values
将 CROSS APPLY 与上述查询一起使用?
如果你只是提取每个项目的开始和结束位置,你可以使用类似的东西:
DECLARE @Value VARCHAR(20) = '2-6';
SELECT Start = TRY_CONVERT(INT, LEFT(@Value, CHARINDEX('-', @Value) - 1)),
[End] = TRY_CONVERT(INT, SUBSTRING(@Value, CHARINDEX('-', @Value) + 1, LEN(@Value)));
给出:
Start End
------------
2 6
然后你可以简单地加入master..spt_values
:
DECLARE @range VARCHAR(50) = '2-6,9-12,15-20'
SELECT s.Value, v.Number
FROM STRING_SPLIT(@range,',') AS s
INNER JOIN master..spt_values AS v
ON v.Number >= TRY_CONVERT(INT, LEFT(s.Value, CHARINDEX('-', s.Value) - 1))
AND v.Number <= TRY_CONVERT(INT, SUBSTRING(s.Value, CHARINDEX('-', s.Value) + 1, LEN(s.Value)))
AND v.Type = 'P';
因为master..spt_values
的价值相当有限,有更好的方法generate a set or sequence without a loop。
当使用 master..spt_values
时,范围 2500-2501
将不起作用,但如果您有数字 table 或不同的数字来源,那就没问题了,例如以下工作最多 10,000,然后通过取消注释更多行,您可以增加范围:
DECLARE @range VARCHAR(50) = '2-6,9-12,15-20,2500-2501';
WITH Numbers (Number) AS
( SELECT ROW_NUMBER() OVER(ORDER BY N1.N)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N1 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N2 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N3 (N) -- 1,000
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N4 (N) -- 10,000
--CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N5 (N) -- 100,000
)
SELECT s.Value, n.Number
FROM STRING_SPLIT(@range,',') AS s
INNER JOIN Numbers AS n
ON n.Number >= TRY_CONVERT(INT, LEFT(s.Value, CHARINDEX('-', s.Value) - 1))
AND n.Number <= TRY_CONVERT(INT, SUBSTRING(s.Value, CHARINDEX('-', s.Value) + 1, LEN(s.Value)))
ORDER BY n.Number;
您可以使用以下脚本实现结果
SELECT DISTINCT Number = number
FROM master..spt_values
CROSS APPLY STRING_SPLIT(@range,',') AS S
WHERE number BETWEEN PARSENAME(REPLACE(value, '-', '.'), 2)
AND PARSENAME(REPLACE(value, '-', '.'), 1)
交叉应用版本
SELECT t.number
FROM STRING_SPLIT(@range,',') sv
CROSS APPLY (
SELECT startv = CAST(PARSENAME(REPLACE(value, '-', '.'), 2) AS INT),
endv = CAST(PARSENAME(REPLACE(value, '-', '.'), 1) AS INT)
) param
CROSS APPLY (
SELECT TOP(endv-startv+1) number = startv + row_number() over(order by startv) - 1
FROM master..spt_values
) t;
假设我有 2-6
并且通过使用下面的程序我能够生成范围
DECLARE @range VARCHAR(10) = '2-6'
DECLARE @startRange INT = PARSENAME(REPLACE(@range, '-', '.'), 2)
DECLARE @lastRange INT = PARSENAME(REPLACE(@range, '-', '.'), 1)
SELECT DISTINCT Number = number
FROM master..spt_values
WHERE number BETWEEN @startRange AND @lastRange
现在我的输入是 2-6,9-12,15-20
我知道通过使用 ,
作为分隔符拆分记录并将上述查询用作函数,我可以实现目标。但是,我想使用 CROSS APPLY 来避免函数。
我试过
DECLARE @range VARCHAR(50) = '2-6,9-12,15-20'
SELECT
value
FROM STRING_SPLIT(@range,',')
但是如何通过 master..spt_values
将 CROSS APPLY 与上述查询一起使用?
如果你只是提取每个项目的开始和结束位置,你可以使用类似的东西:
DECLARE @Value VARCHAR(20) = '2-6';
SELECT Start = TRY_CONVERT(INT, LEFT(@Value, CHARINDEX('-', @Value) - 1)),
[End] = TRY_CONVERT(INT, SUBSTRING(@Value, CHARINDEX('-', @Value) + 1, LEN(@Value)));
给出:
Start End
------------
2 6
然后你可以简单地加入master..spt_values
:
DECLARE @range VARCHAR(50) = '2-6,9-12,15-20'
SELECT s.Value, v.Number
FROM STRING_SPLIT(@range,',') AS s
INNER JOIN master..spt_values AS v
ON v.Number >= TRY_CONVERT(INT, LEFT(s.Value, CHARINDEX('-', s.Value) - 1))
AND v.Number <= TRY_CONVERT(INT, SUBSTRING(s.Value, CHARINDEX('-', s.Value) + 1, LEN(s.Value)))
AND v.Type = 'P';
因为master..spt_values
的价值相当有限,有更好的方法generate a set or sequence without a loop。
当使用 master..spt_values
时,范围 2500-2501
将不起作用,但如果您有数字 table 或不同的数字来源,那就没问题了,例如以下工作最多 10,000,然后通过取消注释更多行,您可以增加范围:
DECLARE @range VARCHAR(50) = '2-6,9-12,15-20,2500-2501';
WITH Numbers (Number) AS
( SELECT ROW_NUMBER() OVER(ORDER BY N1.N)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N1 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N2 (N) -- 100
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N3 (N) -- 1,000
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N4 (N) -- 10,000
--CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS N5 (N) -- 100,000
)
SELECT s.Value, n.Number
FROM STRING_SPLIT(@range,',') AS s
INNER JOIN Numbers AS n
ON n.Number >= TRY_CONVERT(INT, LEFT(s.Value, CHARINDEX('-', s.Value) - 1))
AND n.Number <= TRY_CONVERT(INT, SUBSTRING(s.Value, CHARINDEX('-', s.Value) + 1, LEN(s.Value)))
ORDER BY n.Number;
您可以使用以下脚本实现结果
SELECT DISTINCT Number = number
FROM master..spt_values
CROSS APPLY STRING_SPLIT(@range,',') AS S
WHERE number BETWEEN PARSENAME(REPLACE(value, '-', '.'), 2)
AND PARSENAME(REPLACE(value, '-', '.'), 1)
交叉应用版本
SELECT t.number
FROM STRING_SPLIT(@range,',') sv
CROSS APPLY (
SELECT startv = CAST(PARSENAME(REPLACE(value, '-', '.'), 2) AS INT),
endv = CAST(PARSENAME(REPLACE(value, '-', '.'), 1) AS INT)
) param
CROSS APPLY (
SELECT TOP(endv-startv+1) number = startv + row_number() over(order by startv) - 1
FROM master..spt_values
) t;