分组和切换列和行

时间:2013-08-16 22:01:25

标签: sql sql-server tsql pivot sql-server-2012

我不知道这是否会被正式称为枢轴,但我想要的结果是:

+------+---------+------+
| Alex | Charley | Liza |
+------+---------+------+
|  213 |     345 |    1 |
|   23 |     111 |    5 |
|   42 |      52 |    2 |
|  323 |         |    5 |
|   23 |         |    1 |
|  324 |         |    5 |
+------+---------+------+

我的输入数据采用以下格式:

+-----+---------+
| Apt |  Name   |
+-----+---------+
| 213 | Alex    |
|  23 | Alex    |
|  42 | Alex    |
| 323 | Alex    |
|  23 | Alex    |
| 324 | Alex    |
| 345 | Charley |
| 111 | Charley |
|  52 | Charley |
|   1 | Liza    |
|   5 | Liza    |
|   2 | Liza    |
|   5 | Liza    |
|   1 | Liza    |
|   5 | Liza    |
+-----+---------+

因为我有大约100个名字,所以我不想做这个

的大量子查询
select null, null, thirdcolumn from...
select null, seconcolumn from...
select firstcolumn from...

有没有办法用PIVOT或其他方式执行此操作?

2 个答案:

答案 0 :(得分:2)

您可以使用动态PIVOTROW_NUMBER()功能执行此操作:

DECLARE @cols AS VARCHAR(1000),
        @query  AS VARCHAR(8000)
SELECT @cols = STUFF((SELECT ',' +   QUOTENAME(Name) 
                    FROM (SELECT DISTINCT Name
                          FROM #test
                          )sub
                    ORDER BY Name
                    FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)') 
                    ,1,1,'')
PRINT @cols

SET @query = '
WITH cte AS (SELECT DISTINCT *
             FROM  #test)
    ,cte2 AS (SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Apt)RowRank
             FROM  cte)
SELECT * 
FROM  cte2 
PIVOT (max(Apt) for Name in ('+@cols+')) p
            '
EXEC (@query)

SQL Fiddle - Distinct List, Specific Order

编辑:如果您不希望列表不同,请删除上面的第一个cte,如果您想保持任意顺序,请将ORDER BY更改为(SELECT 1)

DECLARE @cols AS VARCHAR(1000),
        @query  AS VARCHAR(8000)
SELECT @cols = STUFF((SELECT ',' +   QUOTENAME(Name) 
                    FROM (SELECT DISTINCT Name
                          FROM #test
                          )sub
                          ORDER BY Name
                    FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)') 
                    ,1,1,'')
PRINT @cols

SET @query = '
WITH cte AS (SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY (SELECT 1))RowRank
             FROM  #test)
SELECT * 
FROM  cte 
PIVOT (max(Apt) for Name in ('+@cols+')) p
            '
EXEC (@query)

SQL Fiddle - Full List, Arbitrary Order

最后,如果您不想在结果中使用RowRank字段,只需重新使用@cols中的SELECT变量:

SET @query = '
WITH cte AS (SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY (SELECT 1))RowRank
             FROM  #test)
SELECT '+@cols+' 
FROM  cte 
PIVOT (max(Apt) for Name in ('+@cols+')) p
            '
EXEC (@query)

答案 1 :(得分:2)

哦,这有点痛苦,但是你可以用SQL做到这一点。您正在尝试连接列。

select seqnum,
       max(case when name = 'Alex' then apt end) as Alex,
       max(case when name = 'Charley' then apt end) as Charley,
       max(case when name = 'Liza' then apt end) as Liza
from (select t.*, row_number() over (partition by name order by (select NULL)) as seqnum
      from t
     ) t
group by seqnum
order by seqnum;

作为注释:无法保证每列中的原始排序相同。如您所知,SQL表本质上是无序的,因此您需要一个列来指定排序。

要处理多个名称,我只需使用如下查询获取列表:

select distinct 'max(case when name = '''+name+''' then apt end) as '+name+','
from t;

将结果复制到查询中。