A modern adatkezelés világában a relációs adatbázis-rendszerek (RDBMS) alapvető szerepet töltenek be. Ezek közül a Microsoft SQL Server az egyik legnépszerűbb és legszélesebb körben használt platform, amelynek szíve és lelke a T-SQL, azaz a Transact-SQL. Ez a kiterjesztett SQL nyelv nem csupán egy technikai kifejezés; ez az a nyelv, amelyen keresztül az adatbázisokkal kommunikálunk, adatokat kezelünk, és összetett üzleti logikát valósítunk meg. A T-SQL megértése kulcsfontosságú minden adatbázis-fejlesztő, adatbázis-adminisztrátor és adatelemző számára, aki Microsoft SQL Server környezetben dolgozik. Ez a cikk részletesen bemutatja a T-SQL-t, annak jelentését, működését, alapvető és haladó funkcióit, valamint a hatékony használatának legjobb gyakorlatait.
Mi is az a T-SQL valójában?
A T-SQL, vagy Transact-SQL, a Microsoft és a Sybase által kifejlesztett kiterjesztése a standard SQL (Structured Query Language) nyelvnek. Míg az SQL egy ANSI/ISO szabvány, amely meghatározza az adatbázisokkal való interakció alapvető szintaxisát és szemantikáját, a T-SQL hozzáadott funkciókkal bővíti ezt a szabványt, lehetővé téve a Microsoft SQL Server felhasználók számára, hogy sokkal összetettebb feladatokat végezzenek el. Ezek a kiterjesztések magukban foglalják a procedurális programozási elemeket, mint például a változók, feltételes utasítások, ciklusok és hibakezelési mechanizmusok, amelyek a standard SQL-ben nem találhatók meg.
A T-SQL lehetővé teszi a fejlesztők számára, hogy ne csak adatokat kérdezzenek le, szúrjanak be, frissítsenek vagy töröljenek, hanem teljes körű programlogikát építsenek be az adatbázisba. Ez magában foglalja a tárolt eljárások (stored procedures), függvények (functions), triggerek (triggers) és nézetek (views) létrehozását, amelyek mind hozzájárulnak az adatbázis funkcionalitásának és teljesítményének optimalizálásához. Az SQL Server környezetben a T-SQL az elsődleges nyelv, amellyel a fejlesztők interakcióba lépnek az adatbázismotorral, legyen szó adatbázis-objektumok létrehozásáról, adatok manipulálásáról vagy összetett üzleti szabályok implementálásáról.
T-SQL és a standard SQL: a különbségek megértése
A standard SQL egy deklaratív nyelv, ami azt jelenti, hogy a felhasználó leírja, mit szeretne elérni, és nem azt, hogyan. Például, ha adatokat kérdezünk le, megadjuk a kívánt oszlopokat és a szűrési feltételeket, de nem foglalkozunk azzal, hogy az adatbázis-motor hogyan találja meg vagy dolgozza fel ezeket az adatokat. Ezzel szemben a T-SQL, miközben magában foglalja a standard SQL összes deklaratív képességét, hozzáadja a procedurális programozás elemeit is. Ez lehetővé teszi a fejlesztők számára, hogy lépésről lépésre, algoritmikus módon határozzák meg a műveleteket, hasonlóan más programozási nyelvekhez, mint például a C# vagy a Java.
A T-SQL által bevezetett legfontosabb kiterjesztések közé tartoznak a következők:
- Változók deklarálása és használata: Lehetővé teszi ideiglenes értékek tárolását és manipulálását a szkriptekben.
- Vezérlési szerkezetek: Az
IF...ELSE
feltételes utasítások és aWHILE
ciklusok segítségével a programfolyamat irányítható. - Hibakezelés: A
TRY...CATCH
blokkok biztosítják a robusztus hibakezelést, megakadályozva a szkriptek váratlan leállását. - Tranzakciókezelés: Kiterjesztett lehetőségek a tranzakciók kezelésére, beleértve a mentési pontokat (
SAVE TRANSACTION
). - Tárolt eljárások és függvények: Újrahasználható kódblokkok létrehozása, amelyek paramétereket fogadhatnak és értékeket adhatnak vissza.
- Triggerek: Automatikus műveletek végrehajtása meghatározott adatbázis-eseményekre (pl. beszúrás, frissítés, törlés) válaszul.
Ezek a kiterjesztések teszik a T-SQL-t sokkal erőteljesebbé és rugalmasabbá az egyszerű adatmanipulációnál, lehetővé téve komplex üzleti logika közvetlen implementálását az adatbázis-szinten.
A T-SQL alapvető komponensei: DDL, DML, DCL, TCL
Az SQL nyelvet hagyományosan négy fő kategóriába soroljuk, amelyek mindegyike a T-SQL-ben is megtalálható, gyakran kiterjesztett formában:
Adatdefiníciós nyelv (DDL – Data Definition Language)
A DDL utasítások az adatbázis-objektumok, például táblák, nézetek, indexek, tárolt eljárások és függvények létrehozására, módosítására és törlésére szolgálnak. A T-SQL-ben ezek az utasítások rendkívül gazdagok és számos opciót kínálnak az objektumok finomhangolására. Néhány alapvető DDL parancs:
CREATE
: Objektumok létrehozása (pl.CREATE TABLE
,CREATE VIEW
,CREATE PROCEDURE
).ALTER
: Meglévő objektumok módosítása (pl.ALTER TABLE ADD COLUMN
,ALTER PROCEDURE
).DROP
: Objektumok törlése (pl.DROP TABLE
,DROP INDEX
).
Például, egy tábla létrehozása T-SQL-ben a következőképpen nézhet ki:
CREATE TABLE Ugyfelek (
UgyfelID INT PRIMARY KEY IDENTITY(1,1),
Nev NVARCHAR(100) NOT NULL,
Email NVARCHAR(100) UNIQUE,
RegisztracioDatuma DATETIME DEFAULT GETDATE(),
Aktiv BIT DEFAULT 1
);
Ez a példa nem csak egy táblát hoz létre, hanem definiálja az oszlopok adattípusait, a PRIMARY KEY és UNIQUE megszorításokat, valamint alapértelmezett értékeket a RegisztracioDatuma és Aktiv oszlopokhoz. Az IDENTITY(1,1) egy T-SQL specifikus kiterjesztés, amely automatikusan növekvő azonosítót generál.
Adatmanipulációs nyelv (DML – Data Manipulation Language)
A DML utasítások az adatbázisban tárolt adatok lekérdezésére, beszúrására, frissítésére és törlésére szolgálnak. Ezek a leggyakrabban használt T-SQL parancsok, amelyekkel a fejlesztők nap mint nap dolgoznak. A T-SQL számos kiterjesztést kínál a standard DML parancsokhoz, amelyek fejlettebb adatkezelési lehetőségeket biztosítanak.
SELECT
: Adatok lekérdezése az adatbázisból. A T-SQL gazdagítja ezt olyan funkciókkal, mint aTOP
,OFFSET/FETCH
,CTE
-k, ablakfüggvények és számosJOIN
típus.INSERT
: Új adatok beszúrása táblákba.UPDATE
: Meglévő adatok módosítása.DELETE
: Adatok törlése táblákból.MERGE
: Kombinálja azINSERT
,UPDATE
ésDELETE
műveleteket egyetlen utasításban, a forrás és cél táblák közötti különbségek alapján. Ez egy erőteljes T-SQL kiterjesztés.
Egy példa a SELECT
utasításra, amely a T-SQL fejlett funkcióit használja:
WITH LegutobbiRendelesek AS (
SELECT
r.RendelesID,
r.UgyfelID,
r.RendelesDatuma,
r.Osszeg,
ROW_NUMBER() OVER (PARTITION BY r.UgyfelID ORDER BY r.RendelesDatuma DESC) AS rn
FROM
Rendelesek r
)
SELECT
lr.RendelesID,
lr.UgyfelID,
lr.RendelesDatuma,
lr.Osszeg
FROM
LegutobbiRendelesek lr
WHERE
lr.rn = 1;
Ez a lekérdezés Közös Tábla Kifejezést (CTE) és ablakfüggvényt (ROW_NUMBER()) használ, hogy minden ügyfél legutóbbi rendelését lekérdezze. Ezek a funkciók jelentősen növelik a T-SQL rugalmasságát és erejét az összetett lekérdezések megfogalmazásában.
Adatvezérlő nyelv (DCL – Data Control Language)
A DCL utasítások az adatbázis-hozzáférés és a jogosultságok kezelésére szolgálnak. Ezekkel a parancsokkal lehet szabályozni, hogy ki férhet hozzá az adatokhoz és milyen műveleteket hajthat végre. A T-SQL-ben a DCL parancsok kulcsfontosságúak az adatbázis biztonságának fenntartásához.
GRANT
: Jogosultságok megadása felhasználóknak vagy szerepköröknek.REVOKE
: Megadott jogosultságok visszavonása.DENY
: Jogosultságok megtagadása, még akkor is, ha a felhasználó egy másik szerepkörön keresztül rendelkezne velük.
Például, egy felhasználónak jogosultságot adhatunk egy tábla lekérdezésére:
GRANT SELECT ON Ugyfelek TO 'WebAlkalmazasFelhasznalo';
Ez a parancs biztosítja, hogy a ‘WebAlkalmazasFelhasznalo’ nevű felhasználó csak olvashassa az ‘Ugyfelek’ tábla adatait, de ne tudja módosítani vagy törölni azokat.
Tranzakciókezelő nyelv (TCL – Transaction Control Language)
A TCL utasítások a tranzakciók kezelésére szolgálnak, biztosítva az adatbázis konzisztenciáját és integritását. A tranzakciók olyan műveletek sorozatai, amelyeket egyetlen logikai egységként hajtanak végre: vagy mindegyik sikeresen lefut, vagy egyik sem. A T-SQL kiterjesztett lehetőségeket kínál a tranzakciók kezelésére, beleértve a beágyazott tranzakciókat és a mentési pontokat.
BEGIN TRANSACTION
: Egy új tranzakció indítása.COMMIT TRANSACTION
: A tranzakcióban végrehajtott összes módosítás véglegesítése.ROLLBACK TRANSACTION
: A tranzakcióban végrehajtott összes módosítás visszavonása, visszaállítva az adatbázist a tranzakció előtti állapotba.SAVE TRANSACTION
: Mentési pont létrehozása a tranzakción belül, amelyre később visszagörgethetünk (ROLLBACK TO SAVEPOINT
).
Egy klasszikus példa a tranzakciókezelésre egy bankszámláról történő átutalás:
BEGIN TRANSACTION;
BEGIN TRY
UPDATE Szamlak
SET Egyenleg = Egyenleg - 100
WHERE SzamlaID = 1;
UPDATE Szamlak
SET Egyenleg = Egyenleg + 100
WHERE SzamlaID = 2;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
THROW; -- Újradobja a hibát
END CATCH;
Ebben a példában, ha bármelyik UPDATE
utasítás hibába ütközik, az egész tranzakció visszagörgetésre kerül, biztosítva, hogy az egyik számláról levont pénz ne tűnjön el anélkül, hogy a másik számlán megjelenne. Ez a tranzakciós integritás alapvető fontosságú az adatbázisok megbízhatósága szempontjából.
Programozási elemek és vezérlési szerkezetek T-SQL-ben

