MODUL 3 - LEKCIJA 2

Dizajn i Implementacija Stored Procedura

Enkapsulacija poslovne logike, Table-Valued Parameters i robusno rukovanje greškama

⏱️ Trajanje: ~4 časa 📚 Nivo: Srednji do Napredni 🎯 Praktični primjeri: 7

📖 Srce Programabilnosti

Stored Procedura (SP) nije samo kolekcija SQL naredbi. To je programski modul sačuvan unutar baze koji može prihvatati parametre, izvršavati grananja (IF/ELSE), petlje (WHILE) i vraćati rezultate aplikaciji. One su ključne za performanse jer SQL Server čuva njihov Execution Plan u memoriji.

📥 Parametri: Više od običnih varijabli

1. OUTPUT Parametri

Koristimo ih kada želimo vratiti jednu ili više specifičnih vrijednosti bez vraćanja cijelog result-seta.

2. Table-Valued Parameters (TVP)

Ovo je revolucija u komunikaciji Aplikacija-Baza. TVP vam omogućava da pošaljete hiljade redova u SP kao jedan parametar, umjesto da pozivate bazu hiljadu puta.

🛠️ Implementacija TVP-a
-- 1. Definisanje "Tipa Tabele"
CREATE TYPE Stats.ReportDataType AS TABLE (
    DataType NVARCHAR(50),
    Value DECIMAL(18,2),
    RegionID INT,
    Year INT
);
GO

-- 2. Korištenje tipa u proceduri (READONLY je obavezan)
CREATE PROCEDURE Stats.usp_BulkInsertReportData
    @ReportID INT,
    @DataItems Stats.ReportDataType READONLY
AS
BEGIN
    INSERT INTO Stats.ReportData (ReportID, DataType, Value, RegionID, Year)
    SELECT @ReportID, DataType, Value, RegionID, Year FROM @DataItems;
END;
GO

🛡️ Robusno Rukovanje Greškama

U profesionalnom kodu ne koristimo staru @@ERROR varijablu. Koristimo TRY...CATCH blokove i THROW naredbu.

⚠️ Zašto XACT_ABORT ON?

Uvijek podesite SET XACT_ABORT ON na početku procedure. Ovo osigurava da SQL Server automatski prekine i poništi (Rollback) cijelu transakciju ako se desi bilo kakva greška, sprečavajući "djelimično upisane" podatke.

⚡ Standardni Error Handling Template
CREATE PROCEDURE dbo.SafeUpdate
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON; -- Vitalni dio!

    BEGIN TRY
        BEGIN TRANSACTION
            -- Vaš SQL kod ovdje
        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
        
        -- Logiranje greške u posebnu tabelu (opcionalno)
        -- Ponovno bacanje greške aplikaciji
        THROW; 
    END CATCH
END;

🚀 Performance Tuning: Parameter Sniffing

Parameter Sniffing se dešava kada SQL Server kreira plan izvršenja prilagođen PRVOM parametru koji je poslan u SP. Ako je sljedeći parametar drastično drugačiji (npr. traži 1 rekord naspram 1 milion), plan će biti užasno neefikasan.

💡 Kako popraviti?

  • Koristite OPTIMIZE FOR UNKNOWN na kraju upita.
  • Koristite lokalne varijable unutar procedure.
  • Dodajte WITH RECOMPILE ako se podaci drastično mijenjaju svaki put.

🎯 Praktična Vježba: Kompleksni Workflow

Zadatak: Kreiranje Izvještaja sa Provjerom Budžeta

Morate napisati proceduru koja prima DepartmentID i TVP sa listom podataka. Procedura mora: 1. Provjeriti da li odjeljenje ima dovoljno budžeta za izvještaj. 2. Ako nema, baciti user-friendly grešku. 3. Ako ima, kreirati izvještaj i dodati podatke (sve u transakciji).

💡 Profesionalno Rješenje
CREATE PROCEDURE Stats.usp_CreateReport
    @DepartmentID INT,
    @CreatedBy INT,
    @ReportName NVARCHAR(200),
    @ReportType NVARCHAR(50),
    @ReportData Stats.ReportDataType READONLY
AS
BEGIN
    SET XACT_ABORT ON;
    BEGIN TRY
        BEGIN TRANSACTION
            -- Provjera budžeta odjeljenja
            DECLARE @DepartmentBudget DECIMAL(15,2);
            SELECT @DepartmentBudget = Budget FROM Stats.Departments WHERE DepartmentID = @DepartmentID;
            
            IF @DepartmentBudget < 10000 -- Minimalni budžet za izvještaj
                THROW 50001, 'Odjeljenje nema dovoljno budžeta za kreiranje izvještaja.', 1;

            -- Kreiranje izvještaja
            DECLARE @NewReportID INT;
            INSERT INTO Stats.Reports (ReportName, DepartmentID, CreatedBy, ReportType, Status)
            VALUES (@ReportName, @DepartmentID, @CreatedBy, @ReportType, 'Draft');
            SET @NewReportID = SCOPE_IDENTITY();
            
            -- Dodavanje podataka izvještaja
            INSERT INTO Stats.ReportData (ReportID, DataType, Value, RegionID, Year)
            SELECT @NewReportID, DataType, Value, RegionID, Year FROM @ReportData;
            
        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        THROW;
    END CATCH
END;

✅ Zaključak

Stored procedure su vaša prva linija odbrane:

📚 Sljedeća Lekcija

U Lekciji 3.3 prelazimo na Funkcije (UDF). Naučićete razliku između Scalar i Table-Valued funkcija i zašto su one zloglasne po pitanju performansi ako ih pogrešno koristite.