【问题标题】:Split the number range to individual number rows [duplicate]将数字范围拆分为单个数字行[重复]
【发布时间】:2018-01-31 23:07:08
【问题描述】:

我有如下数据

NumberFrom - NumberTo
1          - 2
3          - 3
5          - 7
...
...

我想将它们拆分为单独的行,如下所示

Number
1
2
3
5
6
7
.
.
.

请提出方法。

谢谢

【问题讨论】:

  • 因此,在您的示例中,您有一个定义范围的列还是两列
  • @LearnSQL 我在 Google 上搜索了“sql server convert a range to individual rows”,不到 10 秒就找到了答案。当您学习编程时,学习如何使用 Google 将是您最有价值的工具/技能。
  • 请添加 DDL 并插入 yourtable 的脚本。你用的是什么版本的sql。
  • 它的两个不同的列

标签: sql sql-server tsql


【解决方案1】:

我会使用递归 CTE 来做到这一点:

with cte as (
      select numberfrom, numberto
      from t
      union all
      select numberfrom + 1, numberto
      from cte
      where numberfrom < numberto
     )
select numberfrom as number
from cte;

默认情况下,范围限制为 100,但如果需要,您可以使用 maxrecursion 选项来支持更大的范围。

【讨论】:

  • 您是否阅读过 Jeff Moden 关于使用 rcte 进行此类计数的文章?他证明了在引擎盖下它是 RBAR 处理。 sqlservercentral.com/articles/T-SQL/74118
  • 看来应该符合我的要求,谢谢。
  • @sean lange:非常好的文章。
  • @learnSQL:这篇文章有很多东西要学 ;-)
  • @SeanLange 。 . .有趣的。我注意到 Aaron Bertrand 对用于计数的递归 CTE 没有那么苛刻的看法:sqlblog.org/2009/10/07/…。无论如何,我坦率地承认“计数”或“数字”表可能更有效。当然,SQL Server 可以实现诸如generate_series() 之类的功能,使所有这些讨论都过时了。
【解决方案2】:

递归 CTE 少的解决方案看起来像....

Declare @t table (NumberFrom INT ,  NumberTo INT)
INSERT INTO @t values
(1 , 2),
(3 , 3),
(5 , 7),
(9 , 11)

Select * 
from @t t
CROSS APPLY (  SELECT TOP ( IIF(
                                ABS(t.NumberTo - t.NumberFrom) = 0 
                                , 1 
                                , ABS(t.NumberTo - t.NumberFrom) + 1) )
                        t.NumberFrom +  ROW_NUMBER() OVER (ORDER BY (Select NULL)) - 1 Vals
                FROM master..spt_values x CROSS JOIN master..spt_values y
            ) c

对于早于 2012 年的 SQL Server

我之前使用过 IIF() 函数,如果您使用的是 SQL Server 2005 或 2008,您可以在 TOP 子句中使用 Case 语句,类似这样...

Declare @t table (NumberFrom INT ,  NumberTo INT)
INSERT INTO @t values
(1 , 2),
(3 , 3),
(5 , 7),
(9 , 11)

Select * 
from @t t
CROSS APPLY (  SELECT TOP ( CASE WHEN
                                ABS(t.NumberTo - t.NumberFrom) = 0 
                                THEN 1 
                                ELSE ABS(t.NumberTo - t.NumberFrom) + 1 END )
                        t.NumberFrom +  ROW_NUMBER() OVER (ORDER BY (Select NULL)) - 1 Vals
                FROM master..spt_values x CROSS JOIN master..spt_values y
            ) c

【讨论】:

    【解决方案3】:

    另一种选择是使用临时计数表与 CROSS APPLY。

    示例

    Select B.* 
     From  YourTable A
     Cross Apply (
                    Select Top (NumberTo-NumberFrom+1) N=NumberFrom-1+Row_Number() Over (Order By (Select NULL)) 
                     From  master..spt_values n1 ,master..spt_values n2 
                 ) B
    

    注意:如果跨度不超过 2,500,请删除 ,master..spt_values n2

    退货

    N
    1
    2
    3
    5
    6
    7
    

    【讨论】:

      【解决方案4】:

      试试这个方法

      DECLARE @T TABLE
      (
          NumRange VARCHAR(20)
      )
      
      INSERT INTO @T 
      VALUES('1-2'),('5-7'),('3-3')
      
      ;WITH CTE
      AS
      (
          SELECT
              StrtVal = CAST(SUBSTRING(NumRange,1,CHARINDEX('-',NumRange)-1) AS INT),
              ENdVal = CAST(SUBSTRING(NumRange,CHARINDEX('-',NumRange)+1,LEN(NumRange)) AS INT)
              FROM @T
      
          UNION ALL
      
          SELECT
              StrtVal = StrtVal+1,
              ENdVal
              FROM CTE
                  WHERE StrtVal<ENdVal
      )
      SELECT
          StrtVal
          FROM CTE
              ORDER BY StrtVal
      

      【讨论】:

      • 感谢大家提出的许多宝贵建议。这将有助于下次。
      猜你喜欢
      • 1970-01-01
      • 2016-06-06
      • 1970-01-01
      • 1970-01-01
      • 2020-08-05
      • 1970-01-01
      • 1970-01-01
      • 2017-07-13
      • 2016-07-03
      相关资源
      最近更新 更多