如何将单个整数值列拆分为n列?

时间:2017-08-08 11:15:40

标签: c# sql sql-server tsql sql-server-2012

我有一个SQL Server表table_name,如:

col1   
1
2
3
4
5
6
7
8
9
.
.
.
N

我想要输出如下

col-1 | col-2 | col-3 | col-4 |col-5
-------------------------------
1     | 2     | 3     | 4     |5
6     | 7     | 8     | 9     |10
11    | 12    | 13    | 14    |15

如何从sql-server查询中获取此输出。

4 个答案:

答案 0 :(得分:6)

另一个选项是Dynamic Pivot。

这将创建N列。

示例

Declare @nCols int  = 5

Declare @SQL varchar(max) = '
Declare @nCols int = '+str(@nCols,5)+'
Select *
 From (
        Select RowNr = ((row_number() over (order by col1)-1)/@nCols)+1
              ,ColNr = concat(''col-'',isnull(nullif((row_number() over (order by col1))%@nCols,0),@nCols))
              ,Value = col1
         From YourTable
      ) A
 Pivot (max(Value) For ColNr in (' + Stuff((Select Top (@nCols) ','+QuoteName(concat('col-',Row_Number() Over (Order By (Select NULL)))) From  YourTable For XML Path('')) ,1,1,'') + ') ) p'
Exec(@SQL);
--Print @SQL

<强>返回

enter image description here

如果@nCols = 3,则结果为:

enter image description here

  

编辑完全参数驱动的版本

此版本可以提供

  1. 列数@NbrCols(与上述相同)
  2. 来源@FromSrc,即表格名称或()中的SQL字符串,即(Select ...)
  3. 列名@ColName到Pivot @ColName
  4. 列前缀@ColPrfx,即'Col-'或甚至''
  5. Declare @NbrCols int  = 5
    Declare @FromSrc varchar(max) = 'YourTable'  -- Or SQL '(Select col ...)'
    Declare @ColName varchar(100) = 'col1'
    Declare @ColPrfx varchar(100) = 'Col-'
    
    Declare @SQL varchar(max) = '
    Declare @NbrCols int = '+str(@NbrCols,5)+'
    Select *
     From (
            Select Row = ((row_number() over (order by '+quotename(@ColName)+')-1)/@NbrCols)+1
                  ,Col = concat('''+@ColPrfx+''',isnull(nullif((row_number() over (order by '+quotename(@ColName)+'))%@NbrCols,0),@NbrCols))
                  ,Val = '+quotename(@ColName)+'
             From '+@FromSrc+' A1
          ) A
     Pivot (max(Val) For Col in (' + Stuff((Select Top (@NbrCols) ','+QuoteName(concat(@ColPrfx,Row_Number() Over (Order By (Select NULL)))) From master..spt_values For XML Path('')) ,1,1,'') + ') ) p'
    Exec(@SQL);
    --Print @SQL
    

答案 1 :(得分:4)

使用基于划分row_number()的条件聚合,并使用modulo %进行列放置:

测试设置:

select n into dbo.numbers from (values 
  (1), (2), (3), (4), (5), (6), (7), (8), (9),(10)
,(11),(12),(13),(14),(15),(16),(17),(18),(19),(20)
) t(n)
delete from dbo.numbers where n in (12,13,17);

查询:

select 
    col1 = sum(case when rn%5=0 then n end)
  , col2 = sum(case when rn%5=1 then n end)
  , col3 = sum(case when rn%5=2 then n end)
  , col4 = sum(case when rn%5=3 then n end)
  , col5 = sum(case when rn%5=4 then n end)
from (
  select n, rn = row_number() over (order by n)-1
  from dbo.numbers
  ) t
group by rn/5;

rextester演示:http://rextester.com/UHKY16981

返回:

+------+------+------+------+------+
| col1 | col2 | col3 | col4 | col5 |
+------+------+------+------+------+
|    1 |    2 | 3    | 4    | 5    |
|    6 |    7 | 8    | 9    | 10   |
|   11 |   14 | 15   | 16   | 18   |
|   19 |   20 | NULL | NULL | NULL |
+------+------+------+------+------+

相同的概念,但使用pivot()而不是条件聚合返回相同的结果。

select 
    col1 = [0]
  , col2 = [1]
  , col3 = [2]
  , col4 = [3]
  , col5 = [4]
from (
  select n
    , rn  = (row_number() over (order by n)-1)%5
    , grp = (row_number() over (order by n)-1)/5
  from dbo.numbers
  ) t
pivot (sum(n) for rn in ([0],[1],[2],[3],[4])) p;

答案 2 :(得分:2)

假设你的表不是简单地保持从1到n的连续数字(如果确实如此,Gordon的回答更好), 这是一种方法:

首先,创建并填充样本表(保存是您未来问题中的此步骤)

SELECT TOP 100 IDENTITY(int,1,1) AS col1
    INTO table_name
    FROM sys.objects s1       
    CROSS JOIN sys.objects s2 


DELETE
FROM table_name
WHERE col1 IN
(
    SELECT TOP 67 col1
    FROM table_name
    ORDER BY NEWID()
)

(table_name现在包含33个1到100之间的随机数)

使用几个公用表表达式来获取列号和行号:

;With Cols as
(
    SELECT  col1, 
            ROW_NUMBER() OVER(ORDER BY col1) % 5 ColNumber
    FROM table_name
), RowsAndCols as
(
    SELECT col1, ColNumber, ROW_NUMBER() OVER(PARTITION BY ColNumber ORDER BY col1) As RowNumber
    FROM Cols
)

查询:

SELECT c1.col1, c2.col1 As col2, c3.col1 As col3, c4.col1 As col4, c5.col1 As col5
FROM RowsAndCols c1
LEFT JOIN RowsAndCols c2 ON c1.RowNumber = c2.RowNumber AND c2.ColNumber = 2
LEFT JOIN RowsAndCols c3 ON c1.RowNumber = c3.RowNumber AND c3.ColNumber = 3
LEFT JOIN RowsAndCols c4 ON c1.RowNumber = c4.RowNumber AND c4.ColNumber = 4
LEFT JOIN RowsAndCols c5 ON c1.RowNumber = c5.RowNumber AND c5.ColNumber = 0
WHERE c1.ColNumber = 1

注意我已经使用了左连接,所以如果行数不是5的倍数,你将在最后一行的最后一列中得到空值。

See a live demo on rexteser.

答案 3 :(得分:1)

如果你想要那个输出,你甚至不需要从表开始:

with cte as (
      select 1 as col1, 2 as col2, 3 as col3, 4 as col4, 5 as col5
      union all
      select 5 + col1, 5 + col2, 5 + col3, 5 + col4, 5 + col5
      from cte
      where 5 + col1 <= 15
     )
select *
from cte;