Data utworzenia: 21.05.2024 Numer referencyjny: SXJ128 Publiczny

Jak stworzyć raport tabeli przestawnej w SQL z użyciem CTE | Artykuł ERP XL

Prowadzę szkolenia z administrowania i programowania Comarch ERP XL. Dla jednego z programów szkoleniowych opracowałem ciekawe ćwiczenie z tworzenia w SQL raportu prezentującego dane w postaci tabeli przestawnej. Pragnę się nim z Wami podzielić.

Wiem, że tego typu raport da się łatwo uzyskać w Comarch Business Intelligence. Nie każdy ma jednak Comarch BI.
Wiem też, że istnieje w SQL składnia PIVOT, która powinna sobie z tym poradzić. Ona nie działa w mojej opinii. 

To opracowanie adresuję do osób, które samodzielnie tworzą raporty w SQL i chcieliby rozszerzyć swoją wiedzę.

Co to jest CTE

Common Table Expression (CTE) jest to specyficzny sposób tworzenia zapytań wprowadzony przez Microsoft po raz pierwszy w wersji MS SQL 2005. 

Umożliwia tworzenie w ramach jednego zapytania SQL dowolnej ilości zapytań, które są przechowywane w pamięci oraz możliwości odwołania się do tych zapytań w obrębie tego samego zapytania.  Taka konstrukcja zapewnia kilka korzyści:

1. Upraszcza kod eliminując potrzebę korzystania z T-SQL.

Przykładowo zamiast:

SELECT TrN_KntNumer, Kwota=SUM(TrN_NettoR)
INTO #ObrotyKontrahentowMiesieczne
FROM CDN.TraNag
WHERE TrN_RokMiesiac=201405
GROUP BY TrN_KntNumer

SELECT Knt_GIDNumer, Knt_Akronim, Knt_Nazwa1
INTO #WybraniKontrahenci
FROM CDN.KntKarty
WHERE Knt_Miasto='Łódź'

SELECT Knt_Akronim, Knt_Nazwa1, Kwota=ISNULL(Kwota,0.00)
FROM #WybraniKontrahenci
LEFT JOIN #ObrotyKontrahentowMiesieczne
ON Knt_GIDNumer=TrN_KntNumer

DROP TABLE #ObrotyKontrahentowMiesieczne
DROP TABLE #WybraniKontrahenci

Można zrobić:

WITH
WybraniKontrahenci AS
       (SELECT Knt_GIDNumer, Knt_Akronim, Knt_Nazwa1
       FROM CDN.KntKarty
       WHERE Knt_Miasto='Łódź'),
ObrotyKontrahentowMiesieczne AS
       (SELECT TrN_KntNumer, Kwota=SUM(TrN_NettoR)
       FROM CDN.TraNag
       WHERE TrN_RokMiesiac=201405
       GROUP BY TrN_KntNumer)
SELECT Knt_Akronim, Knt_Nazwa1, Kwota=ISNULL(Kwota,0.00)
FROM WybraniKontrahenci
LEFT JOIN ObrotyKontrahentowMiesieczne
ON Knt_GIDNumer=TrN_KntNumer

 

2. Optymalizuje szybkość działania zapytań.

Zamiast:

SELECT TrN_GIDNumer, WynikFunkcji=dbo.bardzoSkomplikowanaFunkcja (TrN_GIDNumer)
FROM CDN.TraNag
WHERE TrNRokMiesiac=201405 AND
(dbo.bardzoSkomplikowanaFunkcja(TrN_GIDNumer)=1 OR dbo.bardzoSkomplikowanaFunkcja(TrN_GIDNumer)=2)


Można zrobić:

WITH
ZapytanieWyliczajaceFunkcje AS
       SELECT TrN_GIDNumer, WynikFunkcji=dbo.bardzoSkomplikowanaFunkcja (TrN_GIDNumer)
       FROM CDN.TraNag
       WHERE TrN_RokMiesiac=201405
SELECT * FROM ZapytanieWyliczajaceFunkcje
WHERE
WynikFunkcji=1 OR WynikFunkcji=2


