0

需要有关 SQL Server 2008 中的 pivot 子句的帮助。我有一个包含以下信息的表:

我有一个包含 9 列的表格:IDPeriod_1Period_4日期(即 2013-04、2013-07 等)和Amount_1Amount_4(即 30、40 等)。我希望所有不同的日期从Period_1toPeriod_4作为列名,然后将Amount_1对应于Period_1Amount_2对应于Period_2Amount_3对应于Period_3Amount_4对应于Period_4作为行值进行透视。

这是我目前想出的 T-SQL:

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

SELECT @cols = STUFF((
        SELECT DISTINCT ',' + QUOTENAME(ans)
        FROM (
            SELECT Period_1 AS ans
            FROM Booking

            UNION

            SELECT Period_2 AS ans
            FROM Booking

            UNION

            SELECT Period_3 AS ans
            FROM Booking

            UNION

            SELECT Period_4 AS ans
            FROM Booking
            ) a
        FOR XML PATH('')
            ,TYPE
        ).value('.', 'NVARCHAR(MAX)'), 1, 1, '')

SET @query = 'SELECT Id ' + @cols + ' from 
         (
            select Id, Period_1, Amount_1, Period_2, Amount_2
            from Booking
         ) x
        pivot 
        (
            max(Amount_1)
            for Period_1 in (' + @cols + ')

        ) p 
(
            max(Amount_2)
            for Period_2 in (' + @cols + ')

        ) p

        '

EXECUTE (@query)

我得到错误:

消息 265,级别 16,状态 1,第 18
行 PIVOT 运算符中指定的列名“2013-10”与 PIVOT 参数中的现有列名冲突。

有没有办法对包含相同值的多个列进行数据透视查询?请写一个关于如何做到这一点的例子。

我感谢任何形式的帮助。提前致谢。

4

2 回答 2

3

您的部分问题是您的数据未标准化,具有 , , 等的列Period_1使得Amount_1查询Period_2数据Amount_2变得异常困难。我的第一个建议是考虑将您的表结构修复为类似于以下内容:

create table booking
(
  id int,
  period datetime,
  amount decimal(10, 5)
);

这将允许您为每个 ID 设置多个期间和金额。还有其他设计方法,但这应该让您了解如何修复当前结构。

如果您无法修复您的结构,那么我建议您将一个 UNPIVOT 然后一个 PIVOT 应用于您现有的表。UNPIVOT 会将多列数据转换为多行,然后您可以将 PIVOT 应用于金额以获得最终结果。

UNPIVOT 的基本语法如下。我将 CROSS APPLY 与 UNION ALL 一起使用,因为我们需要同时取消周期和金额:

select id, 
  convert(varchar(7), period, 120) period,
  amount
from
(
  select id, 
    period_1, period_2, period_3, period_4,
    amount_1, amount_2, amount_3, amount_4
  from booking
) d
cross apply
(
  select period_1, amount_1 union all
  select period_2, amount_2 union all
  select period_3, amount_3 union all
  select period_4, amount_4
) c (period, amount);

请参阅带有演示的 SQL Fiddle。这为您提供以下格式的数据:

| ID |  PERIOD | AMOUNT |
-------------------------
|  1 | 2013-01 |     30 |
|  1 | 2013-04 |     40 |
|  1 | 2013-07 |     50 |
|  1 | 2013-10 |     60 |

一旦数据采用这种格式,您就可以将 PIVOT 函数应用于Period列中的值:

select id,
  [2013-01], [2013-04], [2013-05],
  [2013-07], [2013-08], [2013-10],
  [2013-11]
from
(
  select id, 
    convert(varchar(7), period, 120) period,
    amount
  from
  (
    select id, 
      period_1, period_2, period_3, period_4,
      amount_1, amount_2, amount_3, amount_4
    from booking
  ) d
  cross apply
  (
    select period_1, amount_1 union all
    select period_2, amount_2 union all
    select period_3, amount_3 union all
    select period_4, amount_4
  ) c (period, amount)
) src
pivot
(
  sum(amount)
  for period in ([2013-01], [2013-04], [2013-05],
                 [2013-07], [2013-08], [2013-10],
                 [2013-11])
) piv;

请参阅SQL Fiddle with Demo。当然,如果您提前知道这些值,上述方法会很好用。但如果你不这样做,那么你会想看看使用动态 SQL 来获得结果:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(period) 
                    from 
                    (
                      select convert(varchar(7), period_1, 120) period, period_1 dt 
                      from booking union all
                      select convert(varchar(7), period_2, 120) period, period_2 dt
                      from booking union all
                      select convert(varchar(7), period_3, 120) period, period_3 dt
                      from booking union all
                      select convert(varchar(7), period_4, 120) period, period_4
                      from booking
                    ) d
                    group by period, dt
                    order by dt
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT id, ' + @cols + ' 
            from
            (
              select id, 
                convert(varchar(7), period, 120) period,
                amount
              from
              (
                select id, 
                  period_1, period_2, period_3, period_4,
                  amount_1, amount_2, amount_3, amount_4
                from booking
              ) d
              cross apply
              (
                select period_1, amount_1 union all
                select period_2, amount_2 union all
                select period_3, amount_3 union all
                select period_4, amount_4
              ) c (period, amount)
            ) src
            pivot 
            (
                sum(amount)
                for period in (' + @cols + ')
            ) p '

execute(@query);

请参阅SQL Fiddle with Demo。这些查询将给出类似的结果:

| ID | 2013-01 | 2013-04 | 2013-05 | 2013-07 | 2013-08 | 2013-10 | 2013-11 |
----------------------------------------------------------------------------
|  1 |     105 |      40 |      86 |      50 |     120 |      60 |      65 |
于 2013-07-08T20:51:06.177 回答
0

我认为您必须在字段名称中附加一些内容并@cols为您使用的每个值创建一个变量,即:

SELECT @cols1 = STUFF((
        SELECT DISTINCT ',' + QUOTENAME(ans)
        FROM (SELECT '1_'+CAST(Period_1  AS VARCHAR(12)) AS ans
              FROM #test
              UNION
              SELECT '1_'+CAST(Period_2  AS VARCHAR(12)) AS ans
              FROM #test
            ) a
        FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1,'')
        ,@cols2 = STUFF((
                SELECT DISTINCT ',' + QUOTENAME(ans)
                FROM (SELECT '2_'+CAST(Period_1 AS VARCHAR(12)) AS ans
                      FROM #test
                      UNION
                      SELECT '2_'+CAST(Period_2 AS VARCHAR(12)) AS ans
                      FROM #test
                    ) a
                FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1,'')

然后在旋转时引用适当的变量,您还必须在那里添加字段名称:

(
    max(Amount_1)
    for '1_'+CAST(Period_1 AS VARCHAR(12)) in (' + @cols1 + ')

) p 

可能有一种更聪明的方法可以做到这一点,但我不知道。

于 2013-07-08T19:42:35.177 回答