Co to te CTE?
I jak zrobić PIVOT?
(oraz słów kilka o GROUPING SETS)
90. Spotkanie PLSSUG Warszawa, 08.10.2015
O czym będzie?
• Co to CTE, a przede wszystkim jak działa rekurencja w CTE?
• Jak obracać dane, czyli PIVOT i UNPIVOT na kilka sposobów
• Grupowanie wielopoziomowe, czyli GROUPING SETS
O mnie
Bartosz Ratajczyk
programista baz danych
(i aplikacji)
czasem też administrator
trener
T-SQL, ETL, C#, PHP, JS, SAS 4GL
http://bartekr.net | b.ratajczyk@gmail.com
MCTS SQL Server 2008, MCSA SQL Server 2012, MCT
Table expressions
Nazwane zapytania, które możemy traktować tak samo jak tabele
Nieutrwalone Utrwalone
Derived table View
Common Table Expression Table Valued Function
Jak wygląda CTE?
-- pokaż sprzedaż pracowników w danym miesiącu
;WITH Dane AS (
SELECT
Pracownik = p.nazwa,
Produkt = pr.nazwa,
RokMiesiac = YEAR(s.data_sprzedazy) * 100 + MONTH(s.data_sprzedazy),
s.ilosc,
s.wartosc
FROM Sprzedaz s
INNER JOIN Pracownik p
ON p.id = s.pracownik_id
INNER JOIN Produkt pr
ON pr.id = s.produkt_id
)
SELECT
*
FROM Dane
CTE z nazwami kolumn
-- pokaż sprzedaż pracowników w danym miesiącu
;WITH Dane (Pracownik, Produkt, RokMiesiąc, Ilość, Wartość) AS (
SELECT
Pracownik = p.nazwa,
Produkt = pr.nazwa,
RokMiesiac = YEAR(s.data_sprzedazy) * 100 + MONTH(s.data_sprzedazy),
s.ilosc,
s.wartosc
FROM Sprzedaz s
INNER JOIN Pracownik p
ON p.id = s.pracownik_id
INNER JOIN Produkt pr
ON pr.id = s.produkt_id
)
SELECT
*
FROM Dane
A ta rekurencja?
;WITH Poziomy AS (
SELECT <kolumny>
FROM Pracownik p
WHERE
przelozony_id IS NULL
UNION ALL
SELECT <kolumny>
FROM Pracownik p
INNER JOIN Poziomy po
ON po.pracownik_id = p.przelozony_id
)
SELECT *
FROM Poziomy
Rekurencja
• Rekurencja w programowaniu: funkcja wywołuje samą siebie
• Rekurencja w SQL: zapytanie wywołuje samo siebie
Różnica między iteracją a rekurencją?
Iteracja: 55 = 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1
Rekurencja:
55 = 10 + (Suma liczb od 1 do 9)
55 = 10 + 9 + (Suma liczb od 1 do 8)
55 = 10 + (9 + (8 + (7 + (6 + (5 + (4 + (3 + (2 + (1)))))))))
Ma to jakieś ograniczenia albo wady?
• CTE vs funkcja (32 poziomy)
• MAXRECURSION X vs MAXRECURSION 0
• Widoczność tylko dla następnego zapytania
• Trudniej testować niż derived tables
• Każde odwołanie do CTE to jego wywołanie od zera (nie dotyczy
rekurencji)
Jak wygląda polecenie PIVOT (i dlaczego tak?)
SELECT
Pracownik,
Produkt,
[201401], [201402], [201403], [201404], [201405], [201406],
[201407], [201408], [201409], [201410], [201411], [201412]
FROM Dane
PIVOT (
SUM(ilosc)
FOR RokMiesiac IN (
[201401], [201402], [201403], [201404], [201405], [201406],
[201407], [201408], [201409], [201410], [201411], [201412]
)
) p
No ale dlaczego tak?
• Jaich informacji potrzebujemy do obrócenia danych z wierszy na
kolumny:
• Po czym grupujemy?
• Z której kolumny bierzemy atrybuty?
• Które wartości nas interesują?
• I jak je zliczać?
A jak to przełożyć na kod – za chwilę
To całkiem niezłe, ale ma ograniczenia
• Tylko jedna funkcja agregująca w konstrukcji PIVOT
• Muszę znać nazwy kolumn i podać je wprost
Zgadza się, ale można to obejść
To teraz w drugą stronę – z kolumn na wiersze
SELECT
Pracownik,
Produkt,
RokMiesiac,
Wartosc
FROM SprzedazPivot
UNPIVOT (
Wartosc
FOR RokMiesiac IN (
[201401], [201402], [201403], [201404], [201405], [201406],
[201407], [201408], [201409], [201410], [201411], [201412]
)
) u
To znowu – dlaczego tak?
• Jakich informacji potrzebujemy do zmiany kolumn na wiersze:
• Które kolumny mają trafić jako wiersze?
• Jak się ma nazywać kolumna, która będzie trzymała nazwy kolumn?
• Jak ma się nazywać kolumna z wartościami z tych kolumn?
Ten UNPIVOT tez ma wady
• Też muszę podać nazwy tych wszystkich kolumn?
Tak, ale wiesz już jak to obejść
• Nie da się obrócić więcej niż jednego zestawu kolumn
Ale na to też jest sposób
Co to GROUPING SET?
• To po prostu zestaw wyrażeń, po których grupujemy
Czyli pisząc tak:
GROUP BY Produkt, Rok, Miesiac, Dzien
Możemy napisać tak:
GROUP BY GROUPING SETS (
(Produkt, Rok, Miesiac, Dzien)
)
(ale wyjdzie dłużej)
No rewelacja, a daje to coś więcej?
• Oczywiście – możemy podać kilka zestawów na raz
GROUP BY GROUPING SETS (
(Produkt, Rok, Miesiac, Dzien),
(Produkt, Rok),
(Produkt, Rok, Miesiac),
()-- <-- TOTAL
)
GROUPING() i GROUPING_ID()
• GROUPING() sprawdza, czy rekord pochodzi z grupowania (1) czy to
rekord źródłowy (0)
SELECT GROUPING(Produkt)
• GROUPING_ID() zwraca maskę bitową kolumn, które nie zostały użyte
w grupowaniu
SELECT GROUPING_ID(Produkt, Rok, Miesiac, Dzien)
CUBE i ROLLUP
• Skracają pisanie wszystkich kombinacji GROUPING SETS
• CUBE tworzy zbiór wszystkich możliwych kombinacji kolumn (2^N)
• ROLLUP traktuje kolejne kolumny jako hierarchię (N + 1(TOTAL))
• Obowiązująca składnia:
• GROUP BY ROLLUP (<kolumny>)
• GROUP BY CUBE (<kolumny>)
• Wycofywana składnia:
• GROUP BY <kolumny> WITH ROLLUP
• GROUP BY <kolumny> WITH CUBE