3. Umożliwia wielopoziomowe rekurencyjne tworzenie zapytań bazując na poprzednich zapytaniach.

Można zrobić:

WITH
ObrotyWszystkie AS
       (SELECT TrN_KntNumer, Rok=TrN_RokMiesiac/100, Miesiac=TrN_RokMiesiac%100, Kwota=TrN_NettoR
       FROM CDN.TraNag
       WHERE TrN_RokMiesiac=201405),
ObrotyMiesiacami AS
       (SELECT TrN_KntNumer, Rok, Miesiac, Kwota=SUM(Kwota)
       FROM ObrotyWszystkie GROUP BY TrN_KntNumer, Rok, Miesiac),
ObrotyLatami AS
       (SELECT TrN_KntNumer, Rok, Kwota=SUM(Kwota) 
       FROM ObrotyMiesiacami GROUP BY TrN_KntNumer, Rok)
SELECT * FROM ObrotyLatami

 

Łącząc te trzy cechy można więc tworzyć zapytania proste i wydajne, a nawet rekurencyjne (choć z narzuconą liczbą rekurencji).

Korzystając ze składni CTE stworzymy w kolejnych krokach raport wyliczający obroty na dokumentach handlowych (w uproszczeniu zakup i sprzedaż) z podziałem na kwartały w układzie tabeli przestawnej.

Krok 1 – podstawowe dane dla raportu

Na pierwszym poziomie zestawienia znajdą się wszystkie niezbędne dane w klasycznym kolumnowym układzie tabeli. W każdym kolejnym kroku będziemy wyświetlać nowe wyniki.

WITH
Krok1 AS
       (SELECT DokTyp=OB_Skrot,
       Rok=TrN_RokMiesiac/100,
       Miesiac=trn_RokMiesiac%100,
       Obrot=TrN_NettoR+TrN_NettoP FROM CDN.TraNag
       JOIN CDN.Obiekty ON TrN_GIDTyp=OB_GIDTyp
WHERE TrN_TrNRok=2014)
SELECT * FROM Krok1

Krok 2 – wyliczenie pola Kwartał

Numer kwartału nie jest zapisany w tabeli bezpośrednio – trzeba wyliczyć go na podstawie numeru miesiąca. Można to wprawdzie było zrobić już w kroku pierwszym, ale skomplikowałoby to kod i dołożyło procesorowi dodatkowej pracy z wielokrotnym obliczaniem miesiąca w warunkach CASE. Zróbmy to w kroku 2:

WITH
Krok1 AS
       (SELECT DokTyp=OB_Skrot,
       Rok=TrN_RokMiesiac/100,
       Miesiac=trn_RokMiesiac%100,
       Obrot=TrN_NettoR+TrN_NettoP FROM CDN.TraNag
       JOIN CDN.Obiekty ON TrN_GIDTyp=OB_GIDTyp
WHERE TrN_TrNRok=2014),
Krok2 AS
       (SELECT DokTyp,
       Rok,
       Kwartal=CASE
             WHEN Miesiac BETWEEN 1 AND 3 then 1
             WHEN Miesiac BETWEEN 4 AND 6 then 2
             WHEN Miesiac BETWEEN 7 AND 9 then 3
             WHEN Miesiac BETWEEN 10 AND 12 then 4
             END,
       Miesiac,
       Obrot
       FROM Krok1)
SELECT * FROM Krok2

Krok 3 – agregacja danych

Dane do pól mamy już wyliczone, a zestaw danych zawężony. Wykonanie agregacji w tym kroku będzie więc znacznie szybsze. Dodajemy agregację w kroku 3:

WITH
Krok1 AS
       (SELECT DokTyp=OB_Skrot,
       Rok=TrN_RokMiesiac/100,
       Miesiac=trn_RokMiesiac%100,
       Obrot=TrN_NettoR+TrN_NettoP FROM CDN.TraNag
       JOIN CDN.Obiekty ON TrN_GIDTyp=OB_GIDTyp
WHERE TrN_TrNRok=2014),
Krok2 AS
       (SELECT DokTyp,
       Rok,
       Kwartal=CASE
             WHEN Miesiac BETWEEN 1 AND 3 then 1
             WHEN Miesiac BETWEEN 4 AND 6 then 2
             WHEN Miesiac BETWEEN 7 AND 9 then 3
             WHEN Miesiac BETWEEN 10 AND 12 then 4
             END,
       Miesiac,
       Obrot
       FROM Krok1),
