Uwaga! ten artykuł mówi o bardziej zaawansowanych, dynamicznych tabelach przestawnych. Jeżeli interesują Cię raczej podstawowe informacje o tym jak zrobić tabelę przestawną w SQL, to zajrzyj raczej do tego artykułu, a potem możesz zajrzeć znowu tutaj.
Język SQL posiada możliwość budowania tabel przestawnych. Zazwyczaj użytkownicy korzystający z tabel przestawnych preferują korzystanie ze specjalistycznych narzędzi analitycznych, w tym z Excela, gdzie budowanie tabel przestawnych jest bardzo proste. Jeżeli jednak jest taka potrzeba, to tabele przestawne (PIVOT TABLE) można także budować bezpośrednio w języku SQL.
Niestety budowa polecenia do utworzenia tabeli przestawnej w SQL jest nieco skomplikowana i niestety sztywna. No chyba, że zstosuje się drobną sztuczkę…
Załóżmy, że z tabeli, w której zapisane są kolor (color), rozmiar (size) oraz cena (ListPrice) chcemy zrobić tabelę przestawną, gdzie w wierszach zobaczymy rozmiary, a w kolumnach kolory. Na przecięciu wierszy i kolumn chcemy zobaczyć średnią cenę.
SELECT * FROM ( SELECT Size, Color, ListPrice FROM Production.Product WHERE Color IS NOT NULL AND Size IS NOT NULL ) AS myProducts PIVOT (AVG(ListPrice) FOR Color IN ([Black],[Silver])) pvt
Wprawdzie zapytanie wyświetliło tabelę przestawną, ale jej kolumny (czyli kolory) musiałeś samodzielnie wymienić. A w Excelu jest tak pięknie… przeciągasz myszką i kolumny wypełniają się same. No cóż – tutaj też się da, chociaż będzie troszkę trudniej.
Problematycznym miejscem jest to, w którym tworzysz listę kolumn do wyświetlenia:
FOR Color IN ([Black],[Silver])
Żeby tabela przestawna zaprezentowała kolumny dla wszystkich danych musisz wymienić tu wszystki dostępne kolory dla tabeli przestawnej. Ale, ale, czy przypadkiem listy takich kolumn nie można wygenerować automatycznie? Przecież polecenie
SELECT DISTINCT Color FROM Production.Product WHERE Color IS NOT NULL
Pokazuje listę dostępnych kolorów!
Gdyby tak tylko:
- tę listę jakoś zapisać do zmiennej,
- a potem tą zmienną wstawić do polecenia tworzącego tabelę przestawną,
- a potem uruchomić to polecenie…
O rety – no przecież się da. Po kolei. Najpierw definiujemy zmienną i zapisujemy w niej listę kolorów i to na dodatek już z wymaganymi nawiasami kwadratowymi:
DECLARE @PivotColumnHeaders VARCHAR(MAX); SET @PivotColumnHeaders='';
SELECT @PivotColumnHeaders = @PivotColumnHeaders + '[' + Color + '],' FROM (SELECT DISTINCT Color FROM Production.Product WHERE Color IS NOT NULL) tabColors SET @PivotColumnHeaders = LEFT(@PivotColumnHeaders,LEN(@PivotColumnHeaders)-1)
Podzapytanie, które tutaj widzisz wybiera unikalne wartości kolorów. Zapytanie zewnętrzne buduje napis z tych nazw kolorów, umieszczając je w nawiasach kwadratowych. Po zakończeniu zapytania zmienna z zapisanymi kolorami kończy się przecinkiem, a nasza lista nie. Dlatego jedna z ostatnich instrukcji wycina ostatni znak ze zmiennej pamiętającej nazwy kolorów.
W ten sposób pierwszy punkt z listy życzeń mamy gotowy.
Jak użyć zmiennej z kolorami w wyrażeniu budowania tabeli przestawnej? Przypomnijmy sobie o dynamicznym SQL. Ma on swoje wady, ale w takim przypadku się przyda. Zrobimy jeden duży napis, który będzie częściowo zbudowany z komendy tworzenia tabeli przestawnej, a częściowo z utworzonej przez nas listy kolumn:
DECLARE @cmd VARCHAR(MAX); SET @cmd=' SELECT * FROM ( SELECT Size, Color, ListPrice FROM Production.Product ) AS myProducts PIVOT (AVG(ListPrice) FOR Color IN ('+@PivotColumnHeaders+')) pvt '
W ten sposób drugi punkt z listy życzeń mamy gotowy.
Ostatnia instrukcja to wykonanie polecenie i zachwyt nad utworzoną tabelą przestawną:
EXECUTE (@cmd)
Przykład do ściągnięcia z mobilo (mobilo24):
http://www.mobilo24.eu/wp-content/uploads/2011/04/dynamiczna_tabela_przestawna.zip
Źródła:
- http://www.kodyaz.com/articles/t-sql-pivot-tables-in-sql-server-tutorial-with-examples.aspx
- http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx
Komentarze:
[…] to, ?e musisz sam wymienia? kolumny tej tabeli. Excel robi? to nie jako zamiast u?ytkownika!. Ten artyku? opowiada, jak zbudowa? tabel? przestawn? dynamiczn?, czyli tak?, kt
Znakomity artykuł.
U mnie nie zadział ostatni krok, czyli: EXECUTE (@cmd).
Musiałem użyć składni: exec sp_executesql @cmd i wszystko śmiga.
Tak to prawda. Należy dopisać EXEC. Każde uruchomienie procedury składowanej wymaga poprzedzenia instrukcja EXEC (za wyjątkiem pierwszej w batchu o ile występuje na samym początku tego batcha). Jak widać kiedy pisałem artykuł uruchamiałem instrukcje jedna po drugiej ręcznie i dlatego (standardowa odpowiedź programisty) u mnie działało. Dziękuję za uwagę i już poprawiam 🙂
A jak próbuje coś identycznie ale w kolumnach mam daty w formacie 'yyyy-mm-dd’ to już nie wychodzi, krzyczy że
The data types varchar and date are incompatible in the add operator.
Co mozna zrobić w takim przyapdku? z góry dzięki. pozdr R
jakoś sobie poradziłem wprowadzając nowy parametr – data zamieniona na varchar. Teraz mam inny problem kolumny z datami sa ustawione nie kolejno …
order by ani w selectie z distinct ani w exec nie działa:
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
da się to jakoś posortować? pozdr R
Polecam
http://davidduffett.net/post/5334646215/get-a-comma-separated-list-of-values-in-sql-with-for
Przedstawiono tu sposób na zbudowanie uporządkowanej listy wartości rozdzielanej przecinkami:
SELECT STUFF
(
SELECT ’,’ + ModelThisFits
FROM ModelProductFits
ORDER BY ModelThisFits
FOR XML PATH(’Model’)
), 1, 1, ”
Dla ścisłości pragnę tylko zauważyć, iż składnia:
SELECT STUFF
(
SELECT ’,’ + ModelThisFits
FROM ModelProductFits
ORDER BY ModelThisFits
FOR XML PATH(’Model’)
), 1, 1, ”
jest nieprawidłowa gdyż zawartość, którą chcemy stuffować należy obłożyć jeszcze nawiasami i powinno to wyglądać tak:
SELECT STUFF
(
(
SELECT ’,’ + ModelThisFits
FROM ModelProductFits
ORDER BY ModelThisFits
FOR XML PATH(’Model’)
), 1, 1, ”
)
Ja bym jednak nie szedł w tę stronę, gdyż zawartość nagłówka w pivocie i tak musisz mieć w formacie tekstowym w nawiasach kwadratowych. Sugerowałbym pozostanie przy rozwiązaniu pierwotnie zaproponowanym przez autora tego postu. Do uzyskania sortowania w pivocie można posłużyć się danymi z innej tabelki gdzie już będą posortowane np.
create table #TwojaTabela (
id int identity (1,1),
TwojaData nvarchar (10)
)
truncate table #TwojaTabela
insert into #TwojaTabela
select cast(cast(ta_data as date) as nvarchar)
from Jakies_dane
group by ta_data
order by ta_data
DECLARE @PivotColumnHeaders1 nvarchar (max), @cmd1 nvarchar (max)
SET @PivotColumnHeaders1=”
SET @cmd1 = ”
SELECT
@PivotColumnHeaders1 = @PivotColumnHeaders1 + '[’ + TwojaData + ’],’
FROM #TwojaTabela
SET @PivotColumnHeaders1 = LEFT(@PivotColumnHeaders1,LEN(@PivotColumnHeaders1)-1)
Zamiast select distinct do uzyskania unikalności daty stosujesz group by i wtedy zadziała order by