SQLServer 2008 PIVOT 上的多个字段问题

Posted

技术标签:

【中文标题】SQLServer 2008 PIVOT 上的多个字段问题【英文标题】:SQLServer 2008 PIVOT on multiple fields issue 【发布时间】:2014-02-14 17:56:19 【问题描述】:

我正在尝试在 3 个字段/列上使用 PIVOT,即站点、值、日期。我正在使用 SQLServer 2008。请在下面查看我的实际表和所需的输出表以及我使用的数据透视查询

实际表:

**tblReference**


**ID**            **Refer_code**    **Site**    **Value**   **Date**

  1                 9290             CA            12.5         2014-01-01 20:20:41

  5                 9290             TX            12.6         2014-01-05 18:20:30

  2                 6651             CA            13.5         2014-01-01 21:20:21

  3                 7442             TX            14.5         2014-01-05 19:15:14

  4                 8093             CA            15.5         2014-01-01 19:20:20

  6                 8093             TX            16.5         2014-01-05 20:20:20


**Desired output table:**


**Refer_code**   **Site_1** **Site_2**  **Val_1**  **Val_2**    **StartDate**   **EndDate**

   9290            CA         TX      12.5       12.6   2014-01-01      2014-01-05
                                                                20:20:41    18:20:30

   6651            CA         NULL    13.5       NULL   2014-01-01      NULL
                                                                21:20:21    

   7442            NULL       TX      NULL       14.5   NULL            2014-01-05 
                                                                                19:15:14

   8093            CA         TX      15.5       16.5   2014-01-01      2014-01-05
                                                                19:20:20    20:20:20


 **Query:**

        SELECT Refer_code, [Site_1], [Site_2], [Date_1] AS StartDate, [Date_2] AS EndDate, [Val_1], [Val_2]
        FROM
        (
        SELECT Refer_code, Site, 'Site_'+ cast(row_number() over(partition by Refer_code order by Date) as nvarchar(50)) SiteVal,Date,'Date_'+cast(row_number() over(partition by Refer_code order by Date) as nvarchar(50)) DateVal,Value,'Val_'+cast(row_number() over(partition by Refer_code order by Date) as nvarchar(50)) Val
            FROM tblReference
        ) x
        pivot 
        (
            min(Site)
            for SiteVal in ([Site_1], [Site_2])
        ) p 
        pivot
        (
            min(Date)
            for DateVal in ([Date_1], [Date_2])
        ) s
        pivot
        (
            min(Value)
            for Val in ([Val_1], [Val_2])
        ) t

上述查询未按预期返回结果。请帮助我解决问题。

【问题讨论】:

【参考方案1】:

有几种方法可以获得所需的结果。最简单的方法可能是使用带有 CASE 表达式的聚合函数,但您也可以使用 PIVOT 函数。

与 CASE 聚合:

我会采取以下步骤,首先使用row_number()为每个refer_code创建一个唯一的序列:

select refer_code, site, 
  row_number() over(partition by refer_code
                    order by date) seq,
  value,
  date
from tblReference;

见SQL Fiddle with Demo。

一旦您拥有唯一的序列,您就可以通过应用带有 CASE 表达式的聚合函数轻松地将现有数据转换为多列:

;with cte as
(
  select refer_code, site, 
    row_number() over(partition by refer_code
                      order by date) seq,
    value,
    date
  from tblReference
)
select refer_code,
  max(case when seq = 1 then site end) site1,
  max(case when seq = 2 then site end) site2,
  max(case when seq = 1 then value end) value1,
  max(case when seq = 2 then value end) value2,
  max(case when seq = 1 then date end) startdate,
  max(case when seq = 2 then date end) enddate
from cte
group by refer_code;

见SQL Fiddle with Demo

枢轴:

如果您想使用 PIVOT 函数并且需要对多个列进行透视,我建议您先查看对这些列进行反透视,然后再应用透视函数。您将需要创建用于新列名的唯一序列,然后您需要取消旋转 sitedatevalue 列。由于您使用的是 SQL Server 2008+,因此您可以使用 CROSS APPLYVALUEs 将多列还原为多行:

;with cte as
(
  select refer_code, site, 
    row_number() over(partition by refer_code
                      order by date) seq,
    value,
    date
  from tblReference
)
select *
from
(
  select refer_code, 
    col = col + cast(seq as varchar(10)),
    val
  from cte
  cross apply
  (
    values
      ('Site', site),
      ('Value', cast(value as varchar(10))),
      ('Date', convert(varchar(10), date, 120))
  ) c (col, val)
) d;

