行没有特定列标题的列

时间:2014-01-09 15:28:35

标签: tsql sql-server-2008-r2

我有一个返回行中数据的查询,我需要更改结果,因此结果是列而不是行。我做过研究并发现this文章有一个可以使用的动态查询,但似乎我的情况似乎不能使用该解决方案。该解决方案似乎依赖于每一行都有一个可用作列名的唯一名称,我没有。

我的数据包含客户对我们工厂的访问的客户记录。有些客户只会看到我们一次,有些客户会在同一时间段内多次看到我们,因此我们无法预测在给定时间段内每位客户的访问次数。注意ID 10219只有一次访问,5180有3次,5199有很多次。

ID      Task                Visit Date      RF Score    PF Score
10219   Follow Up Visit     12/26/2013      1           6
5180    Initial Visit       6/9/2011        3           9
5180    Follow Up Visit     7/8/2011        3           10
5180    Follow Up Visit     9/2/2011        1           10
5199    Follow Up Visit     9/15/2011       2           7
5199    Follow Up Visit     9/8/2011        5           6
5199    Follow Up Visit     10/27/2011      4           7
5199    Follow Up Visit     10/20/2011      2           4
5199    Follow Up Visit     10/13/2011      4           8
5199    Follow Up Visit     11/17/2011      3           4
5199    Follow Up Visit     11/10/2011      2           5
5199    Follow Up Visit     11/3/2011       3           3

对于像这样结构化的数据,任何人都知道如何动态地将这些行转换为列,即使我不知道需要多少列?

编辑:最终结果应如下所示:

    ID          Task1               Visit Date1     RF Score1   PF Score1   Task2               Visit Date2     RF Score2   PF Score2   Task3               Visit Date3     RF Score3   PF Score3   
   5180     Initial Visit           6/9/2011        3           9           Follow Up Visit     7/8/2011        3           10          Follow Up Visit     9/2/2011        1           10

1 个答案:

答案 0 :(得分:2)

solution that you link to适用于您的情况,但您必须稍微调整它,因为您想要转向多列数据。由于您需要转动多个列,因此您首先要将Visit DateTaskRf ScorePf Score列拆分为多行,然后应用pivot功能。除了取消隐私的过程之外,我还建议使用windowing function之类的row_number来为每个id生成一个具有日期的唯一序列。

您将使用以下内容开始查询:

select id, task, [visit date], [rf score], [pf score],
  row_number() over(partition by id
                    order by [visit date]) seq
from yourtable

SQL Fiddle with Demo。这会创建一个数字,用于将Visit DateTaskRf ScorePf Score中的每个值与实际访问次数相关联。

获得此行号后,您将需要将多列拆分为多行数据。有几种方法可以做到这一点,包括使用unpivot功能。但是,由于您使用的是SQL Server 2008R2,因此可以将CROSS APPLYVALUES一起使用:

select id, 
  col = col + cast(seq as varchar(10)),
  value
from 
(
  select id, task, [visit date], [rf score], [pf score],
    row_number() over(partition by id
                      order by [visit date]) seq
  from yourtable
) d
cross apply
(
  values
    ('VisitDate', convert(varchar(10), [visit date], 120)),
    ('Task', [task]),
    ('RfScore', cast([rf score] as varchar(10))),
    ('PfScore', cast([pf score] as varchar(10)))
) c (col, value)

SQL Fiddle with Demo。您的数据现在采用可以轻松转动的格式:

|    ID |        COL |           VALUE |
|-------|------------|-----------------|
|  5180 | VisitDate1 |      2011-06-09 |
|  5180 |      Task1 |   Initial Visit |
|  5180 |   RfScore1 |               3 |
|  5180 |   PfScore1 |               9 |
|  5180 | VisitDate2 |      2011-07-08 |
|  5180 |      Task2 | Follow Up Visit |
|  5180 |   RfScore2 |               3 |
|  5180 |   PfScore2 |              10 |

添加PIVOT时的代码将是:

