A Transact SQL programozási nyelv

Alprogramok, blokkok

Blokkok

A begin end párossal lehet blokkot definiálni. BEGIN { sql_statement | statement_block } END

Tárolt eljárások

Létrehozás parancsa:
CREATE PROCEDURE enev (@e1 [=ert1] tip1 [OUT],..) WITH ENCRYPTION | EXECUTE AS user AS BEGIN …. RETURN ertek; END;


create procedure p1 (@v1 int, @v2 int out) as begin select @v2 = count(*) from tabla1 where kod < @v1; end; begin declare @m1 int; execute dbo.p1 3, @m1 OUT; select @m1; end;

Függvények

A felhasználói függvény olyan Transact SQL utasítások sorozata, amelyeket azért csomagolunk egybe, hogy több helyen is felhasználhassuk. Nagyon jól kiegészítik a tárolt eljárásokat, mert minden olyan helyen felhasználhatjuk őket, ahol a beépített függvényeket is, azaz ahol a tárolt eljárásokat legtöbbször nem. A legegyszerűbb példa erre a SELECT-ben való felhasználás. Például, ha van egy osszeadas nevű függvényünk, akkor azt felhasználhatjuk két oszlopban található számok összeadására, a SELECT utasítás részeként. Ennél kevésbé kézenfekvő helyeken is használhatjuk a függvényeinket: WHERE feltételben, HAVING-ben, CHECK CONSTRAINT-ekben, DEFAULT CONSTRAINT-ekben, számított oszlopok képzésében. Mindenhol működnek, ahol a szerver valamilyen kifejezést vár (mint a>b, c=4 vagy 2x2=5).
CREATE FUNCTION osszeadas ( @a INT, @b INT ) RETURNS INT BEGIN RETURN @a + @b END SELECT osszeadas(Ár, ÁFA), Termék FROM ...
Az SQL Server a függvényeket sokszor tranzakciók, illetve SELECT, UPDATE, satöbbi utasítások kellős közepén hívja meg. Emiatt rendetlen az a függvény, ami menet közben módosítja egy tábla tartalmát, miközben egy SELECT (ami őt hívta meg) éppen dolgozik rajta - nos ilyen esetben nagy gond támadhatna. Az SQL Server ezért megpróbálja megkötni a kezüket. Azaz a felhasználói függvényekben nem tehetünk meg akármit, csak a következőket: Látható, hogy minden pontban arról van szó, hogy megtehetünk szinte bármit, amit csak akarunk, de csak lokálisan, azaz a függvény nem avatkozhat be a külvilágba. Bár az utolsó pont, azaz, hogy külső tárolt eljárásokat is meghívhatunk, azért egy nagyon tág fogalom. Megteheti azt, hogy visszafelé nyit egy kapcsolatot a kiszolgálóra, és azon keresztül megváltoztatja azt a táblát, amiben éppen dolgozik a kódunk a függvény hívása során. De ez általában már túlmutat a normális használaton. Megtehették volna a fejlesztők, hogy teljesen letiltják a külső eljáráshívásokat, de akkor meg elestünk volna olyan lehetőségektől, mint külső parancsok meghívása (xp_cmdshell), levélküldés (xp_sendmail) vagy event log írás (xp_logevent) (és még sok egyéb hasznos funkció). Az imént felsorolt három külső tárolt eljárás azonban pont olyan, aminek nem szabadna lefutni egy függvényben. Miért? Azért mert egy függvény nem változtathatja meg globálisan a rendszer állapotát. A rendszeren nem csak az SQL Server belső lelkivilágát értjük, hanem az egész világot. Így például az xp_cmdshell segítségével akár le is formázhatjuk kollégánk merevlemezét. Azaz ezeket a külső tárolt eljárásokat nem szabadna meghívni egy felhasználói függvényből, amire nyomatékosan fel is hívja a figyelmet a dokumentáció (Books Online). Vannak még más problémás elemek is, amelyeket bizonyos esetekben szintén nem szabad használni függvényekben. Ezek a nemdeterminisztikus függvények. Ők a függvények azon fajtái, amelyeknek a működése vagy az általa visszaadott érték időben vagy a szerver állapotától függően nem megjósolható módon változik. Azaz ugyanazokkal a paraméterekkel meghívva egyszer a-t mond, másszor b-t. A legegyszerűbb példa erre a GetDate() beépített függvény, ami a pillanatnyi időt adja vissza. Ez minden egyes meghívás pillanatában más értéket ad vissza. A fordítóprogram nem engedi meg, hogy ilyen nemdeterminisztikus beépített függvényeket helyezzük el a saját függvényeinkben. Például a következő függvény törzsre: RETURN RAND(10) a fordító az „Invalid use of 'rand' within a function.” hibaüzenettel válaszol. Miért ilyen problémás pont a determinizmus kérdése az SQL Serverben? Azért, mert vannak benne olyan új szolgáltatások, amelyek nem tudnának helyesen működni a „bizonytalan” nemdeterminisztikus függvényekkel. Két helyen nem lehet felhasználni a nemdeterminisztikus függvényeket: Mindkét esetben indexet építünk táblában található adatokra. Az SQL Server nem tud indextáblát építeni olyan adatokra, amelyek minden pillanatban változnak. A clustered index az adatok fizikai sorrendjét határozza meg. Ezen a héten így legyek sorban az adatok, a következő héten meg másképp, csak azért, mert meggondolta magát a transzformáló függvény? Nem is tehetünk ilyet.