见SQL Fiddle with Demo。这会将您的数据转换为以下格式:

| REFER_CODE |    COL |        VAL |
|------------|--------|------------|
|       6651 |  Site1 |         CA |
|       6651 | Value1 |      13.50 |
|       6651 |  Date1 | 2014-01-01 |
|       7442 |  Site1 |         TX |
|       7442 | Value1 |      14.50 |
|       7442 |  Date1 | 2014-01-05 |

现在您可以轻松地应用 pivot 函数来获得最终结果,因此整个代码将是:

;with cte as
(
  select refer_code, site, 
    row_number() over(partition by refer_code
                      order by date) seq,
    value,
    date
  from tblReference
)
select refer_code, 
  site1, site2, 
  startdate = date1, enddate = date2,
  value1, value2
from
(
  select refer_code, 
    col = col + cast(seq as varchar(10)),
    val
  from cte
  cross apply
  (
    values
      ('Site', site),
      ('Value', cast(value as varchar(10))),
      ('Date', convert(varchar(10), date, 120))
  ) c (col, val)
) d
pivot
(
  max(val)
  for col in (site1, site2, date1, date2,
              value1, value2)
) piv;

见SQL Fiddle with Demo。两者都会给出以下结果:

| REFER_CODE | SITE1 |  SITE2 |  STARTDATE |    ENDDATE | VALUE1 | VALUE2 |
|------------|-------|--------|------------|------------|--------|--------|
|       6651 |    CA | (null) | 2014-01-01 |     (null) |  13.50 | (null) |
|       7442 |    TX | (null) | 2014-01-05 |     (null) |  14.50 | (null) |
|       8093 |    CA |     TX | 2014-01-01 | 2014-01-05 |  15.50 |  16.50 |
|       9290 |    CA |     TX | 2014-01-01 | 2014-01-05 |  12.50 |  12.60 |

编辑:

根据您的评论,您需要将列设为 CAsite1 等。那么获得结果的最简单方法是使用带有 CASe 的聚合函数:

select refer_code,
  max(case when site = 'CA' then site end) site1,
  max(case when site = 'TX' then site end) site2,
  max(case when site = 'CA' then value end) value1,
  max(case when site = 'TX' then value end) value2,
  max(case when site = 'CA' then date end) startdate,
  max(case when site = 'TX' then date end) endate
from tblReference
group by refer_code;

见SQL Fiddle with Demo。这给出了一个结果:

| REFER_CODE |  SITE1 |  SITE2 | VALUE1 | VALUE2 |                      STARTDATE |                         ENDATE |
|------------|--------|--------|--------|--------|--------------------------------|--------------------------------|
|       6651 |     CA | (null) |   13.5 | (null) | January, 01 2014 21:20:21+0000 |                         (null) |
|       7442 | (null) |     TX | (null) |   14.5 |                         (null) | January, 05 2014 19:15:14+0000 |
|       8093 |     CA |     TX |   15.5 |   16.5 | January, 01 2014 19:20:20+0000 | January, 05 2014 20:20:20+0000 |
|       9290 |     CA |     TX |   12.5 |   12.6 | January, 01 2014 20:20:41+0000 | January, 05 2014 18:20:30+0000 |

【讨论】:

感谢您的回复。但是我有了使用它的想法。我使用了聚合和大小写。我有一个查询:a) 预期的输出与上面显示的不一样。如果您观察到 Refer_Code 7442。它没有出现在 CA 但出现在 TX。所以我应该显示 Site1 = NULL 和 Site2 = TX。在 PIVOT 案例中类似 @user3311016 所以你是说CA总是site1而TX是site2?你有其他网站吗?你会一直只有site1/site2、value1/value2吗? 嗨,没有固定的站点。但在任何给定的记录集 Site1、Site2、Val1、Val2 上总是有 2 个不同的站点。其中一些refer_code 显示在 Site1,一些显示在 Site2,两者兼而有之。似乎是识别它所需的其他一些条件参考,或者是否有任何方法可以使用较低的 Date val @user3311016 你需要一些方法来确定site1/site2的价值,你有类似的东西吗? 不。我会调查一下。感谢您的解决方案。

以上是关于SQLServer 2008 PIVOT 上的多个字段问题的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL Server 2008 中使用 PIVOT

SQL Server 2008 R2 使用 PIVOT 和 varchar 列不起作用

SQL Server 2008 中的 PIVOT SQL 数据

SQL Server 2008 中的 PIVOT / UNPIVOT

sqlServer PIVOT函数求解

SQL Server - CASE 语句上的 PIVOT