select id, 
  VisitDate1, Task1, RfScore1, PfScore1,
  VisitDate2, Task2, RfScore2, PfScore2,
  VisitDate3, Task3, RfScore3, PfScore3,
  VisitDate4, Task4, RfScore4, PfScore4,
  VisitDate5, Task5, RfScore5, PfScore5,
  VisitDate6, Task6, RfScore6, PfScore6
from
(
  select id, 
    col = col + cast(seq as varchar(10)),
    value
  from 
  (
    select id, task, [visit date], [rf score], [pf score],
      row_number() over(partition by id
                        order by [visit date]) seq
    from yourtable
  ) d
  cross apply
  (
    values
      ('VisitDate', convert(varchar(10), [visit date], 120)),
      ('Task', [task]),
      ('RfScore', cast([rf score] as varchar(10))),
      ('PfScore', cast([pf score] as varchar(10)))
  ) c (col, value)
) d
pivot
(
  max(value)
  for col in (VisitDate1, Task1, RfScore1, PfScore1,
              VisitDate2, Task2, RfScore2, PfScore2,
              VisitDate3, Task3, RfScore3, PfScore3,
              VisitDate4, Task4, RfScore4, PfScore4,
              VisitDate5, Task5, RfScore5, PfScore5,
              VisitDate6, Task6, RfScore6, PfScore6)
) 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 id
                                                order by [visit date]) seq
                      from yourtable
                    ) d
                    cross apply
                    (
                      select 'VisitDate', 1 union all
                      select 'Task', 2 union all
                      select 'RfScore', 3 union all
                      select 'PfScore', 4
                    ) c (col, so)
                    group by seq, col, so
                    order by seq, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'select id,  ' + @cols + ' 
            from
            (
              select id, 
                col = col + cast(seq as varchar(10)),
                value
              from
              (
                select id, task, [visit date], [rf score], [pf score],
                  row_number() over(partition by id
                                    order by [visit date]) seq
                from yourtable
              ) d
              cross apply
              (
                values
                  (''VisitDate'', convert(varchar(10), [visit date], 120)),
                  (''Task'', task),
                  (''RfScore'', cast([rf score] as varchar(10))),
                  (''PfScore'', cast([pf score] as varchar(10)))
              ) c (col, value)
            ) s
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute sp_executesql @query;

SQL Fiddle with Demo。两个版本都给出了结果:

|    ID | VISITDATE1 |           TASK1 | RFSCORE1 | PFSCORE1 | VISITDATE2 |           TASK2 | RFSCORE2 | PFSCORE2 | VISITDATE3 |           TASK3 | RFSCORE3 | PFSCORE3 | VISITDATE4 |           TASK4 | RFSCORE4 | PFSCORE4 | VISITDATE5 |           TASK5 | RFSCORE5 | PFSCORE5 | VISITDATE6 |           TASK6 | RFSCORE6 | PFSCORE6 | VISITDATE7 |           TASK7 | RFSCORE7 | PFSCORE7 | VISITDATE8 |           TASK8 | RFSCORE8 | PFSCORE8 |
|-------|------------|-----------------|----------|----------|------------|-----------------|----------|----------|------------|-----------------|----------|----------|------------|-----------------|----------|----------|------------|-----------------|----------|----------|------------|-----------------|----------|----------|------------|-----------------|----------|----------|------------|-----------------|----------|----------|
|  5180 | 2011-06-09 |   Initial Visit |        3 |        9 | 2011-07-08 | Follow Up Visit |        3 |       10 | 2011-09-02 | Follow Up Visit |        1 |       10 |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |
|  5199 | 2011-09-08 | Follow Up Visit |        5 |        6 | 2011-09-15 | Follow Up Visit |        2 |        7 | 2011-10-13 | Follow Up Visit |        4 |        8 | 2011-10-20 | Follow Up Visit |        2 |        4 | 2011-10-27 | Follow Up Visit |        4 |        7 | 2011-11-03 | Follow Up Visit |        3 |        3 | 2011-11-10 | Follow Up Visit |        2 |        5 | 2011-11-17 | Follow Up Visit |        3 |        4 |
| 10219 | 2013-12-26 | Follow Up Visit |        1 |        6 |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |     (null) |          (null) |   (null) |   (null) |