2013-08-02 18 views
14

Mam następujące tabele źródłowe i docelowe w serwerze sql 2008R2. Jak mogę wykonać przestawianie w TSQL, aby dostać się do miejsca docelowego ze źródła.SQL Server Pivot wiele kolumn na podstawie jednej kolumny

SourceTbl

empId empIndex empState empStDate empEndDate 
======================================================== 
10  1   AL   1/1/2012  12/1/2012 
10  2   FL   2/1/2012  2/1/2013 
15  1   FL   3/20/2012 1/1/2099 

DestTbl

empId empState1 empState1StDate empState1EndDt empState2 empState2StDate empState2EndDt 
========================================================================================================= 
10  AL   1/1/2012   12/1/2012   FL   2/1/2012   2/1/2013 
15  FL   3/20/2012   1/1/2099   NULL  NULL    NULL 

nadzieję, że empIndex jakoś pomóc w zwrotnicy.

+0

pivot jest również znany jako transformacja w msaccess. Twoje pytanie jest unikalne, ponieważ zawiera również tekst (a nie liczby całkowite) w wynikowych komórkach. funkcja agregująca nadal musi być stosowana, w tym przypadku MIN() powinno nadążyć za wartością tekstu, nawet jeśli jest tylko 1 wartość tekstowa. – hamish

Odpowiedz

23

Ponieważ używasz programu SQL Server, istnieje kilka różnych sposobów przekształcania wierszy w kolumny. Możesz użyć funkcji agregującej z wyrażeniem CASE:

select empid, 
    max(case when empindex = 1 then empstate end) empState1, 
    max(case when empindex = 1 then empStDate end) empStDate1, 
    max(case when empindex = 1 then empEndDate end) empEndDate1, 
    max(case when empindex = 2 then empstate end) empState2, 
    max(case when empindex = 2 then empStDate end) empStDate2, 
    max(case when empindex = 2 then empEndDate end) empEndDate2 
from sourcetbl 
group by empid; 

Zobacz SQL Fiddle with Demo.

Jeśli chcesz korzystać z funkcji PIVOT, aby uzyskać wynik, to polecam najpierw unpivoting kolumny empState, empStDateempEndDate i tak będziesz mieć wiele wierszy w pierwszej kolejności. Można użyć funkcji UNPIVOT lub CROSS APPLY do konwersji danych kod będzie:

select empid, col+cast(empindex as varchar(10)) col, value 
from sourcetbl 
cross apply 
(
    select 'empstate', empstate union all 
    select 'empstdate', convert(varchar(10), empstdate, 120) union all 
    select 'empenddate', convert(varchar(10), empenddate, 120) 
) c (col, value); 

Zobacz Demo. Gdy dane nieprzestawne, można zastosować funkcję PIVOT więc ostateczny kod będzie:

select empid, 
    empState1, empStDate1, empEndDate1, 
    empState2, empStDate2, empEndDate2 
from 
(
    select empid, col+cast(empindex as varchar(10)) col, value 
    from sourcetbl 
    cross apply 
    (
    select 'empstate', empstate union all 
    select 'empstdate', convert(varchar(10), empstdate, 120) union all 
    select 'empenddate', convert(varchar(10), empenddate, 120) 
) c (col, value) 
) d 
pivot 
(
    max(value) 
    for col in (empState1, empStDate1, empEndDate1, 
       empState2, empStDate2, empEndDate2) 
) piv; 

Zobacz SQL Fiddle with Demo.

Th powyżej wersji zadziała świetne, jeśli masz ograniczoną liczbę empindex, ale jeśli nie można użyć dynamicznego SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(col+cast(empindex as varchar(10))) 
        from SourceTbl 
        cross apply 
        (
         select 'empstate', 1 union all 
         select 'empstdate', 2 union all 
         select 'empenddate', 3 
        ) c (col, so) 
        group by col, so, empindex 
        order by empindex, so 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 

set @query = 'SELECT empid,' + @cols + ' 
      from 
      (
       select empid, col+cast(empindex as varchar(10)) col, value 
       from sourcetbl 
       cross apply 
       (
        select ''empstate'', empstate union all 
        select ''empstdate'', convert(varchar(10), empstdate, 120) union all 
        select ''empenddate'', convert(varchar(10), empenddate, 120) 
       ) c (col, value) 
      ) x 
      pivot 
      (
       max(value) 
       for col in (' + @cols + ') 
      ) p ' 

execute sp_executesql @query; 

See SQL Fiddle with Demo

Można użyć tych zapytań INSERT W swoim numerze DestTbl lub zamiast zapisywania danych w tym formacie masz teraz zapytanie, aby uzyskać pożądany wynik.

te pytania umieścić dane w formacie:

| EMPID | EMPSTATE1 | EMPSTDATE1 | EMPENDDATE1 | EMPSTATE2 | EMPSTDATE2 | EMPENDDATE2 | 
--------------------------------------------------------------------------------------- 
| 10 |  AL | 2012-01-01 | 2012-12-01 |  FL | 2012-02-01 | 2013-02-01 | 
| 15 |  FL | 2012-03-20 | 2099-01-01 | (null) |  (null) |  (null) | 
+0

Doceniam twój wysiłek. To także rozwiązuje mój problem. Bardzo dziękuję @bluefeet –

+0

Widziałem FOR XML używane dużo przed .. ale kudos i * thumbsup * dla najlepszego wykorzystania: QUOTEN STUFF i NVARCHAR (MAX) w PIVOT :) MSACCESS nazywa to TRANSFORM (nie wiesz, dlaczego to polecenie nigdy nie dotarł do serwera SQL) – hamish

-1

Wow, to było bardziej skomplikowane, niż sobie wyobrażałem, ale nie zmusić go do pracy świetnie! dzięki. To była moja ostateczna wersja. TextKey zawiera dane, które chcesz przekształcić w kolumny, a wartość TextValue jest wartością, która kończy się wewnątrz każdej komórki.

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


select @cols = STUFF((SELECT distinct ', ' + QUOTENAME(TextKey) 
        from #SourceTbl 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 

set @query = 'SELECT FromEntityID, DisplayName, ' + @cols + ' 
       FROM 
       (
        select FromEntityID, DisplayName, TextKey, TextValue 
        from #SourceTbl 
      ) x 
       pivot 
       (
        min(TextValue) 
        for TextKey in (' + @cols + ') 
      ) p 
       ORDER BY FromEntityID 
       ' 

execute(@query)