A séma-kötés

A felhasználói függvények igen erősen kötődnek azokhoz a táblákhoz, és egyéb objektumokhoz, amelyekre hivatkoznak. Ha azok módosulnak anélkül, hogy erről a függvény tudna, akkor a kapcsolatuk vége barátságtalan lesz, és a függvény nem fog jól működni. Azért, hogy a jó viszonyban ne következhessen be szakadás, a függvény létrehozásakor (CREATE FUNCTION) megadhatjuk, hogy a függvény legyen hozzákötve az általa használt objektumokhoz. Ezt az SQL Server megjegyzi, és nem engedi módosítani vagy törölni az ily módon leláncolt objektumokat. A kötés jelzését a RETURNS és a függvény törzsét kezdő BEGIN közé kell írni:
... RETURNS ... WITH SCHEMABINDING BEGIN ...

Függvénytípusok

Háromféle felhasználói függvénytípust hozhatunk létre:

Skaláris függvények

A skaláris függvények nagyon egyszerűek: kapnak néhány paramétert, azokon végeznek valamilyen műveletet, majd az eredményt egy skaláris értékként visszaadják. Leginkább a procedurális nyelvek függvényeihez hasonlítanak. A CREATE FUNCTION jelzi, hogy ez egy felhasználói függvény lesz. Ezután jön a függvény neve. Általában a függvényeknek vannak paramétereik, ezeket zárójelben soroljuk fel a függvény neve után. A @ nem opcionális, Transact SQL-ben minden változót kötelező @-al kezdeni. A paraméter neve után meg kell adni a típusát. Itt majdnem az összes, a kiszolgáló által támogatott adattípust fel lehet használni, egy-két (image, text vagy cursor) típust kivéve. A RETURNS után kell definiálni a visszatérési érték adattípusát. A kötöttségek ugyanazok, mint a paramétereknél. A függvény törzsét, ahol a funkcionalitást írjuk le, a BEGIN és END kulcsszavak közé kell elhelyezni. A skaláris visszatérési értékű függvényekre minimum 2 tagú névvel kell hivatkozni. Azaz legalább a függvény tulajdonosát meg kell adnunk ahhoz, hogy az SQL Server felismerje a függvényünket.
SELECT dbo.osszeadas(1,4) vagy SELECT Northwind.dbo.osszeadas(1,4)

Egy utasításból álló, tábla visszatérési értékű függvények

CREATE FUNCTION OrdersByCustomer( @CustomerID varchar(5)) RETURNS TABLE AS RETURN ( SELECT * FROM Orders WHERE CustomerID = @CustomerID) SELECT CustomerID, ShippedDate FROM OrdersByCustomer('THEBI')

Több utasításból álló, tábla visszatérési értékű függvények

Bonyolultabb esetben a visszatérési érték nem állítható elő egyetlen SELECT utasítás segítségével, ilyenkor kell használnunk ezt a függvénytípust. Mivel ilyenkor már nem egyértelmű, hogy melyik lekérdezés kimenetét szeretnénk visszaadni, explicit deklarálnunk kell a visszatérési értékként szolgáló tábla szerkezetét egy tábla típusú változóként. A változót INSERT utasítások segítségével feltöltjük (akárhány lépésben), és a RETURN utasítás ezt fogja visszaadni a hívónak.
Create function func1 (@p1 int) returns @ret table(col1 int, col2 nvarchar(100)) begin insert into @ret(col1, col2) values (@p1, ‘a’); return; end Select * from dbo.func1(4)