11

我很难弄清楚如何在 SQL Server 2008 中使用多列进行动态透视。

我的示例表如下:

ID  YEAR  TYPE  TOTAL   VOLUME
DD1 2008    A   1000    10
DD1 2008    B   2000    20
DD1 2008    C   3000    30
DD1 2009    A   4000    40
DD1 2009    B   5000    50
DD1 2009    C   6000    60
DD2 2008    A   7000    70
DD2 2008    B   8000    80
DD2 2008    C   9000    90
DD2 2009    A   10000   100
DD2 2009    B   11000   110
DD2 2009    C   12000   120

我正在尝试如下所示的支点:

ID  2008_A_TOTAL    2008_A_VOLUME   2008_B_TOTAL    2008_B_VOLUME   2008_C_TOTAL    2008_C_VOLUME   2009_A_TOTAL    2009_A_VOLUME   2009_B_TOTAL    2009_B_VOLUME   2009_C_TOTAL    2009_C_VOLUME
DD1 1000            10              2000            20              3000            30              4000            40              5000            50              6000            60
DD2 7000            70              8000            80              9000            90              10000           100             11000           110             12000           120

我的 SQL Server 2008 查询创建表如下:

CREATE TABLE ATM_TRANSACTIONS 
(
 ID varchar(5),
 T_YEAR varchar(4),
 T_TYPE varchar(3), 
 TOTAL int,
 VOLUME int
);

INSERT INTO ATM_TRANSACTIONS
(ID,T_YEAR,T_TYPE,TOTAL,VOLUME)

VALUES
('DD1','2008','A',1000,10),
('DD1','2008','B',2000,20),
('DD1','2008','C',3000,30),
('DD1','2009','A',4000,40),
('DD1','2009','B',5000,50),
('DD1','2009','C',6000,60),
('DD2','2008','A',7000,70),
('DD2','2008','B',8000,80),
('DD2','2008','C',9000,90),
('DD2','2009','A',10000,100),
('DD2','2009','B',11000,110),
('DD2','2009','C',1200,120);

T_Year列将来可能会更改,但该T_TYPE列通常是已知的,所以我不确定是否可以将 SQL Server 中的 PIVOT 函数与动态代码结合使用?

我尝试按照此处的示例进行操作:

http://social.technet.microsoft.com/wiki/contents/articles/17510.t-sql-dynamic-pivot-on-multiple-columns.aspx

但我最终得到了奇怪的结果。

4

3 回答 3

23

为了得到结果,在应用 PIVOT 函数以获得最终结果之前,您需要先查看对Total和列中的数据进行反透视。Volume我的建议是首先编写查询的硬编码版本,然后将其转换为动态 SQL。

UNPIVOT 进程将这些多列转换为行。UNPIVOT 有几种方法,您可以使用 UNPIVOT 功能,也可以使用 CROSS APPLY。取消透视数据的代码将类似于:

select id, 
    col = cast(t_year as varchar(4))+'_'+t_type+'_'+col, 
    value
from ATM_TRANSACTIONS t
cross apply
(
    select 'total', total union all
    select 'volume', volume
) c (col, value);

这为您提供以下格式的数据:

+-----+---------------+-------+
| id  |      col      | value |
+-----+---------------+-------+
| DD1 | 2008_A_total  |  1000 |
| DD1 | 2008_A_volume |    10 |
| DD1 | 2008_B_total  |  2000 |
| DD1 | 2008_B_volume |    20 |
| DD1 | 2008_C_total  |  3000 |
| DD1 | 2008_C_volume |    30 |
+-----+---------------+-------+

然后你可以应用 PIVOT 函数:

select ID, 
    [2008_A_total], [2008_A_volume], [2008_B_total], [2008_B_volume],
    [2008_C_total], [2008_C_volume], [2009_A_total], [2009_A_volume]
from
(
    select id, 
        col = cast(t_year as varchar(4))+'_'+t_type+'_'+col, 
        value
    from ATM_TRANSACTIONS t
    cross apply
    (
        select 'total', total union all
        select 'volume', volume
    ) c (col, value)
) d
pivot
(
    max(value)
    for col in ([2008_A_total], [2008_A_volume], [2008_B_total], [2008_B_volume],
                [2008_C_total], [2008_C_volume], [2009_A_total], [2009_A_volume])
) piv;

