生成范围

时间:2017-02-15 08:23:09

标签: sql tsql sql-server-2016 cross-apply

假设我有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,',')

但如何将CROSS APPLY与上述查询一起使用master..spt_values

3 个答案:

答案 0 :(得分:2)

如果您只是提取每个项目的开始和结束位置,您可以使用以下内容:

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范围不起作用,但如果您有数字表或不同的数字来源,那就没关系,例如以下工作量最多为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;

答案 1 :(得分:1)

您可以使用以下脚本

来实现结果
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.js"></script>

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