2013-09-06 18 views
9

Mam bardzo trudny czas próbując dowiedzieć się, jak wykonać dynamiczny węzeł w SQL Server 2008 z wieloma kolumnami.Serwer SQL: dynamiczny wykres przestawny na 5 kolumnach

Moja Przykładowa tabela wygląda następująco:

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 

i próbuję przegubu go w następujący sposób:

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 

My SQL Server 2008 kwerenda jest następujący, aby utworzyć tabelę:

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); 

Kolumna T_Year może się zmienić w przyszłości, ale kolumna jest ogólnie wiesz, więc nie jestem pewien, czy mogę użyć kombinacji funkcji PIVOT w SQL Server z dynamicznym kodem?

Próbowałem po przykład tutaj:

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

ale skończyło się z dziwnych rezultatów.

Odpowiedz

13

Aby uzyskać wynik, trzeba spojrzeć na unpivoting dane w Total i Volume kolumn pierwszy przed zastosowaniem funkcji PIVOT dostać Wynik końcowy. Moja sugestia to najpierw napisać zakodowaną wersję zapytania, a potem przekonwertować ją na dynamiczny SQL.

Proces UNPIVOT przekształca te wiele kolumn w wiersze. Istnieje kilka sposobów na UNPIVOT, możesz użyć funkcji UNPIVOT lub użyć CROSS APPLY. Kod dla UNPIVOT dane będą podobne do:

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); 

Daje to dane w formacie:

+-----+---------------+-------+ 
| 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 | 
+-----+---------------+-------+ 

Następnie można zastosować funkcję 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; 

Teraz masz odpowiednią logikę, możesz przekonwertować to na dynamiczny 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; 

To daje wynik:

+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+ 
| 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 | 
+-----+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+--------------+---------------+ 
+0

fantastyczne. Nie zdawałem sobie sprawy, że muszę rozpiąć/zastosować krzyż, a następnie przestawić. Dzięki wielkie. – Mkov

+0

suppppppperrrrrrrrrrb –

+0

To jest świetne. Trwające prace nad dynamicznymi kolumnami wymagają nieco więcej pracy, ale ogólny pomysł wciąż jest ważny. –

1
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 

Niestety, sqlfiddle.com nie działa w tej chwili, więc nie mogę utworzyć dla ciebie przykładu.

Zapytanie stworzony przez dynamicznego SQL będzie:

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 
0

Spróbuj:

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 ('[email protected]+')) as xx' 

IF ISNULL(@Query, '')<>'' 
    EXEC (@Query)