Krok3 AS
       (SELECT DokTyp,
       Rok,
       Kwartal,
       Obrot=SUM(Obrot)
       FROM Krok2
       GROUP BY DokTyp, Rok, Kwartal)
SELECT * FROM Krok3

 

Krok 4 – PIVOT danych

Na koniec przeniesiemy kolumnę Kwartal do czterech oddzielnych kolumn. Każda kolumna to obroty tylko dla jednego kwartału ograniczonego warunkiem WHERE. To oznacza potrzebę dodania czterech odrębnych zapytań. Wyświetlając wyniki połączymy te zapytania przez FULL JOIN, aby zapewnić kompletność danych, gdyby w jakimś kwartale ich zabrakło.

WITH
Krok1 AS
       (SELECT DokTyp=OB_Skrot,
       Rok=TrN_RokMiesiac/100,
       Miesiac=trn_RokMiesiac%100,
       Obrot=TrN_NettoR+TrN_NettoP FROM CDN.TraNag
       JOIN CDN.Obiekty ON TrN_GIDTyp=OB_GIDTyp
WHERE TrN_TrNRok=2014),
Krok2 AS
       (SELECT DokTyp,
       Rok,
       Kwartal=CASE
             WHEN Miesiac BETWEEN 1 AND 3 then 1
             WHEN Miesiac BETWEEN 4 AND 6 then 2
             WHEN Miesiac BETWEEN 7 AND 9 then 3
             WHEN Miesiac BETWEEN 10 AND 12 then 4
             END,
       Miesiac,
       Obrot
       FROM Krok1),
Krok3 AS
       (SELECT DokTyp,
       Rok,
       Kwartal,
       Obrot=SUM(Obrot)
       FROM Krok2
       GROUP BY DokTyp, Rok, Kwartal),
Krok4Kwartal1 AS
       (SELECT DokTyp, Rok, Kwartal, Obrot FROM Krok3 WHERE Kwartal=1),
Krok4Kwartal2 AS
       (SELECT DokTyp, Rok, Kwartal, Obrot FROM Krok3 WHERE Kwartal=2),
Krok4Kwartal3 AS
       (SELECT DokTyp, Rok, Kwartal, Obrot FROM Krok3 WHERE Kwartal=3),
Krok4Kwartal4 AS
       (SELECT DokTyp, Rok, Kwartal, Obrot FROM Krok3 WHERE Kwartal=4)
SELECT * FROM Krok4Kwartal1
FULL JOIN Krok4Kwartal2 ON Krok4Kwartal1.DokTyp=Krok4Kwartal2.DokTyp
FULL JOIN Krok4Kwartal3 ON Krok4Kwartal2.DokTyp=Krok4Kwartal3.DokTyp
FULL JOIN Krok4Kwartal4 ON Krok4Kwartal3.DokTyp=Krok4Kwartal4.DokTyp

 

Krok 5 – oswajanie wyników

Końcowe zapytanie zwraca wszystkie kolumny ze wszystkich czterech zapytań, co jest mało czytelne. Proponuję nieco je zmodyfikować, aby raport stał się bardziej przyjazny (zmiana tylko w zakresie składni SELECT):

WITH
Krok1 AS
       (SELECT DokTyp=OB_Skrot,
       Rok=TrN_RokMiesiac/100,
       Miesiac=trn_RokMiesiac%100,
       Obrot=TrN_NettoR+TrN_NettoP FROM CDN.TraNag
       JOIN CDN.Obiekty ON TrN_GIDTyp=OB_GIDTyp
WHERE TrN_TrNRok=2009),
Krok2 AS
       (SELECT DokTyp,
       Rok,
       Kwartal=CASE
             WHEN Miesiac BETWEEN 1 AND 3 then 1
             WHEN Miesiac BETWEEN 4 AND 6 then 2
             WHEN Miesiac BETWEEN 7 AND 9 then 3
             WHEN Miesiac BETWEEN 10 AND 12 then 4
             END,
       Miesiac,
       Obrot
       FROM Krok1),
