Základy použití uložených procedur v .NET

Používat či nepoužívat? Mezi příznivci a odpůrci uložených procedur se vede letitá zákopová válka. Jeden z nejaktuálnějších výstřelů najdete například na blogu Franse Boumy. Velká část programátorů (zejména začínajících) ale vůbec neví co uložené procedury (stored procedures) jsou a jak se používají. Právě jim je určen tento článek. Zda je budete využívat nebo ne, už nechávám na vás.

Co je uložená procedura

Uloženou proceduru je možno si představit jako metodu na straně SQL Serveru, nebo jako dávkový příkaz. Sekvenci SQL příkazů je možno si na straně serveru uložit a pojmenovat. Pak ji lze znovu vyvolat pomocí jejího jména.

Používání uložených procedur má své výhody:

  • Kód je do jisté míry nezávislý na struktuře databázových tabulek (při změně tabulek jest změniti jenom patřičné procedury).
  • Jazyk SQL je vhodný na zpracování některých typů úloh.
  • Platí-li předchozí bod, je takové zpracování rychlejší, než kdyby se provádělo na straně SQL klienta (tedy aplikace).

Má ovšem i své nevýhody:

  • Musíte se učit další jazyk, protože tady už si se SELECT, INSERT, UPDATE a DELETE mnohdy nevystačíte.
  • Přesouváním aplikační logiky na databázovou vrstvu se aplikace znepřehledňuje, zejména je-li příliš složitá na to, aby se tam dala přenést veškerá aplikační logika.
  • Používáte-li uložené procedury důsledně na všechno, budete jich mít spoustu. Nepoužíváte-li (někdo se řídí zásadou čtu vždy přímo, měním vždy přes proceduru), přijdete o výhodu nezávislosti na struktuře.
  • S tou rychlostí to neplatí absolutně, protože SQL 2000 si umí cacheovat i ad-hoc execution plan; a i když platí, poznáte to až při velké zátěži nebo opravdu velké databázi.

Moje první uložená procedura

Tradice velí, abych vám ukázal, jak pomocí SP vypsat 'Hello World!'. Půjdu trochu dál, a naučím vás na tomto příkladu, kterak procedury vytvářet, měnit a mazat. Otevřete si SQL Query Analyzer (nebo jakékoliv jiné prostředí, které vám umožní přímo posílat na SQL server příkazy) a založte si nějakou dočasnou databázi. Připojte se k ní a vykonejte následující příkaz:

CREATE PROCEDURE my_hello_world
AS
	SELECT 'Hello, World!'
GO

Po spuštění tohot příkazu se nestane nic viditelného. V hloubi SQL serveru se však stane jedna důležitá věc: vznikne uložená procedura my_hello_world.

Nyní si dovolím malou odbočku k názvům: všechny vestavěné uložené procedury mají název začínající prefixem "sp_". Jejich názvy se píší malými písmeny a jednotlivé slova se oddělují podtržítkem - typickým příkladem může být sp_server_info (vypíše informace o serveru). V zájmu zachování zdravého rozumu doporučuji, abyste pro své uložené procedury používali stejnou logiku vytváření jmen, ale jiný prefix. Obvyklé je "my_" (jako moje v angličtině), ale může to být třeba i zkratka vašeho projektu.

Pokud chcete tuto proceduru vykonat, jednoduše zapište její jméno a spusťte:

my_hello_world

Výsledkem bude resultset o jediném řádku a sloupci, obsahující text Hello, World!

Pokud chcete uloženou proceduru změnit, použijte místo CREATE PROCEDURE příkaz ALTER PROCEDURE:

ALTER PROCEDURE my_hello_world
AS
	SELECT 'Ahoj, světe!'
GO

Pokud chcete proceduru smazat, použijte DROP PROCEDURE:

DROP PROCEDURE my_hello_world

Moje první užitečná procedura

Představme si web, který nabízí možnost zapsat svoji adresu do mailing listu a přihlásit se tak k odběru zpráv. Seznam adres je uložen v SQL tabulce MailingList, která je deklarována takto:

CREATE TABLE MailingList (
	Adresa   varchar(50)  NOT NULL,
	Datum    datetime     NOT NULL,
)

Pokud chceme přidávat do mailing listu adresu, je dobré ověřit, zda v něm již není, aby tam jeden uživatel nebyl zapsán dvakrát. Musíme tedy za sebou vykonat dva příkazy: zjistit, zda se zadaná adresa nachází v mailing listu a pokud ne, přidat ji:

CREATE PROCEDURE my_pridat_adresu
	@Adresa varchar(50)