现在您有了正确的逻辑,您可以将其转换为动态 SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(cast(t_year as varchar(4))+'_'+t_type+'_'+col) 
                    from ATM_TRANSACTIONS t
                    cross apply
                    (
                        select 'total', 1 union all
                        select 'volume', 2
                    ) c (col, so)
                    group by col, so, T_TYPE, T_YEAR
                    order by T_YEAR, T_TYPE, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT id,' + @cols + ' 
            from 
            (
                select id, 
                    col = cast(t_year as varchar(4))+''_''+t_type+''_''+col, 
                    value
                from ATM_TRANSACTIONS t
                cross apply
                (
                    select ''total'', total union all
                    select ''volume'', volume
                ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute sp_executesql @query;

这会给你一个结果:

+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+
| id  | 2008_A_total | 2008_A_volume | 2008_B_total | 2008_B_volume | 2008_C_total | 2008_C_volume | 2009_A_total | 2009_A_volume | 2009_B_total | 2009_B_volume | 2009_C_total | 2009_C_volume |
+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+
| DD1 |         1000 |            10 |         2000 |            20 |         3000 |            30 |         4000 |            40 |         5000 |            50 |         6000 |            60 |
| DD2 |         7000 |            70 |         8000 |            80 |         9000 |            90 |        10000 |           100 |        11000 |           110 |         1200 |           120 |
+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+
于 2013-09-06T12:11:33.883 回答
2
declare @stmt nvarchar(max)

select @stmt = isnull(@stmt + ', ', '') + 
        'sum(case when T_YEAR = ''' + T.T_YEAR + ''' and T_TYPE = ''' + T.T_TYPE + ''' then TOTAL else 0 end) as ' + quotename(T.T_YEAR + '_' +  T.T_TYPE + '_TOTAL') + ',' +
        'sum(case when T_YEAR = ''' + T.T_YEAR + ''' and T_TYPE = ''' + T.T_TYPE + ''' then VOLUME else 0 end) as ' + quotename(T.T_YEAR + '_' +  T.T_TYPE + '_VOLUME')
from (select distinct T_YEAR, T_TYPE from ATM_TRANSACTIONS) as T
order by T_YEAR, T_TYPE

select @stmt = '
    select
        ID, ' + @stmt + ' from ATM_TRANSACTIONS group by ID'

exec sp_executesql
    @stmt = @stmt

不幸的是,sqlfiddle.com 目前无法正常工作,因此我无法为您创建示例。

动态 SQL 创建的查询将是:

select
    ID,
    sum(case when T_YEAR = '2008' and T_TYPE = 'A' then TOTAL else 0 end) as 2008_A_TOTAL,
    sum(case when T_YEAR = '2008' and T_TYPE = 'A' then VOLUME else 0 end) as 2008_A_VOLUME,
    ...
from ATM_TRANSACTIONS
group by ID
于 2013-09-06T12:09:53.310 回答
0

请试试:

DECLARE @pivv NVARCHAR(MAX),@Query NVARCHAR(MAX)

SELECT @pivv=COALESCE(@pivv+',','')+ QUOTENAME(T_YEAR+'_'+T_TYPE+'_TOTAL')+','+QUOTENAME(T_YEAR+'_'+T_TYPE+'_VOLUME') from ATM_TRANSACTIONS GROUP BY T_YEAR, T_TYPE

IF ISNULL(@pivv, '')<>''
    SET @Query='SELECT * FROM(
        SELECT ID, T_YEAR+''_''+T_TYPE+''_TOTAL'' TYP, TOTAL VAL from ATM_TRANSACTIONS UNION
        SELECT ID, T_YEAR+''_''+T_TYPE+''_VOLUME'' TYP, VOLUME VAL from ATM_TRANSACTIONS 
        )x pivot (SUM(VAL) for TYP in ('+@pivv+')) as xx'

IF ISNULL(@Query, '')<>''
    EXEC (@Query)
于 2013-09-06T12:14:50.103 回答