1

我有这种表:

Name   Date      Value
-----------------------
Test 1/1/2001   10
Test 2/1/2001   17
Test 3/1/2001   52
Foo  5/4/2011   15
Foo  6/4/2011   321
My   15/5/2005  36
My   25/7/2005  75

我想展示这样的结果:

Name   Date      Value  Name   Date      Value  Name   Date      Value
---------------------------------------------------------------------
Test 1/1/2001    10      Foo  5/4/2011   15      My   15/5/2005  36
Test 2/1/2001    17      Foo  6/4/2011   321     My   25/7/2005  75
Test 3/1/2001    52

我需要显示与我的名称列中存在的一样多的列

我怎么能在 Sql 中做到这一点?

4

2 回答 2

6

为了获得您想要的结果,您将不得不取消透视表中的列并应用透视函数。

unpivot可以使用函数完成,UNPIVOT可以使用CROSS APPLYwith VALUES

取消透视:

select rn, 
  col +'_'+cast(dr as varchar(10)) col, 
  new_values
from
(
  select name, 
    convert(varchar(10), date, 101) date, 
    cast(value as varchar(10)) value,
    dense_rank() over(order by name) dr,
    row_number() over(partition by name order by date) rn
  from yourtable
) d
unpivot
(
  new_values
  for col in (name, date, value)
) un;

交叉申请:

select rn, 
  col +'_'+cast(dr as varchar(10)) col, 
  c.value
from
(
  select name, 
    convert(varchar(10), date, 101) date, 
    cast(value as varchar(10)) value,
    dense_rank() over(order by name) dr,
    row_number() over(partition by name order by date) rn
  from yourtable
) d
cross apply
(
  values
    ('Name', name), ('Date', date), ('Value', Value)
) c (col, value);

请参阅两个版本的带有演示的 SQL Fiddle 。这给出了结果:

| RN |     COL | NEW_VALUES |
-----------------------------
|  1 |  name_1 |        Foo |
|  1 |  date_1 | 04/05/2011 |
|  1 | value_1 |         15 |
|  2 |  name_1 |        Foo |
|  2 |  date_1 | 04/06/2011 |
|  2 | value_1 |        321 |
|  1 |  name_2 |         My |
|  1 |  date_2 | 05/15/2005 |
|  1 | value_2 |         36 |

这些查询获取您现有的列值并将它们转换为行。一旦它们在行中,您可以使用窗口函数创建新的列名dense_rank

将数据转换为行后,您可以使用新的列名(使用dense_rank值创建)并应用该PIVOT函数。

使用 UNPIVOT 进行 PIVOT:

select name_1, date_1, value_1,
  name_2, date_2, value_2,
  name_3, date_3, value_3
from
(
  select rn, 
    col +'_'+cast(dr as varchar(10)) col, 
    new_values
  from
  (
    select name, 
      convert(varchar(10), date, 101) date, 
      cast(value as varchar(10)) value,
      dense_rank() over(order by name) dr,
      row_number() over(partition by name order by date) rn
    from yourtable
  ) d
  unpivot
  (
    new_values
    for col in (name, date, value)
  ) un
) src
pivot
(
  max(new_values)
  for col in (name_1, date_1, value_1,
              name_2, date_2, value_2,
              name_3, date_3, value_3)
) piv;

请参阅带有演示的 SQL Fiddle

PIVOT 与 CROSS APPLY:

select name_1, date_1, value_1,
  name_2, date_2, value_2,
  name_3, date_3, value_3
from
(
  select rn, 
    col +'_'+cast(dr as varchar(10)) col, 
    c.value
  from
  (
    select name, 
      convert(varchar(10), date, 101) date, 
      cast(value as varchar(10)) value,
      dense_rank() over(order by name) dr,
      row_number() over(partition by name order by date) rn
    from yourtable
  ) d
  cross apply
  (
    values
      ('Name', name), ('Date', date), ('Value', Value)
  ) c (col, value)
) src
pivot
(
  max(value)
  for col in (name_1, date_1, value_1,
              name_2, date_2, value_2,
              name_3, date_3, value_3)
) piv;

请参阅SQL Fiddle with Demo

动态枢轴:

如果您的列数有限或已知,上述版本将非常有用,如果没有,那么您将需要使用动态 SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(col +'_'+cast(dr as varchar(10)))
                    from 
                    (
                      select dense_rank() over(order by name) dr
                      from yourtable
                    ) t
                    cross apply
                    (
                      values(1, 'Name'), (2, 'Date'), (3, 'Value')
                    ) c (sort, col)
                    group by col, dr, sort
                    order by dr, sort
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT ' + @cols + ' 
              from 
             (
                select rn, 
                  col +''_''+cast(dr as varchar(10)) col, 
                  c.value
                from
                (
                  select name, 
                    convert(varchar(10), date, 101) date, 
                    cast(value as varchar(10)) value,
                    dense_rank() over(order by name) dr,
                    row_number() over(partition by name order by date) rn
                  from yourtable
                ) d
                cross apply
                (
                  values
                    (''Name'', name), (''Date'', date), (''Value'', Value)
                ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p'

execute(@query)

请参阅SQL Fiddle with Demo

每个查询的结果是:

| NAME_1 |     DATE_1 | VALUE_1 | NAME_2 |     DATE_2 | VALUE_2 | NAME_3 |     DATE_3 | VALUE_3 |
-------------------------------------------------------------------------------------------------
|    Foo | 04/05/2011 |      15 |     My | 05/15/2005 |      36 |   Test | 01/01/2001 |      10 |
|    Foo | 04/06/2011 |     321 |     My | 07/25/2005 |      75 |   Test | 01/02/2001 |      17 |
| (null) |     (null) |  (null) | (null) |     (null) |  (null) |   Test | 01/03/2001 |      52 |
于 2013-03-21T16:47:47.423 回答
0

手动或使用程序。大多数人习惯于以线性方式显示输出(默认)。如果有人要求您这样做,您可以告诉他们这不是应用程序的工作方式。您可以将结果集导出为 csv,然后将其导入 Excel 之类的文件中并手动重新格式化,或者使用 ASP.net 或 PHP 等服务器端语言将结果格式化为表格。

当您解析输出时,您可以根据当前检查最后一个 var Name。如果它们不同,则添加一列。编写脚本仍然很棘手,因为它们很可能会按顺序从数据库中出来。所以你会有一个像 test, test, test, foo, foo 这样的序列,这意味着你需要创建一个多维数组来组织数据以获得列数。然后根据该表设置表,并使用一个计数器来计算行名,然后是下面的数据。

我不确定您熟悉哪些应用程序,因此在 PHP 中,多维数组的输出看起来像这样。

row [1]['name']=test
row [1][test][1]['date'] = 1/1/2001

不过,这更像是一种视觉输出。数据库旨在保存数据并以直观的方式返回。

于 2013-03-21T16:33:05.783 回答