AS
	SET @Adresa = LOWER(@Adresa)
	IF NOT EXISTS(SELECT * FROM MailingList WHERE Adresa=@Adresa) INSERT INTO MailingList (Adresa, Datum) VALUES (@Adresa, GETDATE())
GO

V tomto okamžiku se na scéně objevuje novinka: SQL parametry - to jsou ty názvy se zavináčem na začátku. Parametr je v podstatě proměnná. V našem případě jejím prostřednictvím předáváme e-mailovou adresu. Pokud budeme chtít naši uloženou proceduru zavolat z VB.NET, učiníme tak takto:

Dim DB As New System.Data.SqlClient.SqlConnection("SERVER=(local);UID=demo;PWD=demo")
Dim CMD As New System.Data.SqlClient.SqlCommand("my_pridat_adresu", DB)
CMD.CommandType = System.Data.CommandType.StoredProcedure
CMD.Parameters.Add("@Adresa", "uzivatel@server.tld")
CMD.ExecuteNonQuery()
DB.Close()

Používáme výstupní parametry

V tomto okamžiku máme zajištěno, že v mailing listu nebude žádná adresa dvakrát - a pokud se někdo pokusí přidat adresu, která tam už je, nic se nestane. Lepší by ovšem bylo, kdybychom se o tomto stavu nějak dozvěděli.

SQL umí používat i výstupní parametry, tedy takové, jimiž nám bude hodnota vrácena zpět. Přidáme tedy ještě parametr @Vysledek, který bude v případě úspěchu obsahovat text '+OK něco' a v případě neúspěchu '-ERR něco' (tuto syntaxi jsem si vypůjčil z POP3 protokolu):

ALTER PROCEDURE my_pridat_adresu
	@Adresa varchar(50),
	@Vysledek varchar(100) output
AS
	SET @Adresa = LOWER(@Adresa)
	IF EXISTS(SELECT * FROM MailingList WHERE Adresa=@Adresa) BEGIN
		SET @Vysledek = '-ERR Adresa se již nachází v mailing listu'
	END ELSE BEGIN
		INSERT INTO MailingList (Adresa, Datum) VALUES (@Adresa, GETDATE())
		SET @Vysledek = '+OK Adresa byla úspěšně přidána do mailing listu'
	END
GO

Z prostředí VB.NET zavoláme proceduru a výsledek získáme nějak takhle:

Dim DB As New System.Data.SqlClient.SqlConnection("SERVER=(local);UID=demo;PWD=demo")
Dim CMD As New System.Data.SqlClient.SqlCommand("my_pridat_adresu", DB)
CMD.CommandType = System.Data.CommandType.StoredProcedure
CMD.Parameters.Add("@Adresa", "uzivatel@server.tld")
CMD.Parameters.Add("@Vysledek", System.Data.SqlDbType.VarChar, 100).Direction = System.Data.ParameterDirection.Output
CMD.ExecuteNonQuery()
DB.Close()
Response.Write("Výsledek: " & CType(CMD.Parameters("@Vysledek").Value, String))

Závěr

Na jednoduchých příkladech jste se naučili, jak vytvářet uložené procedury a volat je z prostředí .NET.

K tomu, aby vám tato dovednost byla v praxi k něčemu dobrá, se ovšem musíte naučit jazyk Transact-SQL. Dobrým zdrojem informací vám budou SQL ServerBooks Online, které jsou součástí instalace SQL Serveru a nebo si je samostatně můžete stáhnout z webu Microsoftu.

Titulek:
Text komentáře:
Vaše jméno:
Váš e-mail: (nebude zveřejněn)

WWW stránka:
Opište text z obrázku:
odpovědětodpovědět Gravatar

RE:Základy použití uložených procedur v .NET

Ja by som este k vyhodam SP zaradil aj fakt, ze su bezpecnejsie(mam na mysli sql injection). Iste, sql injection sa da osetrit aj v kode, ale to opat zvysuje neprehladnost kodu. K nevyhodam este to, ze exucution plan pre proceduru sa robi v momente ked je procedura vytvorena, cize ak sa tabulka vyraznejsie pocas prevadzky systemy zvacsi tak execution plan danej procedury nemusi byt zdaleka idealny. Proceduru preto treba obcas(alebo pravidelne) zmazat a znova vytvorit co zvysuje naroky na udrzbu databazy(to uz vsak nie je problem programatora, ale DB admina :-)).

