按第一列分组,并拆分(pivot?)剩余两列

时间:2013-06-27 21:57:30

标签: sql-server sql-server-2008 tsql

这里的TSQL问题。请参阅下图中的源和所需输出。还提供了构建源表的代码。

enter image description here


DECLARE @tablevar TABLE(
record nvarchar(10),
category nvarchar(50),
value float)

INSERT INTO @tablevar
VALUES
('110-AL','credits_cle',1),
('110-AL','credits_ethics',2),
('110-AR','credits_ethics',2.5),
('110-AZ','credits_prof_resp',1.5),
('110-AZ', 'credits_ethics',5),
('110-AZ', 'credits_cle',4)

3 个答案:

答案 0 :(得分:5)

由于你想要PIVOT两列数据,你可以这样做的一种方法是同时应用UNPIVOT和PIVOT函数。 UNPIVOT会将多列categoryvalue转换为多行,然后您可以应用PIVOT来获得最终结果:

select record, 
  category1, value1, 
  category2, value2, 
  category3, value3
from
(
  select record, col+cast(seq as varchar(10)) col, val
  from
  (
    select record, category, 
      cast(value as nvarchar(50)) value,
      row_number() over(partition by record order by category) seq
    from tablevar
  ) d
  unpivot
  (
    val
    for col in (category, value)
  ) unpiv
) src
pivot
(
  max(val)
  for col in (category1, value1, category2, value2, category3, value3)
) piv;

请参阅SQL Fiddle with Demo

如果您有不确定数量的值,那么您将不得不使用类似于此的动态SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(col+cast(seq as varchar(10))) 
                    from
                    (
                      select row_number() over(partition by record order by category) seq
                      from tablevar
                    ) d
                    cross apply
                    (
                      select 'category', 1 union all
                      select 'value', 2
                    ) c (col, so)
                    group by seq, so, col
                    order by seq, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = 'SELECT record,' + @cols + ' 
             from 
             (
               select record, col+cast(seq as varchar(10)) col, val
                from
                (
                  select record, category, 
                    cast(value as nvarchar(50)) value,
                    row_number() over(partition by record order by category) seq
                  from tablevar
                ) d
                unpivot
                (
                  val
                  for col in (category, value)
                ) unpiv
            ) x
            pivot 
            (
                max(val)
                for col in (' + @cols + ')
            ) p '

execute(@query);

请参阅SQL Fiddle with Demo

答案 1 :(得分:2)

这是我第一次使用PIVOT,代码可能非常难看。这是:

with ranked as (
    select *, RANK() OVER (PARTITION by record ORDER by category) as r
    from @tablevar
), labeled as (
    select record, category as content, 'category' + CAST(r as varchar(MAX)) as label
     from ranked
    union all
    select record, cast(value AS nvarchar(MAX)),  'value' + CAST(r as varchar(MAX)) as label
     from ranked) --select * from labeled
select record, [category1] as [category], [value1] as [value], [category2] as [category], [value2] as [value], [category3] as [category], [value3] as [value]
from (SELECT * FROM labeled) as source
PIVOT(
    max(content)
    for label in ([category1], [value1], [category2], [value2], [category3], [value3])) as pvt

答案 2 :(得分:2)

这是我对它的刺痛

;with Z as
(
select record, category, value,  ROW_NUMBER() over (partition by record order by category) as ranker
from @tablevar
) 
select Z2.record, Z2.c1, Z3.v1, Z2.c2, Z3.v2, Z2.c3, Z3.v3 from 
(
select  record, [1] c1, [2] c2, [3] c3 from 
(select record, category, ranker from Z) as Z0
pivot
( min(category) for ranker in ([1], [2], [3])) as pvt
) Z2

join
(
select  record, [1] v1, [2] v2, [3] v3 from 
(select record, value, ranker from Z) as Z1
pivot
( min(value) for ranker in ([1], [2], [3])) as pvt
) Z3 
on Z2.record = Z3.record