Krok3 AS
       (SELECT DokTyp,
       Rok,
       Kwartal,
       Obrot=SUM(Obrot)
       FROM Krok2
       GROUP BY DokTyp, Rok, Kwartal),
Krok4Kwartal1 AS
       (SELECT DokTyp, Rok, Kwartal, Obrot FROM Krok3 WHERE Kwartal=1),
Krok4Kwartal2 AS
       (SELECT DokTyp, Rok, Kwartal, Obrot FROM Krok3 WHERE Kwartal=2),
Krok4Kwartal3 AS
       (SELECT DokTyp, Rok, Kwartal, Obrot FROM Krok3 WHERE Kwartal=3),
Krok4Kwartal4 AS
       (SELECT DokTyp, Rok, Kwartal, Obrot FROM Krok3 WHERE Kwartal=4)
SELECT [Typ dokumentu]=COALESCE (Krok4Kwartal1.DokTyp, Krok4Kwartal2.DokTyp, Krok4Kwartal3.DokTyp, Krok4Kwartal4.DokTyp),
[Rok]=COALESCE (Krok4Kwartal1.Rok, Krok4Kwartal2.Rok, Krok4Kwartal3.Rok, Krok4Kwartal4.Rok),
[Obroty I]=ISNULL(Krok4Kwartal1.Obrot, 0.00),
[Obroty II]=ISNULL(Krok4Kwartal2.Obrot, 0.00),
[Obroty III]=ISNULL(Krok4Kwartal3.Obrot, 0.00),
[Obroty IV]=ISNULL(Krok4Kwartal4.Obrot, 0.00)
FROM Krok4Kwartal1
FULL JOIN Krok4Kwartal2 ON Krok4Kwartal1.DokTyp=Krok4Kwartal2.DokTyp
FULL JOIN Krok4Kwartal3 ON Krok4Kwartal2.DokTyp=Krok4Kwartal3.DokTyp
FULL JOIN Krok4Kwartal4 ON Krok4Kwartal3.DokTyp=Krok4Kwartal4.DokTyp

 

Wynik zapytania:

Comarch ERP XL raport tabeli przestawnej

Zastosowanie CTE w Comarch ERP XL

Z użyciem składni CTE możesz tworzyć szybkie i wydajne raporty bez potrzeby sięgania do Transact SQL. Nie ma potrzeby tworzyć dla nich uciążliwych procedury SQL, co upraszcza proces dodawania nowych raportów i modyfikacji istniejących. Zapytania CTE mogą być wykorzystane bezpośrednio w Comarch ERP XL na poziomie Raportów typu wykres, Szybkich raportów SQL, polecenia Command w obrębie Crystal Reports, a nawet jako zaawansowany warunek EXISTS dla filtrów.

Ponadto zastosowanie mechanizmu rekurencji umożliwia zbieranie danych ustrukturyzowanych, takich jak drzewo towarów, kontrahentów, czy technologii produkcji.

Chcesz wyliczyć koszt produktu? Możesz sięgnąć do kosztów materiałów na tym produkcie. A co jeśli materiały też są półproduktami? A jeśli półprodukty składają się z innych półproduktów? Dzięki zastosowaniu CTE sięgniesz do dowolnego poziomu technologii bez tworzenia skomplikowanych tasiemców SQL. 

Ten przykład został zaczerpnięty ze szkolenia Comarch ERP XL Średniozaawansowany SQL

Jeśli chcesz dowiedzieć się więcej na temat szkoleń prowadzonych przeze mnie i nasz zespół to zapraszam Cię do odwiedzenia naszego Centrum Szkoleniowego Prospeo.
 

Sample2ść h3 tag

Sample pararaph