Osobne pre pomenovanie procedur(a inych objektov) pouzivam odlisne nazvy. Vysvetlim preco: davat prefix "my_ " ma svoje opodstatnenie pretoze viem ze je to moja(uzivatelska) procedura a nie systemova. Co my vadi je, ze neviem ze je to procedura. Moze to byt tiez pohlad alebo iny objekt. Osobne vzdy pouzivam prefix "sp" pre procedury a slova v nazve neoddelujem podtrzitkom, ale nove slovo zacinam velkym pismenom co zaroven skracuje nazov. Procedura sa potom vola nejak takto "spLatestOrders". Z nazvu viem ze je to procedura a od systemovej ju rozpoznam podla toho ze tam nie je podtrzitko.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

IMHO nemají SP na SQL injection žádný vliv. Parametry můžeš stejně dobře používat i v ad-hoc dotazech (SELECT COUNT(*) FROM Uzivatele WHERE Login=@Login AND Password=@Password) a co se týče bezpečnosti proti SQL injection, jsi na tom zcela stejně.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

Ano to mas pravdu. Ale z praktickeho hladiska, aspon podla mojich (i ked oproti tvojim malych) skusenosti, pokial programator nieco pisat nemusi tak to nenapise a potom dotaz vyzera vacsinou takto "SELECT COUNT(*) FROM Uzivatele WHERE Login='"+login.Text+"' AND Password='"+pwd.Text+"'", kdezto ked nieco podobne urobi s SP tak nic nehrozi. Bola o tom obsirnejsia diskusia na EMWAC pred casom. Je to o tom ako si da programator zalezat na tom co robi.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

Jen k tomuto, kazdy by mel psat kod stylem "SELECT * FROM xxx WHERE cosi = " & FormatStringForDB(param) .... atd. Cize SQL injection nehrozi. Kdo to tak nedela, koleduje si.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

SELECT * by se nemelo pouzivat pokud mozno vubec a misto spojovani stringu by se mely pouzivat parametry - pak si FormatStringForDB muzete usetrit.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

SELECT * proc ne? Pokud mam tridu, ktera mi reprezentuje jeden zaznam v tabulce v DB, pak samozrejme SELECT hvezda udelam. Cize pokud napriklad autentifikuju uzivatele, tak udelam SELECT UserID FROM.... a nasledne udelam SELECT * nad tabulkou uzivatelu, protoze o nem potrebuju vedet, jak se jmenuje, jaky ma email, kolikrat uz se zalogoval atp.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

24.1.2005 6:59:2324.1.2005 6:59:23 šťourašťoura ---.251.65.212.contactel.net

Hvězdu, když už použít, tak jedině za předpokladu, že opravdu chcete načíst a POUŽÍT všechny sloupce. Jinak je to plýtvání prostředky, a to jak SQL serveru (kdybyste četl jen některé sloupce, třeba by to pokryl index), tak sítě i klienta.

Lenost programátora vypisovat sloupce se dá "vyřešit" použitím nějakého nástroje, který vám seznam sloupců oddělených čárkami a mezerami z tabulky vygeneruje (umí to Query Analyzer, když si výstup přepnete do textu v CSV formátu, anebo jednoduchý skriptík v téměř libovolném prostředí/jazyku).

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

24.1.2005 10:25:2924.1.2005 10:25:29 CancriCancri 80.188.246.---

Použití * opravňuje pouze ve výrazech typu EXISTS(SELECT * ...) kdy ponecháš na optimizeru vyber pro nej vhodneho sloupece. Jinak vypisovat sloupce VZDY. Ale toto patri mezi zalezitosti, ktere se nedaji pochopit za rok.

Je sakra rozdil mezi programovanim .net a sql. A pouziti vyrazu "zaznam" nekde vyse u komentare je presne o tom. Spravne samozrejme "řádek". Jaký je mezitím rozdíl ať si každý dohledá sám.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

Píše se EXISTS(SELECT 1 FROM ...

a je to nej optimalizator neoptimalizator. :)

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

22.1.2005 13:53:0722.1.2005 13:53:07 PospaPospa ---.aquasoft.cz

Jen jedna poznámka k onomu vytváření exec. plánu u storek. Pokud se nepletu, tak když se přepočítavají statistiky nad tab. a indexama, upravuje se adekvátně i exec. plán SP.

A ještě jedna poznámka. S .NET 2.0, resp s Yukonem bude možnost vytvářet SP přímo v .NETu, resp. C# a pod. I na základě toho říkám SP rozhodně ano.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

"když se přepočítavají statistiky nad tab. a indexama, upravuje se adekvátně i exec. plán SP."

Ano ale predstavte si situaciu ze v SP dynamicky generujete dotaz pomocou exec, alebo tam mate viac prikazov If..Else - cize vam pri kazdom volani procedury moze vzniknut uplne iny dotaz. Napriklad pri prvom volani sa optimalizuje exec. plan pre dotaz SELECT * FROM Customers, procedura sa potom podla statistik bude recompile iba v pripade ze sa vyznamne zmeni tabulka Customers. Avsak pri inom spsusteni procedury moze procedura vykonat prikaz SELECT * FROM Employees, ale exec. plan bude optimalizovany pre dotaz SELECT * FROM Customers.

