MODUL 2 - LEKCIJA 4

CTE i Window Funkcije

Moderni T-SQL: Od rekurzivnih stabala do kompleksne analitike bez GROUP BY zamke

⏱️ Trajanje: ~4 časa 📚 Nivo: Napredni 🎯 Praktični primjeri: 8

📖 Evolucija T-SQL Upita

Pisanje upita u 2025. godini se drastično razlikuje od onog prije 15 godina. Zamorne podupite (subqueries) i kursore zamijenili smo Common Table Expressions (CTE) i Window funkcijama. Ovi alati ne samo da čine kod čitljivijim, već omogućavaju SQL Serveru da bolje optimizuje plan izvršenja.

📑 Common Table Expressions (CTE): Privremeni Pogledi

CTE je privremeni set rezultata koji možete definisati unutar scope-a jednog SELECT, INSERT, UPDATE ili DELETE upita.

🛠️ Osnovni CTE Primjer
WITH EconomicData_CTE AS (
    -- Definišemo "virtuelnu tabelu"
    SELECT r.RegionName, SUM(ed.GDP) as TotalGDP
    FROM Stats.EconomicData ed
    INNER JOIN Stats.Regions r ON ed.RegionID = r.RegionID
    WHERE ed.Year = 2023
    GROUP BY r.RegionName
)
-- Koristimo je odmah ispod
SELECT * FROM EconomicData_CTE WHERE TotalGDP > 1000000000;

Rekurzivni CTE: Snaga Stabala

Ovo je jedini način da bez kursora prođete kroz hijerarhije (npr. menadžment struktura ili folderi).

🌲 Rekurzija u T-SQL
WITH DepartmentHierarchy_CTE AS (
    -- Anchor member: Odakle počinjemo (odjeljenja bez parent-a)
    SELECT DepartmentID, DepartmentName, ManagerID, 0 AS Level
    FROM Stats.Departments WHERE ManagerID IS NULL
    
    UNION ALL
    
    -- Recursive member: Kako se povezujemo dalje (zaposlenici po odjeljenjima)
    SELECT d.DepartmentID, d.DepartmentName, d.ManagerID, dh.Level + 1
    FROM Stats.Departments d
    INNER JOIN Stats.Employees e ON d.ManagerID = e.EmployeeID
    INNER JOIN DepartmentHierarchy_CTE dh ON e.DepartmentID = dh.DepartmentID
)
SELECT * FROM DepartmentHierarchy_CTE ORDER BY Level;

🪟 Window Funkcije: Analitika u letu

Window funkcije omogućavaju da vršite agregacije (SUM, AVG) i rangiranje, a da pri tome ne gubite individualne redove (za razliku od GROUP BY).

1. Klauzula OVER(PARTITION BY ...)

PARTITION BY dijeli redove u grupe, a funkcija se primjenjuje na svaku grupu posebno.

⚡ Agregacija bez Group By
SELECT 
    d.DepartmentName, 
    p.Budget,
    -- Prikazuje total budžeta po odjeljenju pored SVAKOG projekta
    SUM(p.Budget) OVER(PARTITION BY d.DepartmentName) as TotalBudgetByDepartment,
    -- Prikazuje procenat ovog projekta u odnosu na total budžet odjeljenja
    CAST(p.Budget * 100.0 / SUM(p.Budget) OVER(PARTITION BY d.DepartmentName) AS DECIMAL(5,2)) as PctOfTotal
FROM Stats.Projects p
INNER JOIN Stats.Departments d ON p.DepartmentID = d.DepartmentID;

2. Rangiranje: ROW_NUMBER, RANK, DENSE_RANK

Funkcija Ponašanje kod "tie" (istih vrijednosti)
ROW_NUMBER() Uvijek unikatni brojevi (1, 2, 3, 4).
RANK() Isti broj za iste vrijednosti, preskače brojeve (1, 2, 2, 4).
DENSE_RANK() Isti broj za iste vrijednosti, NE preskače brojeve (1, 2, 2, 3).

3. Time Travel u redovima: LEAD i LAG

Ove funkcije vam omogućavaju da pristupite vrijednosti prethodnog ili sljedećeg reda.

🔍 Analiza Trenda
SELECT 
    ed.Year, 
    ed.GDP,
    r.RegionName,
    -- Vrijednost GDP-a od PROŠLE GODINE za istu regiju
    LAG(ed.GDP) OVER(PARTITION BY ed.RegionID ORDER BY ed.Year) as PreviousGDP,
    -- Razlika u odnosu na prošlu godinu
    ed.GDP - LAG(ed.GDP) OVER(PARTITION BY ed.RegionID ORDER BY ed.Year) as GDPGrowth
FROM Stats.EconomicData ed
INNER JOIN Stats.Regions r ON ed.RegionID = r.RegionID;

🎯 Praktična Vježba: Detekcija Duplikata

Zadatak: Čišćenje "Prljavih" Podataka

U tabeli Stats.ReportData imate hiljade dupliranih podataka za isti izvještaj i regiju. Morate zadržati samo najnoviji rekord za svaku kombinaciju ReportID i RegionID, a ostale obrisati.

Korak 1: Koristite CTE i ROW_NUMBER() da identifikujete duplikate.

Korak 2: Izvršite DELETE direktno nad tim CTE-om.

💡 Ekspertsko Rješenje
WITH DuplicateFinder AS (
    SELECT 
        ReportDataID,
        ReportID,
        RegionID,
        CreatedDate,
        ROW_NUMBER() OVER(
            PARTITION BY ReportID, RegionID 
            ORDER BY CreatedDate DESC
        ) as RowNum
    FROM Stats.ReportData
)
-- Brišemo sve što nije broj 1 (najnoviji)
DELETE FROM Stats.ReportData 
WHERE ReportDataID IN (
    SELECT ReportDataID FROM DuplicateFinder WHERE RowNum > 1
);
GO

✅ Zaključak

CTE i Window funkcije su "švicarski nož" modernog SQL developera:

📚 Sljedeća Lekcija

U Lekciji 2.5 ulazimo u svijet JSON-a. Naučićete kako SQL Server 2022 tretira polustrukturirane podatke kao da su obične tabele.