A T-SQL igazi ereje a procedurális programozási képességeiben rejlik, amelyek lehetővé teszik a komplex logikai folyamatok implementálását az adatbázison belül. Ezek az elemek jelentősen megkülönböztetik a T-SQL-t a standard SQL-től.
Változók deklarálása és használata
A T-SQL lehetővé teszi a változók deklarálását és használatát a szkriptekben. A változók ideiglenes tárolóhelyként szolgálnak az adatok számára, amelyeket a szkript futása során lehet módosítani és felhasználni. A változónevek elé egy @
jelet kell tenni.
DECLARE @UgyfelNev NVARCHAR(100);
DECLARE @RendelesOsszeg DECIMAL(10, 2);
SET @UgyfelNev = 'Kovács János';
SELECT @RendelesOsszeg = SUM(Osszeg) FROM Rendelesek WHERE UgyfelID = 1;
PRINT 'Az ügyfél neve: ' + @UgyfelNev;
PRINT 'A rendelések összege: ' + CAST(@RendelesOsszeg AS NVARCHAR(20));
A változók deklarálásánál meg kell adni az adattípust is, ami biztosítja a típusbiztonságot. A SET
vagy a SELECT
utasítással adhatunk értéket a változóknak.
Feltételes vezérlés: IF…ELSE
Az IF...ELSE
szerkezet lehetővé teszi a kódblokkok feltételes végrehajtását. Ez alapvető fontosságú a dinamikus üzleti logika megvalósításához.
DECLARE @Keszlet INT = 10;
DECLARE @RendeltMennyiseg INT = 5;
IF @RendeltMennyiseg <= @Keszlet
BEGIN
PRINT 'A rendelés teljesíthető.';
UPDATE Termekek SET Keszlet = Keszlet - @RendeltMennyiseg WHERE TermekID = 1;
END
ELSE
BEGIN
PRINT 'A rendelés nem teljesíthető, nincs elegendő készlet.';
END;
A BEGIN...END
blokkok használata javasolt, ha több utasítást szeretnénk végrehajtani az IF
vagy ELSE
ágban.
Ciklusok: WHILE
A WHILE
ciklus egy kódblokkot ismételten végrehajt, amíg egy adott feltétel igaz. Bár a T-SQL-ben gyakran előnyösebbek a halmaz alapú (set-based) műveletek a ciklusok helyett a teljesítmény miatt, vannak esetek, amikor a WHILE
ciklus elengedhetetlen.
DECLARE @Szamlalo INT = 1;
WHILE @Szamlalo <= 5
BEGIN
PRINT 'Ciklus futása: ' + CAST(@Szamlalo AS NVARCHAR(10));
SET @Szamlalo = @Szamlalo + 1;
END;
Ciklusok használatakor ügyelni kell arra, hogy legyen egy kilépési feltétel, különben végtelen ciklusba kerülhet a szkript.
Kurzorok: sorról sorra feldolgozás
A kurzorok lehetővé teszik az eredményhalmaz sorról sorra történő feldolgozását, ami ellentétes a T-SQL halmaz alapú (set-based) filozófiájával. Bár a kurzorok rugalmasságot biztosítanak, általában teljesítményproblémákat okozhatnak nagy adatmennyiségek esetén. Használatuk csak akkor javasolt, ha nincs más halmaz alapú megoldás.
DECLARE @UgyfelNev NVARCHAR(100);
DECLARE UgyfelKurzor CURSOR FOR
SELECT Nev FROM Ugyfelek WHERE Aktiv = 1;
OPEN UgyfelKurzor;
FETCH NEXT FROM UgyfelKurzor INTO @UgyfelNev;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Aktív ügyfél: ' + @UgyfelNev;
FETCH NEXT FROM UgyfelKurzor INTO @UgyfelNev;
END;
CLOSE UgyfelKurzor;
DEALLOCATE UgyfelKurzor;
A kurzorok használata magában foglalja a deklarálást, megnyitást, sorok beolvasását (FETCH), a feldolgozást, bezárást és felszabadítást (DEALLOCATE). A @@FETCH_STATUS
rendszerfüggvény jelzi, hogy sikeres volt-e a sor beolvasása.
Hibakezelés: TRY...CATCH
A T-SQL modern hibakezelése a TRY...CATCH
blokkokra épül, hasonlóan más programozási nyelvekhez. Ez lehetővé teszi a robusztus szkriptek írását, amelyek képesek kezelni a futásidejű hibákat anélkül, hogy leállnának.
BEGIN TRY
-- Kísérlet egy olyan műveletre, ami hibát okozhat
INSERT INTO NemLetezoTabla (Oszlop) VALUES (1);
END TRY
BEGIN CATCH
PRINT 'Hiba történt!';
PRINT 'Hiba szám: ' + CAST(ERROR_NUMBER() AS NVARCHAR(10));
PRINT 'Hiba üzenet: ' + ERROR_MESSAGE();
ROLLBACK TRANSACTION; -- Ha tranzakcióban vagyunk
END CATCH;
A CATCH
blokkban számos rendszerfüggvény áll rendelkezésre a hiba részleteinek lekérdezésére, mint például az ERROR_NUMBER()
, ERROR_MESSAGE()
, ERROR_SEVERITY()
, ERROR_STATE()
, ERROR_LINE()
és ERROR_PROCEDURE()
. A THROW
utasítás használható a hiba újradobására, ha azt a hívó félnek is kezelnie kell.
Fejlettebb T-SQL fogalmak és technikák
A T-SQL nem áll meg az alapvető programozási szerkezeteknél. Számos fejlett funkciót kínál, amelyekkel komplex adatbázis-megoldások hozhatók létre.
Tárolt eljárások (Stored Procedures)
A tárolt eljárások előre fordított T-SQL kódblokkok, amelyeket az adatbázisban tárolnak és többször is végrehajthatók. Paramétereket fogadhatnak, és eredményhalmazokat vagy kimeneti paramétereket adhatnak vissza. Főbb előnyei:
- Teljesítmény: Az SQL Server optimalizálja és gyorsítótárazza a végrehajtási tervet.
- Biztonság: A felhasználók közvetlen táblahozzáférés nélkül futtathatnak eljárásokat.
- Moduláris felépítés: Újrahasználható kódblokkokat hozhatunk létre.
- Hálózati forgalom csökkentése: Kevesebb adatot kell küldeni a kliens és a szerver között.
CREATE PROCEDURE UjUgyfelRegisztracio
@Nev NVARCHAR(100),
@Email NVARCHAR(100),
@UgyfelID INT OUTPUT
AS
BEGIN
SET NOCOUNT ON; -- Elnyomja az érintett sorok számáról szóló üzenetet
INSERT INTO Ugyfelek (Nev, Email)
VALUES (@Nev, @Email);
SET @UgyfelID = SCOPE_IDENTITY(); -- Lekérdezi az utolsó generált azonosítót
END;
Ez az eljárás regisztrál egy új ügyfelet, és visszaadja az újonnan generált UgyfelID-t egy kimeneti paraméteren keresztül. A SCOPE_IDENTITY()
egy T-SQL funkció, amely az aktuális hatókörben létrehozott utolsó IDENTITY értékét adja vissza.
Függvények (Functions)
A T-SQL-ben két fő típusú felhasználó által definiált függvény létezik:
- Skalár értékű függvények (Scalar-valued functions): Egyetlen értéket adnak vissza.
- Tábla értékű függvények (Table-valued functions): Egy táblát adnak vissza eredményként. Ezek lehetnek inline (egyszerű SELECT utasítás) vagy multi-statement (komplexebb logika).
A függvények, ellentétben az eljárásokkal, használhatók SELECT
utasításokban, WHERE
záradékokban és JOIN
feltételekben is. Fő korlátozásuk, hogy nem módosíthatják az adatbázis állapotát (nincsenek INSERT
, UPDATE
, DELETE
utasítások bennük).
CREATE FUNCTION GetTeljesNev (@Keresztnev NVARCHAR(50), @Vezeteknev NVARCHAR(50))
RETURNS NVARCHAR(101)
AS
BEGIN
RETURN @Vezeteknev + ' ' + @Keresztnev;
END;
GO
SELECT dbo.GetTeljesNev('János', 'Kovács') AS TeljesNev;
Ez egy egyszerű skalár értékű függvény, amely két paraméterből fűz össze egy teljes nevet. A dbo
előtag a séma nevét jelöli, és a függvények hívásakor általában javasolt használni.
Triggerek (Triggers)
A triggerek speciális tárolt eljárások, amelyek automatikusan aktiválódnak, amikor egy meghatározott adatbázis-esemény (pl. INSERT
, UPDATE
, DELETE
) bekövetkezik egy táblán vagy nézeten. Lehetővé teszik az adatbázis-integritás fenntartását, auditálást vagy összetett üzleti szabályok kikényszerítését.
Két fő típusuk van:
AFTER
triggerek: Akkor futnak le, miután az esemény (INSERT/UPDATE/DELETE) megtörtént. Azinserted
ésdeleted
virtuális táblákon keresztül férnek hozzá az érintett adatokhoz.INSTEAD OF
triggerek: Az esemény helyett futnak le. Gyakran használják nézetek frissítésére, ahol a mögöttes táblák közvetlenül nem frissíthetők.
CREATE TRIGGER trg_UgyfelModositasNaplozas
ON Ugyfelek
AFTER UPDATE
AS
BEGIN
INSERT INTO UgyfelNaplo (UgyfelID, RegiNev, UjNev, ModositasDatuma)
SELECT
d.UgyfelID,
d.Nev,
i.Nev,
GETDATE()
FROM
deleted d
INNER JOIN
inserted i ON d.UgyfelID = i.UgyfelID
WHERE
d.Nev <> i.Nev; -- Csak ha a név megváltozott
END;
Ez a trigger naplózza az ügyfelek névváltozásait egy 'UgyfelNaplo' táblába. Az inserted
tábla tartalmazza az UPDATE utáni, a deleted
tábla pedig az UPDATE előtti (vagy DELETE esetén a törölt) sorokat.
Nézetek (Views)
A nézetek virtuális táblák, amelyek egy SELECT
utasítás eredményhalmazát reprezentálják. Nem tárolnak fizikailag adatokat, hanem minden lekérdezéskor dinamikusan generálódnak. Fő felhasználási területei:
- Biztonság: Csak a releváns adatokhoz és oszlopokhoz ad hozzáférést a felhasználóknak.
- Adatkomplexitás elrejtése: Egyszerűsíti az összetett lekérdezéseket a végfelhasználók számára.
- Adatfüggetlenség: A mögöttes táblák szerkezetének változásai kevésbé érintik az alkalmazásokat, ha nézeten keresztül férnek hozzá az adatokhoz.
CREATE VIEW AktivUgyfelekRendelesei AS
SELECT
u.UgyfelID,
u.Nev AS UgyfelNev,
r.RendelesID,
r.RendelesDatuma,
r.Osszeg
FROM
Ugyfelek u
INNER JOIN
Rendelesek r ON u.UgyfelID = r.UgyfelID
WHERE
u.Aktiv = 1;
Ez a nézet csak az aktív ügyfelek rendeléseit mutatja meg, elrejtve a háttérben lévő JOIN
logikát.
Közös tábla kifejezések (CTE - Common Table Expressions)
A CTE-k ideiglenes, elnevezett eredményhalmazok, amelyek egyetlen SELECT
, INSERT
, UPDATE
, DELETE
vagy CREATE VIEW
utasítás hatókörén belül definiálhatók. Növelik a lekérdezések olvashatóságát és modularitását, különösen összetett logikák esetén. Két fő típusuk van:
- Nem rekurzív CTE-k: Egyszerűbb, olvashatóbb lekérdezéseket tesznek lehetővé.
- Rekurzív CTE-k: Hierarchikus adatok (pl. szervezeti fa, anyagjegyzék) lekérdezésére használhatók.
WITH UgyfelRendelesOsszesites AS (
SELECT
UgyfelID,
COUNT(RendelesID) AS RendelesekSzama,
SUM(Osszeg) AS TeljesOsszeg
FROM
Rendelesek
GROUP BY
UgyfelID
)
SELECT
u.Nev,
uro.RendelesekSzama,
uro.TeljesOsszeg
FROM
Ugyfelek u
INNER JOIN
UgyfelRendelesOsszesites uro ON u.UgyfelID = uro.UgyfelID
WHERE
uro.TeljesOsszeg > 1000;
Ez a CTE először összegzi az ügyfelenkénti rendeléseket, majd ezt az ideiglenes eredményt felhasználva lekérdezi az ügyfelek nevét és az összesített adatokat.
Ablakfüggvények (Window Functions)
Az ablakfüggvények lehetővé teszik aggregált számítások elvégzését egy sorcsoporton (ablakon) belül, miközben minden egyes sor megőrzi az eredeti részletességét. Rendkívül erőteljesek rangsorolásra, mozgó átlagok számítására és egyéb elemzési feladatokra.
Példák ablakfüggvényekre:
- Rangsoroló függvények:
ROW_NUMBER()
,RANK()
,DENSE_RANK()
,NTILE()
- Analitikai függvények:
LAG()
,LEAD()
,FIRST_VALUE()
,LAST_VALUE()
- Aggregáló függvények ablakban:
SUM() OVER()
,AVG() OVER()
,COUNT() OVER()
,MIN() OVER()
,MAX() OVER()
SELECT
RendelesID,
UgyfelID,
RendelesDatuma,
Osszeg,
SUM(Osszeg) OVER (PARTITION BY UgyfelID ORDER BY RendelesDatuma) AS UgyfelKumulaltOsszeg,
LAG(RendelesDatuma, 1, '1900-01-01') OVER (PARTITION BY UgyfelID ORDER BY RendelesDatuma) AS ElőzőRendelesDatuma
FROM
Rendelesek
ORDER BY
UgyfelID, RendelesDatuma;
Ez a lekérdezés kiszámítja az ügyfelenkénti kumulált rendelési összeget és az előző rendelés dátumát (LAG
) az ablakfüggvények segítségével. Az OVER()
záradék definiálja az ablakot, amelyen a függvény működik.
Dinamikus SQL
A dinamikus SQL olyan T-SQL kód, amelyet futásidőben állítanak össze stringekből, majd végrehajtanak. Ez rugalmasságot biztosít, például amikor a felhasználói bevitel alapján kell lekérdezéseket építeni, vagy változó táblanevekkel kell dolgozni. Azonban biztonsági kockázatot (SQL injekció) és nehezebb hibakeresést is rejt magában.
DECLARE @TableName NVARCHAR(128) = 'Ugyfelek';
DECLARE @SQL NVARCHAR(MAX);
DECLARE @UgyfelID INT = 1;
SET @SQL = N'SELECT * FROM ' + QUOTENAME(@TableName) + ' WHERE UgyfelID = ' + CAST(@UgyfelID AS NVARCHAR(10));
-- Veszélyes: SQL injekcióra hajlamos
-- EXEC(@SQL);
-- Biztonságosabb, paraméterezett lekérdezés a sp_executesql-el
SET @SQL = N'SELECT * FROM ' + QUOTENAME(@TableName) + ' WHERE UgyfelID = @ID';
EXEC sp_executesql @SQL, N'@ID INT', @ID = @UgyfelID;
A QUOTENAME()
függvény segít megvédeni a táblaneveket az SQL injekciótól, az sp_executesql
pedig lehetővé teszi a paraméterek biztonságos átadását. Mindig az sp_executesql
-t kell előnyben részesíteni a sima EXEC()
-kel szemben, ha paramétereket használunk.
Teljesítményoptimalizálás T-SQL-ben
A jól megírt T-SQL kód nem csak funkcionálisan helyes, hanem hatékony is. A teljesítményoptimalizálás kulcsfontosságú a nagy adatbázisok és nagy terhelésű rendszerek esetében.
Indexek szerepe és típusai
Az indexek az adatbázis-táblák speciális keresési struktúrái, amelyek felgyorsítják az adatlekérdezést. Nélkülük az adatbázis-motornak minden sort át kell vizsgálnia (teljes táblavizsgálat), ami rendkívül lassú lehet nagy tábláknál.
- Clustered Index (Klaszterezett index): Meghatározza a tábla sorainak fizikai tárolási sorrendjét. Egy táblának csak egy klaszterezett indexe lehet. Általában a PRIMARY KEY hoz létre klaszterezett indexet.
- Non-clustered Index (Nem klaszterezett index): Különálló struktúra, amely tartalmazza az indexelt oszlopok értékeit és mutatókat a tábla tényleges adatsoraira. Egy táblának több nem klaszterezett indexe is lehet.
- Columnstore Index (Oszlopalapú index): Kifejezetten adattárházakhoz és analitikai lekérdezésekhez optimalizált, oszloporientált adattárolást használ, ami rendkívül hatékony nagy mennyiségű adat aggregálásánál.
-- Nem klaszterezett index létrehozása az Email oszlopon
CREATE NONCLUSTERED INDEX IX_Ugyfelek_Email ON Ugyfelek (Email);
Az indexek megfelelő használata drámaian javíthatja a lekérdezések teljesítményét, de túlzott számuk rontja az INSERT
, UPDATE
, DELETE
műveletek sebességét, mivel az indexeket is frissíteni kell.
Lekérdezéstervezés (Query Plans)
Az SQL Server lekérdezéstervezője (Query Optimizer) felelős azért, hogy meghatározza a leghatékonyabb módot egy T-SQL lekérdezés végrehajtására. A lekérdezéstervek (execution plans) vizuális vagy szöveges formában mutatják be, hogyan hajtja végre az SQL Server a lekérdezést. Elemzésük kulcsfontosságú a teljesítményproblémák azonosításához.
A lekérdezéstervek elemzése elengedhetetlen eszköz a T-SQL teljesítményhangolásban. Megmutatják, hol pazarol az adatbázis-motor időt és erőforrásokat, például hiányzó indexek, rossz
JOIN
-ok vagy felesleges táblavizsgálatok miatt.
A SQL Server Management Studio (SSMS) segítségével könnyen megtekinthetők a lekérdezéstervek (Ctrl+L
vagy 'Display Estimated Execution Plan').
JOIN optimalizálás
A JOIN
utasítások alapvetőek a relációs adatbázisokban, de helytelen használatuk komoly teljesítményproblémákat okozhatnak. Fontos a megfelelő JOIN
típus (INNER JOIN
, LEFT JOIN
, stb.) kiválasztása, és a JOIN
feltételek indexelt oszlopokon való alkalmazása.
- Kerüld a kereszt-
JOIN
-okat (CROSS JOIN
), hacsak nem szándékos. - Használj megfelelő indexeket a
JOIN
kulcsokon. - A
WHERE
záradékokat helyezd el aJOIN
előtt, ha lehetséges, hogy csökkentsd a feldolgozandó sorok számát.
WHERE záradékok hatékonysága
A WHERE
záradékok szűrik az adatokat, és a hatékonyságuk kritikus. A következőket érdemes figyelembe venni:
- Használj kereshető argumentumokat (SARGable). Ez azt jelenti, hogy a
WHERE
záradékban lévő feltételek lehetővé teszik az indexek használatát. Például,WHERE Oszlop = 'érték'
SARGable, deWHERE FUNKCIÓ(Oszlop) = 'érték'
vagyWHERE Oszlop LIKE '%érték%'
általában nem. - Kerüld a nem SARGable függvényeket az indexelt oszlopokon.
- Használd a legspecifikusabb feltételeket először, ha logikailag lehetséges.
Tranzakciók kezelése és zárolások (Locks)
A tranzakciók és az adatbázis-zárolások szorosan összefüggenek. A zárolások biztosítják az adatok integritását a konkurens hozzáférés esetén. A túl hosszú vagy nem megfelelően kezelt tranzakciók azonban zárolási problémákat (deadlock, blocking) okozhatnak, ami rontja a rendszer teljesítményét és válaszkészségét.
- Tartsd a tranzakciókat a lehető legrövidebbre.
- Csak a szükséges adatokat zárold.
- Használd a megfelelő izolációs szintet (pl.
READ COMMITTED
,READ UNCOMMITTED
,SNAPSHOT
). AREAD UNCOMMITTED
(NOLOCK
) használata teljesítményt javíthat, de koszos olvasásokat (dirty reads) eredményezhet.
SET-based vs. Row-by-Row feldolgozás
A T-SQL alapvetően halmaz alapú (set-based) nyelv. Ez azt jelenti, hogy a műveleteket adathalmazokon hajtja végre, nem pedig egyes sorokon. A soronkénti (row-by-row) feldolgozás (pl. kurzorok használatával) általában sokkal lassabb és erőforrás-igényesebb. Mindig törekedj arra, hogy halmaz alapú megoldásokat találj a feladatokra.
-- Halmaz alapú (gyorsabb)
UPDATE Ugyfelek
SET Aktiv = 0
WHERE UtolsoBejelentkezes < DATEADD(year, -1, GETDATE());
-- Soronkénti (lassabb, kerülendő)
DECLARE @UgyfelID INT;
DECLARE UgyfelKurzor CURSOR FOR
SELECT UgyfelID FROM Ugyfelek WHERE UtolsoBejelentkezes < DATEADD(year, -1, GETDATE());
OPEN UgyfelKurzor;
FETCH NEXT FROM UgyfelKurzor INTO @UgyfelID;
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE Ugyfelek SET Aktiv = 0 WHERE UgyfelID = @UgyfelID;
FETCH NEXT FROM UgyfelKurzor INTO @UgyfelID;
END;
CLOSE UgyfelKurzor;
DEALLOCATE UgyfelKurzor;
A fenti példa jól illusztrálja, miért előnyösebb a halmaz alapú megközelítés: rövidebb, olvashatóbb és sokkal hatékonyabb.
Biztonság T-SQL-lel
Az adatbázis-biztonság kritikus fontosságú, és a T-SQL számos eszközt biztosít ennek megvalósításához.
Felhasználók és szerepkörök kezelése
Az SQL Server a biztonságot a felhasználók (logins) és szerepkörök (roles) rendszerén keresztül kezeli. A logins szerver szintű hozzáférést biztosítanak az SQL Serverhez, míg a users adatbázis szintű hozzáférést biztosítanak egy adott adatbázishoz. A szerepkörök (server roles és database roles) jogosultságok gyűjteményei, amelyek megkönnyítik a jogosultságok kezelését nagy számú felhasználó esetén.
-- Szerver login létrehozása
CREATE LOGIN [AlkalmazasLogin] WITH PASSWORD = 'StrongPassword123!', CHECK_POLICY = ON;
-- Adatbázis felhasználó létrehozása és hozzárendelése a login-hoz
CREATE USER [AlkalmazasUser] FOR LOGIN [AlkalmazasLogin];
-- Felhasználó hozzáadása egy adatbázis szerepkörhöz
ALTER ROLE db_datareader ADD MEMBER [AlkalmazasUser];
Javasolt, hogy a felhasználók ne közvetlenül a sysadmin
vagy db_owner
szerepkörök tagjai legyenek, hanem csak a minimálisan szükséges jogosultságokat kapják meg a Principle of Least Privilege (PoLP) elv alapján.
Jogosultságok (GRANT, DENY, REVOKE)
A GRANT
, DENY
és REVOKE
parancsokkal lehet szabályozni az objektumokhoz való hozzáférést és a műveletek végrehajtását. A GRANT
megadja a jogosultságot, a DENY
megtagadja (felülírja a GRANT
-ot), a REVOKE
pedig visszavonja a korábban megadott vagy megtagadott jogosultságot.
-- Jogosultság megadása SELECT műveletre egy táblán
GRANT SELECT ON Ugyfelek TO [AlkalmazasUser];
-- Jogosultság megtagadása DELETE műveletre
DENY DELETE ON Ugyfelek TO [AlkalmazasUser];
-- Jogosultság visszavonása
REVOKE SELECT ON Ugyfelek FROM [AlkalmazasUser];
Ezekkel a parancsokkal nagyon finoman hangolható a hozzáférés-vezérlés az adatbázisban.
SQL injekció elleni védekezés
Az SQL injekció az egyik leggyakoribb és legveszélyesebb támadási forma, amely során a támadó rosszindulatú SQL kódot szúr be a bemeneti mezőkbe, hogy manipulálja az adatbázist. A T-SQL-ben a legfontosabb védekezési mód a paraméterezett lekérdezések használata.
Soha ne fűzd össze a felhasználói bevitelt közvetlenül az SQL lekérdezésekkel. Mindig használj paraméterezett lekérdezéseket vagy az
sp_executesql
eljárást a dinamikus SQL esetén.
-- ROSSZ (SQL injekcióra hajlamos)
-- DECLARE @UgyfelNev NVARCHAR(100) = ''' OR 1=1 --'; -- Rosszindulatú input
-- DECLARE @SQL NVARCHAR(MAX) = 'SELECT * FROM Ugyfelek WHERE Nev = ''' + @UgyfelNev + '''';
-- EXEC(@SQL);
-- JÓ (Paraméterezett lekérdezés)
DECLARE @UgyfelNevParam NVARCHAR(100) = 'Kovács János'; -- Tiszta input
SELECT * FROM Ugyfelek WHERE Nev = @UgyfelNevParam;
-- JÓ (Dinamikus SQL esetén)
DECLARE @DinamikusSQL NVARCHAR(MAX);
DECLARE @KeresettNev NVARCHAR(100) = 'Kovács János';
SET @DinamikusSQL = N'SELECT * FROM Ugyfelek WHERE Nev = @NevParameter';
EXEC sp_executesql @DinamikusSQL, N'@NevParameter NVARCHAR(100)', @NevParameter = @KeresettNev;
A paraméterezett lekérdezések biztosítják, hogy a bemenet adatként, és ne kódként legyen értelmezve, így megelőzve az injekciós támadásokat.
Adatmaszkolás (Data Masking)
A dinamikus adatmaszkolás lehetővé teszi, hogy bizonyos felhasználók számára elrejtsük az érzékeny adatokat, miközben az adatok az adatbázisban változatlanok maradnak. Ez különösen hasznos fejlesztői vagy tesztkörnyezetekben, ahol nem szükséges a valós adatok teljes hozzáférése.
ALTER TABLE Ugyfelek
ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()');
-- Egy felhasználó, aki nem jogosult a maszkolatlan adatok megtekintésére
CREATE USER TesztFelhasznalo WITHOUT LOGIN;
GRANT SELECT ON Ugyfelek TO TesztFelhasznalo;
-- Ha a TesztFelhasznalo lekérdezi az Email oszlopot, maszkolt értéket lát
-- SELECT Email FROM Ugyfelek; -- Eredmény: aXXX@XXXX.com
A maszkolási függvények lehetnek beépítettek (pl. email()
, partial()
, random()
) vagy egyénileg definiáltak.
Transparent Data Encryption (TDE)
A TDE (Transparent Data Encryption) lehetővé teszi a teljes adatbázis titkosítását nyugalmi állapotban (data at rest). Ez azt jelenti, hogy az adatfájlok a lemezen titkosítva vannak, ami extra biztonsági réteget biztosít az adatszivárgás ellen, ha a fizikai adathordozó illetéktelen kezekbe kerül. A TDE implementációja nem igényel alkalmazásmódosítást, transzparens a felhasználók és az alkalmazások számára.
Gyakori hibák és buktatók T-SQL programozásban

Még a tapasztalt fejlesztők is belefuthatnak gyakori T-SQL buktatókba. Ezek elkerülése kulcsfontosságú a robusztus és hatékony rendszerek építéséhez.
- Kurzorok túlzott használata: Ahogy már említettük, a kurzorok soronkénti feldolgozást végeznek, ami nagy adatmennyiségek esetén rendkívül lassú. Szinte mindig létezik halmaz alapú alternatíva.
- Hiányzó vagy rossz indexek: A nem megfelelő indexelés vagy a túl sok index egyaránt ronthatja a teljesítményt. Fontos az indexek gondos tervezése és rendszeres karbantartása.
- Tranzakciókezelési hibák: Elmaradt
COMMIT
vagyROLLBACK
, túl hosszú tranzakciók, vagy nem megfelelő izolációs szintek zárolási problémákat (deadlock, blocking) okozhatnak. - SQL injekcióra hajlamos kód: A felhasználói bemenet közvetlen összefűzése a lekérdezésekkel a legsúlyosabb biztonsági hiba, ami teljes rendszerkompromittáláshoz vezethet.
NULL
értékek helytelen kezelése: ANULL
nem egyenlő a nullával vagy az üres stringgel. Speciális kezelést igényel (pl.IS NULL
,IS NOT NULL
,COALESCE
,ISNULL
), és aNULL
értékekkel való összehasonlítások váratlan eredményeket adhatnak.- Implicit adattípus-konverziók: Ha az SQL Servernek futásidőben kell adattípusokat konvertálnia (pl. stringet számmá), az teljesítménycsökkenést okozhat, és megakadályozhatja az indexek használatát. Mindig használj explicit konverziót (
CAST
,CONVERT
), ha szükséges. SELECT *
használata éles környezetben: ASELECT *
szükségtelen oszlopokat is lekérdezhet, növelve a hálózati forgalmat és a memóriahasználatot. Mindig csak a szükséges oszlopokat kérdezd le.- Skalár értékű függvények használata a
WHERE
záradékban: Ez általában megakadályozza az indexek használatát, mivel az SQL Servernek minden sort ki kell értékelnie a függvényen keresztül.
T-SQL a gyakorlatban: egy példa
Tekintsünk egy egyszerű példát egy képzeletbeli webshop rendszerében, ahol egy ügyfél rendelést ad le. A folyamat magában foglalja az ügyfél ellenőrzését, a rendelés rögzítését és a készlet frissítését, mindezt egy tranzakción belül.
-- Előzetes táblák létrehozása a példához
IF OBJECT_ID('dbo.Termekek', 'U') IS NOT NULL DROP TABLE dbo.Termekek;
IF OBJECT_ID('dbo.Rendelesek', 'U') IS NOT NULL DROP TABLE dbo.Rendelesek;
IF OBJECT_ID('dbo.Ugyfelek', 'U') IS NOT NULL DROP TABLE dbo.Ugyfelek;
CREATE TABLE Ugyfelek (
UgyfelID INT PRIMARY KEY IDENTITY(1,1),
Nev NVARCHAR(100) NOT NULL,
Email NVARCHAR(100) UNIQUE,
Aktiv BIT DEFAULT 1
);
CREATE TABLE Termekek (
TermekID INT PRIMARY KEY IDENTITY(1,1),
Nev NVARCHAR(100) NOT NULL,
Ar DECIMAL(10, 2) NOT NULL,
Keszlet INT NOT NULL DEFAULT 0
);
CREATE TABLE Rendelesek (
RendelesID INT PRIMARY KEY IDENTITY(1,1),
UgyfelID INT FOREIGN KEY REFERENCES Ugyfelek(UgyfelID),
RendelesDatuma DATETIME DEFAULT GETDATE(),
Osszeg DECIMAL(10, 2) NOT NULL,
Statusz NVARCHAR(50) DEFAULT 'Függőben'
);
INSERT INTO Ugyfelek (Nev, Email) VALUES ('Kiss Péter', 'kiss.peter@example.com');
INSERT INTO Termekek (Nev, Ar, Keszlet) VALUES ('Laptop', 1200.00, 5);
INSERT INTO Termekek (Nev, Ar, Keszlet) VALUES ('Egér', 25.00, 20);
GO
-- Tárolt eljárás egy új rendelés feldolgozására
CREATE PROCEDURE RendelesFeldolgozas
@UgyfelEmail NVARCHAR(100),
@TermekID INT,
@Mennyiseg INT,
@RendelesID INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @UgyfelID INT;
DECLARE @TermekAr DECIMAL(10, 2);
DECLARE @AktualisKeszlet INT;
DECLARE @TeljesOsszeg DECIMAL(10, 2);
BEGIN TRANSACTION;
BEGIN TRY
-- 1. Ügyfél azonosítása
SELECT @UgyfelID = UgyfelID FROM Ugyfelek WHERE Email = @UgyfelEmail AND Aktiv = 1;
IF @UgyfelID IS NULL
BEGIN
;THROW 50001, 'Nem létező vagy inaktív ügyfél.', 1;
END
-- 2. Termék adatainak lekérdezése és készlet ellenőrzése
SELECT @TermekAr = Ar, @AktualisKeszlet = Keszlet
FROM Termekek
WHERE TermekID = @TermekID;
IF @TermekAr IS NULL
BEGIN
;THROW 50002, 'Nem létező termék.', 1;
END
IF @AktualisKeszlet < @Mennyiseg
BEGIN
;THROW 50003, 'Nincs elegendő készlet.', 1;
END
-- 3. Készlet csökkentése
UPDATE Termekek
SET Keszlet = Keszlet - @Mennyiseg
WHERE TermekID = @TermekID;
-- 4. Rendelés rögzítése
SET @TeljesOsszeg = @TermekAr * @Mennyiseg;
INSERT INTO Rendelesek (UgyfelID, Osszeg, Statusz)
VALUES (@UgyfelID, @TeljesOsszeg, 'Feldolgozás alatt');
SET @RendelesID = SCOPE_IDENTITY();
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
-- Hiba naplózása (opcionális)
INSERT INTO HibaNaplo (HibaUzenet, HibaDatuma) VALUES (ERROR_MESSAGE(), GETDATE());
;THROW; -- Újradobja a hibát a hívó félnek
END CATCH;
END;
GO
-- Rendelés indítása (sikeres eset)
DECLARE @OutputRendelesID INT;
EXEC RendelesFeldolgozas
@UgyfelEmail = 'kiss.peter@example.com',
@TermekID = 1, -- Laptop
@Mennyiseg = 1,
@RendelesID = @OutputRendelesID OUTPUT;
PRINT 'Sikeres rendelés! Rendelés ID: ' + CAST(@OutputRendelesID AS NVARCHAR(10));
-- Készlet ellenőrzése
SELECT Keszlet FROM Termekek WHERE TermekID = 1;
-- Rendelés ellenőrzése
SELECT * FROM Rendelesek WHERE RendelesID = @OutputRendelesID;
-- Rendelés indítása (sikertelen eset - nincs készlet)
DECLARE @OutputRendelesID_hiba INT;
BEGIN TRY
EXEC RendelesFeldolgozas
@UgyfelEmail = 'kiss.peter@example.com',
@TermekID = 1, -- Laptop
@Mennyiseg = 10, -- Nincs elegendő készlet
@RendelesID = @OutputRendelesID_hiba OUTPUT;
END TRY
BEGIN CATCH
PRINT 'Hiba történt a rendeléskor: ' + ERROR_MESSAGE();
END CATCH;
GO
Ez a kiterjedt példa bemutatja, hogyan használható a T-SQL egy komplex üzleti folyamat kezelésére. Magában foglalja a tárolt eljárásokat, változókat, feltételes logikát, tranzakciókezelést (BEGIN TRANSACTION
, COMMIT
, ROLLBACK
), hibakezelést (TRY...CATCH
, THROW
), adatmanipulációt (SELECT
, UPDATE
, INSERT
), és az OUTPUT
paraméterek használatát. Az egész folyamat atomi, azaz vagy sikeresen befejeződik, vagy az összes változtatás visszavonásra kerül, biztosítva az adatbázis konzisztenciáját.
T-SQL és a modern adatbázis-fejlesztés
A T-SQL nem egy statikus nyelv; folyamatosan fejlődik az SQL Server új verzióival, és alkalmazkodik a modern fejlesztési paradigmákhoz.
Felhőalapú adatbázisok
A Microsoft Azure platformján a Azure SQL Database és Azure Synapse Analytics kínálja az SQL Server képességeit a felhőben. A T-SQL itt is az elsődleges nyelv marad, de kiegészül olyan felhőspecifikus funkciókkal, mint a skálázhatóság, magas rendelkezésre állás és integráció más Azure szolgáltatásokkal. A fejlesztők ugyanazt a T-SQL kódot használhatják a helyi (on-premise) és a felhőalapú környezetekben is.
DevOps és adatbázis verziókezelés
A modern szoftverfejlesztésben a DevOps elvek egyre inkább teret nyernek az adatbázis-fejlesztésben is. Ez magában foglalja az adatbázis-objektumok (táblák, eljárások, függvények) verziókezelését (pl. Git), az automatizált tesztelést és az automatizált telepítést (CI/CD pipeline-ok). A T-SQL szkriptek kulcsfontosságúak ebben a folyamatban, mivel ezek definiálják az adatbázis állapotát és a változásokat.
JSON és XML támogatás
Az SQL Server és ezzel együtt a T-SQL is kiterjedt támogatást nyújt a JSON és XML adatformátumok kezelésére. Ez lehetővé teszi a strukturált és félstrukturált adatok tárolását, lekérdezését és manipulálását közvetlenül az adatbázison belül, ami rendkívül hasznos a webes alkalmazásokkal és API-kkal való integráció során.
-- JSON adatok lekérdezése és feldolgozása
DECLARE @jsonInfo NVARCHAR(MAX) = N'{"Nev": "Kovács Anna", "Kor": 30, "Varos": "Budapest"}';
SELECT
JSON_VALUE(@jsonInfo, '$.Nev') AS Nev,
JSON_VALUE(@jsonInfo, '$.Kor') AS Kor,
JSON_VALUE(@jsonInfo, '$.Varos') AS Varos;
-- JSON tömb konvertálása táblává
DECLARE @jsonArray NVARCHAR(MAX) = N'[{"Termek": "Laptop", "Ar": 1200}, {"Termek": "Egér", "Ar": 25}]';
SELECT Termek, Ar
FROM OPENJSON(@jsonArray)
WITH (
Termek NVARCHAR(100) '$.Termek',
Ar DECIMAL(10,2) '$.Ar'
);
Ezek a funkciók jelentősen megkönnyítik az adatok interoperabilitását és a modern alkalmazások fejlesztését.
A T-SQL tehát sokkal több, mint egy egyszerű adatbázis-lekérdező nyelv. Egy teljes értékű programozási környezetet biztosít a Microsoft SQL Server számára, amely lehetővé teszi a fejlesztők számára, hogy robusztus, biztonságos és hatékony adatbázis-megoldásokat építsenek. Az alapoktól a fejlett funkciókig terjedő ismerete elengedhetetlen mindazok számára, akik sikeresen szeretnének dolgozni az SQL Server ökoszisztémájában.