Nechcem sa vsak hadat, alebo vas o niecom presviedcat. Nie som expert na SQL Server, takze sa rad necham poucit. Ak je to teda inak ako si myslim tak ma prosim vyvedte z omylu.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

Prepacte, tie dva dotazy v predchadzajucom prispevku nie su asi najvhodnejsie a ani z praktickeho zivota. Skor si predstavte ze pri prvom zavolani moze byt dotaz SELECT * FROM Customers WHERE city='NY' AND surname='Blake' a pri inom zavolani moze byt dotaz SELECT * FROM Customers WHERE country='USA'. Plan moze byt pre kazdy dotaz iny. Ale vykona sa vzdy podla prveho. Recompile podla statistik nastane az ked sa vyznamne zmeni tabulka Customers.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

23.1.2005 8:08:1523.1.2005 8:08:15 PepaPepa ---.fnmotol.cz

Jen bych chtěl podotknout k tomu pojmenování procedur. Jestli se nepletu, tak procedury pojmenované sp_ se vždy nejdříve hledají v databázi master bez ohledu na to jaká je zrovna aktuální, takže je rozumné pojmenovávat procedury jinak.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

A preco to pisete ako odpoved na moj komentar? Ved v mojom komentari predsa uvadzam ze ich pomenuvavam inak.

odpovědětodpovědět Gravatar

RE: RE:Základy použití uložených procedur v .NET

Ono strcit SQL primo do kodu taky neni uplne idelani, protoze kdyz budu chtit neco na tom SQL zmenit, tak musim kompilovat projekt. Kdyz to budu mit v SP, tak to staci zmenit tam a vsechno jede jak ma.

odpovědětodpovědět Gravatar

Článek nic moc ...

22.1.2005 20:14:3322.1.2005 20:14:33 Daniel SteigerwaldDaniel Steigerwald ---.8.broadband3.iol.cz

Čau, čekal jsem trochu víc, než že stvoříš článek z odkazu který sem ti poslal mailem + recykluješ svůj starší článek. Mohl jsi například vycházet z diskuze pod ním, kde se hovořilo, že mezi parametrizovanými dotazy a uloženými procedurami není zas takový výkonostní rozdíl.

IMHO ukládat text chybové odpovědi do uložené procedury smrdí business logikou, která tam napatří. Na předání výsledku je return value.

Já osobně od uložených procedur upouštím, a používám je jen tam kde to je vhodné. Snažím se programovat komponenty a ty si řeší přístup do databáze sami (dle nastavení z web.config nebo jinde), kdyby byly závislé na procedurách, přidělával bych si práci.

Jaký starší článek? Já jsem už někdy něco psal o uložených procedurách?

Aha, už jsem to našel, já jsem to už jednou psal v seriálu o Cheetahu! No, aspoň že vykrádám jenom sám sebe :)

Jinak ten původní článek najdete na http://archive.aspnetwork.cz/art/clanek.asp?id=256

Aha, tak tohle nepočítám. Jednak je to čtyři roky staré (takže už si to nikdo nepřečte pokud to explicitně nehledá), druhak jsem to nepsal já a treťjak je to bohapustá teorie.

odpovědětodpovědět Gravatar

Už nikdy nenapíšu nic o uložených procedurách

Už nikdy nenapíšu nic o uložených procedurách. Už nikdy nenapíšu nic o uložených procedurách. Už nikdy nenapíšu nic o uložených procedurách. Už nikdy nenapíšu nic o uložených procedurách. Už nikdy nenapíšu nic o uložených procedurách. Už nikdy nenapíšu nic o uložených procedurách. Už nikdy nenapíšu nic o uložených procedurách. Už nikdy nenapíšu nic o uložených procedurách. Už nikdy nenapíšu nic o uložených procedurách. Už nikdy nenapíšu nic o uložených procedurách.

odpovědětodpovědět Gravatar

Díky moc za článek

15.2.2011 11:57:0815.2.2011 11:57:08 RadekRadek 77.104.220.---

Ahoj, dneska jsem poprvé otevřel VS2008 a potřeboval jsem napsat programek, který zavolá SQL procku a vezme z ní data. 1000x děkuji autorovi za tento článek. Předtím jsem hledal hodinu v různých anglických zdrojích, ale tohle je super. A funguje to. Díky!!!

  • Altairis
  • Nemesis
  • Microsoft MVP
  • IIS
  • ASP.NET