-1

对于像这样的表:

COL1    COL2    COL3    COL4
item1   7/29/13 cat     blue
item3   7/29/13 fish    purple
item1   7/30/13 rat     green
item2   7/30/13 bat     grey
item3   7/30/13 bird    orange

您将如何 PIVOT 以通过 COL2 获取行,所有其他列作为 COL1 值作为块重复?

COL2    COL1    COL3    COL4    COL1    COL3    COL4    COL1    COL3    COL4
7/29/13 item1   cat     blue    item2   NULL    NULL    item3   fish    purple
7/30/13 item1   rat     green   item2   bat     grey    item3   bird    orange
4

2 回答 2

2

为了得到这个结果,你需要做一些事情:

  1. col1从和中获取不同的值列表col2
  2. 取消透视列中的数据col1col3并且col4
  3. 从 unpivot 中旋转结果

要获取日期和项目(col1 和 col2)的不同列表以及现有表中的值,您需要使用类似于以下内容的内容:

select t.col1, t.col2,
  t2.col3, t2.col4,
  row_number() over(partition by t.col2
                      order by t.col1) seq
from 
(
  select distinct t.col1, c.col2
  from yourtable t
  cross join 
  (
    select distinct col2
    from yourtable
  ) c
) t
left join yourtable t2
  on t.col1 = t2.col1
  and t.col2 = t2.col2;

请参阅SQL Fiddle with Demo。获得此列表后,您将需要对数据进行反透视。有几种方法可以做到这一点,使用 UNPIVOT 函数或使用 CROSS APPLY:

select d.col2,
  col = col+'_'+cast(seq as varchar(10)),
  value
from
(
  select t.col1, t.col2,
    t2.col3, t2.col4,
    row_number() over(partition by t.col2
                        order by t.col1) seq
  from 
  (
    select distinct t.col1, c.col2
    from yourtable t
    cross join 
    (
      select distinct col2
      from yourtable
    ) c
  ) t
  left join yourtable t2
    on t.col1 = t2.col1
    and t.col2 = t2.col2
) d
cross apply
(
  select 'col1', col1 union all
  select 'col3', col3 union all
  select 'col4', col4 
) c (col, value);

请参阅SQL Fiddle with Demo。这将为您提供如下所示的数据:

|                        COL2 |    COL |  VALUE |
-------------------------------------------------
| July, 29 2013 00:00:00+0000 | col1_1 |  item1 |
| July, 29 2013 00:00:00+0000 | col3_1 |    cat |
| July, 29 2013 00:00:00+0000 | col4_1 |   blue |
| July, 29 2013 00:00:00+0000 | col1_2 |  item2 |
| July, 29 2013 00:00:00+0000 | col3_2 | (null) |
| July, 29 2013 00:00:00+0000 | col4_2 | (null) |

最后,您将对col列中的项目应用 PIVOT 函数:

select col2, 
  col1_1, col3_1, col4_1, 
  col1_2, col3_2, col4_2, 
  col1_3, col3_3, col4_3
from
(
  select d.col2,
    col = col+'_'+cast(seq as varchar(10)),
    value
  from
  (
    select t.col1, t.col2,
      t2.col3, t2.col4,
      row_number() over(partition by t.col2
                          order by t.col1) seq
    from 
    (
      select distinct t.col1, c.col2
      from yourtable t
      cross join 
      (
        select distinct col2
        from yourtable
      ) c
    ) t
    left join yourtable t2
      on t.col1 = t2.col1
      and t.col2 = t2.col2
  ) d
  cross apply
  (
    select 'col1', col1 union all
    select 'col3', col3 union all
    select 'col4', col4 
  ) c (col, value)
) src
pivot
(
  max(value)
  for col in (col1_1, col3_1, col4_1, 
              col1_2, col3_2, col4_2, 
              col1_3, col3_3, col4_3)
)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 col2
                                                  order by col1) seq
                      from yourtable
                    ) t
                    cross apply
                    (
                      select 'col1', 1 union all
                      select 'col3', 2 union all
                      select 'col4', 3
                    ) c (col, so)
                    group by col, seq, so
                    order by seq, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT col2, ' + @cols + ' 
            from 
            (
                select d.col2,
                  col = col+''_''+cast(seq as varchar(10)),
                  value
                from
                (
                  select t.col1, t.col2,
                    t2.col3, t2.col4,
                    row_number() over(partition by t.col2
                                        order by t.col1) seq
                  from 
                  (
                    select distinct t.col1, c.col2
                    from yourtable t
                    cross join 
                    (
                      select distinct col2
                      from yourtable
                    ) c
                  ) t
                  left join yourtable t2
                    on t.col1 = t2.col1
                    and t.col2 = t2.col2
                ) d
                cross apply
                (
                  select ''col1'', col1 union all
                  select ''col3'', col3 union all
                  select ''col4'', col4 
                ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute sp_executesql @query;

请参阅SQL Fiddle with Demo。所有版本都会给出一个结果:

|                        COL2 | COL1_1 | COL3_1 | COL4_1 | COL1_2 | COL3_2 | COL4_2 | COL1_3 | COL3_3 | COL4_3 |
----------------------------------------------------------------------------------------------------------------
| July, 29 2013 00:00:00+0000 |  item1 |    cat |   blue |  item2 | (null) | (null) |  item3 |   fish | purple |
| July, 30 2013 00:00:00+0000 |  item1 |    rat |  green |  item2 |    bat |   grey |  item3 |   bird | orange |
于 2013-07-30T18:20:47.323 回答
0

动态UNPIVOT+PIVOT方法总是很酷,当为一组已知和有限的值做这种事情时,后续JOIN的工作也很好(在SELECT列表上懒惰):

WITH cte AS (SELECT *,ROW_NUMBER() OVER (PARTITION BY COL2 ORDER BY COL1)'RowRank'
             FROM  #Table1)
SELECT *
FROM  cte a
LEFT JOIN cte b
  ON a.COL2 = b.COL2
  AND a.RowRank = b.RowRank - 1
LEFT JOIN cte c
  ON b.COL2 = c.COL2
  AND b.RowRank = c.RowRank - 1
WHERE a.RowRank = 1

或者,如果要维护字段的顺序:

WITH cte AS (SELECT a.*,b.RowRank
             FROM  #Table1 a
             JOIN (SELECT Col1,ROW_NUMBER() OVER (ORDER BY Col1)'RowRank' 
                   FROM #Table1
                   GROUP BY COL1) b
                 ON a.Col1 = b.Col1)
SELECT *
FROM  cte a
LEFT JOIN cte b
  ON a.COL2 = b.COL2
  AND a.RowRank = b.RowRank - 1
LEFT JOIN cte c
ON a.COL2 = c.COL2
  AND a.RowRank = c.RowRank - 2
WHERE a.RowRank = 1

但是,如果没有“锚”值,这将分崩离析,即如果item1给定日期没有记录,则不会包括在内。

于 2013-07-30T19:09:16.297 回答