无聚合时转置行和列

6

我有以下数据集

Account Contact

1   324324324
1   674323234
2   833343432
2   433243443
3   787655455
4   754327545
4   455435435
5   543544355
5   432455553
5   432433242
5   432432432

我希望输出如下所示:
Account Contact1    Contact2    Contact3    Contact4

1   324324324   674323234       
2   833343432   433243443       
3   787655455           
4   754327545   455435435       
5   543544355   432455553   432433242   432432432

问题也在于我有数量不固定的账户和联系人。
2个回答

10

如果您要应用 PIVOT 函数,您需要使用聚合函数来获得结果,但您还希望使用窗口函数如 row_number() 为帐户中的每个联系人生成唯一的序列。

首先,您需要查询类似于:

select account, contact,
  'contact'
    + cast(row_number() over(partition by account
                              order by contact) as varchar(10)) seq
from yourtable

请查看带演示的SQL Fiddle。这将创建一个新列,其中包含唯一的序列:

| ACCOUNT |   CONTACT |      SEQ |
|---------|-----------|----------|
|       1 | 324324324 | contact1 |
|       1 | 674323234 | contact2 |

如果您的列数很有限,那么您可以硬编码您的查询:

select account,
  contact1, contact2, contact3, contact4
from 
(
  select account, contact,
    'contact'
      + cast(row_number() over(partition by account
                                order by contact) as varchar(10)) seq
  from yourtable
) d
pivot
(
  max(contact)
  for seq in (contact1, contact2, contact3, contact4)
) piv;

请查看带演示的SQL Fiddle

如果您有未知数量的列,则必须使用动态SQL:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(seq) 
                    from
                    (
                      select 'contact'
                              + cast(row_number() over(partition by account
                                                        order by contact) as varchar(10)) seq
                      from yourtable
                    ) d
                    group by seq
                    order by seq
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT account, ' + @cols + ' 
            from 
            (
                select account, contact,
                  ''contact''
                    + cast(row_number() over(partition by account
                                              order by contact) as varchar(10)) seq
                from yourtable
            ) x
            pivot 
            (
                max(contact)
                for seq in (' + @cols + ')
            ) p '

execute sp_executesql @query;

请参考具有演示的SQL Fiddle。两种方法都会给您一个结果:

| ACCOUNT |  CONTACT1 |  CONTACT2 |  CONTACT3 |  CONTACT4 |
|---------|-----------|-----------|-----------|-----------|
|       1 | 324324324 | 674323234 |    (null) |    (null) |
|       2 | 433243443 | 833343432 |    (null) |    (null) |
|       3 | 787655455 |    (null) |    (null) |    (null) |
|       4 | 455435435 | 754327545 |    (null) |    (null) |
|       5 | 432432432 | 432433242 | 432455553 | 543544355 |

2

这是一种略微不同的生成动态 PIVOT 的方法:

DECLARE @c INT;

SELECT TOP 1 @c = COUNT(*) 
  FROM dbo.YourTable
  GROUP BY Account 
  ORDER BY COUNT(*) DESC;

DECLARE @dc1 NVARCHAR(MAX) = N'', @dc2 NVARCHAR(MAX) = N'', @sql NVARCHAR(MAX);

SELECT @dc1 += ',Contact' + RTRIM(i), @dc2 += ',[Contact' + RTRIM(i) + ']'
  FROM (SELECT TOP (@c) i = number + 1 
  FROM master.dbo.spt_values WHERE type = N'P' ORDER BY number) AS x;

SET @sql = N'SELECT Account ' + @dc1 + 
  ' FROM (SELECT Account, Contact, rn = ''Contact'' 
      + RTRIM(ROW_NUMBER() OVER (PARTITION BY Account ORDER BY Contact))
    FROM dbo.YourTable) AS src PIVOT (MAX(Contact) FOR rn IN (' 
      + STUFF(@dc2, 1, 1, '') + ')) AS p;';

EXEC sp_executesql @sql;

SQLiddle demo


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接