úterý 30. září 2014

Optimalizace komunikace s DB - Kurzory

Komunikaci mezi aplikací a databázovým serverem je možné do značné míry optimalizovat. Technologie FireDAC zpřístupňuje množství parametrů, které upravují způsob, jakým se požadavek zaslaný databázovému serveru bude zpracovávat.
Z hlediska odezvy jsou velmi důležitá nastavení týkající se vytvoření datové sady na serveru a její následný přenos na klientskou stanici.

Volba typu DB kurzoru

FireDAC umožňuje volbu typu kurzoru prostřednictvím parametru "CursorKind".
Parametr "CursorKind" má dopad na:
Čas potřebný pro načtení prvního záznamu výsledkové sady
Čas potřebný pro načtení kompletní výsledkové sady
Možnost současného otevření více kurzorů
Stabilitu (neměnnost) kurzoru
Nároky na systémové prostředky databázového stroje

FireDAC FetchOptions

Dostupné možnosti nastavení jsou:

  • ckAutomatic - Typ kurzoru je vybrán automaticky na základě nastavení ostatních parametrů. 
  • ckDefault - Obsahuje záznamy, které odpovídaly dotazu v okamžiku jeho spuštění. Načtení prvního záznamu může být pomalejší, protože se na klienta přesouvá celá výsledková sada. Celkově je ale rychlejší. 
  • ckDynamic - Dynamický serverový kursor reflektuje změny způsobené aktualizacemi, které proběhly po dobu kdy je kurzor aktivní. Načtení prvního záznamu je rychlé, získání celé výsledkové sady může být pomalejší. 
  • ckStatic - Statický serverový kurzor obsahuje záznamy, které odpovídaly dotazu v okamžiku jeho spuštění.
  • ckForwardOnly - Jednosměrný serverový kurzor. Uvolňuje již použité záznamy, takže šetří paměť. Parametr "Unidirectional" musí být "True".



Příklad Delphi

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Nastavení typu kurzoru na úrovni připojení
  FDConnection.Connected := False;
  FDConnection.FetchOptions.CursorKind := ckStatic;
  FDConnection.Connected := True;
end;

Příklad C++ Builder

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  // Nastavení typu kurzoru na úrovni komponenty FDQuery
  FDQuery->Active = False;
  FDQuery->FetchOptions->CursorKind = ckAutomatic;
  FDQuery->Active = True;
}

Způsob práce s kurzorem

Parametr "Unidirectional" určuje, zda se lze v kurzoru pohybovat oběma směry, tedy dopředu i zpět. Standardně je má parametr nastavenu hodnotu "False", kdy je povolen i zpětný pohyb. Pokud je nastaven na "True", šetří se systémové zdroje (již zpracované záznamy jsou uvolněny z paměti), ale například při použití komponenty "DBGrid" vyvolá přesun na předchozí záznam chybové hlášení.

Chyba kurzoru

Uvolnění kurzoru

Databázový stroj udržuje kurzor dokud není datová sada uzavřena, nebo není uvolněn objekt, který je jejím správcem. Parametr "AutoClose", pokud je nastaven na "True", uvolní kurzor ihned po načtení posledního záznamu.

Pozor! Jestliže příkaz vrací více datových sad, musí být tento parametr nastaven na "False", jinak dojde k uzavření kurzoru po načtení všech záznamů první datové sady a další sady již načteny nebudou!

pátek 13. června 2014

LiveBindings IV - Pokročilejší nastavení II

Uživatelské formátování (Custom Format)

Dalším požadavkem, se kterým se můžeme setkat při propojování vizuálních komponent a dat je potřeba zobrazení dat v určitém požadovaném tvaru. Může se jednat například o prezentaci telefonních či směrovacích čísel, peněžních částek, položek typu datum a podobně.

LiveBindings Methods a Output Converters 

Uvnitř LiveBindings výrazů lze s předávanými parametry pracovat za pomoci dvou skupin vestavěných funkcí. Jedna (Output Converters) sdružuje funkce pro převod datových typů, druhá (Methods) pak především funkce pro zpracování a úpravu předávaných dat.

LiveBindings Konverzní funkce & metody

LiveBindings nabízí řadu vestavěných funkcí. Pokud by funkce kolidovala s funkcí či metodou používanou dotčeným objektem, lze ji deaktivovat. Dialogy pro aktivaci/deaktivaci funkcí a převodníků lze otevřít z "Inspektora Objektů". Na formuláři musí být umístěna a vybrána komponenta "BindingList".

Příklady formátování výstupu

Mějme jednoduchou aplikaci, která bude zobrazovat data z databáze. Struktura databázové tabulky je následující:

CREATE TABLE LB_DEMO2
(
  ID Integer NOT NULL,
  ZBOZI1 Varchar(20),
  ZBOZI2 Varchar(20),
  CENA Numeric(7,2),
  DATUM Date DEFAULT current_date,
  CONSTRAINT PK_LB2 PRIMARY KEY (ID)
);

Formulář pak může vypadat zhruba takto:

Návrh formuláře

Formátování řetězců

Pokud bychom potřebovali běžné formátovací funkce jako je například převod na malá nebo velká písmena, stačí otevřít "Object Inspector" a do "Custom Format" zapsat příslušný předpis s využitím příslušné funkce LiveBindings.

Object Inspector

Výraz "%s" odkazuje na zpracovávaný řetězec v původním tvaru, tak jak byl přijat od zdrojové komponenty.


V praxi může vyvstat potřeba zpracování více než jednoho řetězce. Například budeme požadovat sloučení polí "ZBOZI1" a "ZBOZI2" z naší tabulky a zobrazení výsledného řetězce v komponentě "Label".
Protože LiveBindings engine standardně umožňuje definovat pro vazbu pouze jeden "zdrojový" a jeden "cílový" objekt, musíme použít drobnou lest. V "BindingsList" vytvoříme nový "BindLink". Jako zdrojovou komponentu vybereme "BindSourceDB". Ta reprezentuje datovou sadu (tedy nadřízený objekt), v které jsou obě databázová pole definována. Tím získáme přístup k metodám, které budeme pro manipulaci s daty potřebovat. Nyní již stačí jen zapsat výraz pro sloučení získaných řetězců, např.:

UpperCase(self.FieldByName('ZBOZI1').Text)  + " " +
self.FieldByName('ZBOZI2').Text

BindingsList

"Self" odkazuje na zdrojový objekt a zpřístupňuje tak všechna data ze zdrojového objektu.


Formátování číselných hodnot

Pro formátování čísel můžeme použít dva přístupy. Pokud je zdrojem dat databáze, lze způsob zobrazení určit přímo pro daný sloupec. V okně "Structure" si zobrazíme pro datovou sadu (v naší aplikaci komponenta "Table") všechny sloupce.

Okno "Structure"

Následně označíme sloupec "CENA", pro který hodláme změnit formátování a v okně "Object Inspector" odpovídajícím způsobem nastavíme vlastnost "DisplayFormat". Zde např. "### ###.00".

Nastavení "DisplayFormat"

Stejného výsledku dosáhneme, pokud podobně jako u formátování řetězců nastavíme v okně "Object Inspector" vlastnost "CustomFormat". Formátování nelze nastavit přímo, ale za pomoci funkce "Format()". Pro zobrazení s přesností na dvě desetinná místa tedy například "Format('%%.2f', value)".


Úplný přehled argumentů funkce "Format()" je uveden v Embarcadero docwiki.


Pokud má být spolu s číslem zobrazen další symbol (procenta, měna, apod.), stačí pouze připojit patřičný string. V případě procent je třeba znak uvádět zdvojeně.

Format('%%.2f', value) + ' %%'
Format('%%.2f', value) + ' Kč'


Protože LiveBindings pracuje s ObjectPascalem i C++, lze v předpisu pro formátování použít jak jednoduché tak dvojité apostrofy. Akceptován tak bude zápis Format('%%.2f', value) i Format("%%.2f", value).

Datum a čas

Stejným způsobem je možné formátovat i položky typu datum či čas. Pouze místo funkce "Format()" je třeba použít funkci "FormatDateTime". Zápis formátování pro úplné zobrazení pro položku "DATUM" tak může vypadat následovně:

FormatDateTime('dd/mm/yyyy hh:nn:ss AM/PM', value)

Finální zobrazení

úterý 3. června 2014

LiveBindings III - Custom Parse

V minulých příspěvcích jsem popisoval vizuální návrh propojení za pomoci LiveBindings Designeru nebo průvodce "LiveBindings Wizard". V praxi se však můžeme setkat s požadavky, které vizuálním návrhem nelze jednoduše realizovat.

Uživatelské zpracování (Custom Parse)

Custom Parse umožňuje řešit situace, kdy je třeba data předávaná mezi objekty upravit do formy, kterou je cílový objekt schopen akceptovat. Typicky se jedná o převody mezi různými datovými typy, nebo o úpravu dat do podoby odpovídající definované masce. S tímto požadavkem se můžeme často setkat například při návrhu databázových aplikací v prostředí FireMonkey.

Příklad:

Vytvořme si jednoduchou FireMonkey aplikaci, která bude sloužit k editaci záznamů databázové tabulky s následující strukturou:
CREATE TABLE LBDEMO ( KONTAKT_ID Integer NOT NULL, PRIJMENI Varchar(50), ZEME Char(2), AKTIVNI Integer, CONSTRAINT PK_KONTAKT_ID PRIMARY KEY (KONTAKT_ID) );
Nejprve si v Delphi nebo C++ Builderu navrhneme formulář pro editaci dat (viz obrázek níže). Pole "AKTIVNÍ", které je v databázi reprezentováno jako integer, bude ve formuláři zobrazeno za pomoci komponenty "CheckBox".

Formulář aplikace

Pro propojení jednotlivých komponent se zdrojem dat použijeme LiveBindings Designer (viz následující diagram).

Nastavení LiveBindings

Pokud takový projekt přeložíme, a pokusíme se změnit hodnotu zaškrtnutím nebo naopak odškrtnutím "CheckBoxu", bude zobrazena chyba. Automatická konverze převede typ boolean na string (defaultní datový typ pro všechny výrazy LiveBindings), který databázový stroj očekávající integer nedokáže zpracovat.

Chyba konverze

Řešením je zmiňovaná uživatelská konfigurace propojení. Nejprve v "LiveBindings Designeru" odstraníme nevyhovující vazbu (na symbol vazby klikneme pravým tlačítkem myši) a zvolíme příkaz "Remove Link".

Odstranění vazby

Následně otevřeme "LiveBindings Wizard" a definujeme nový "BindLink". Alternativně jej můžeme vytvořit pomocí dvojkliku na komponentě "BindingsList" (otevře se okno pro editaci LiveBindings propojení), kde z nabídky vybereme volbu "New Binding => BindLink".
V okně "Object Inspektor" nastavíme jako zdrojovou komponentu datový zdroj, v tomto případě "BindSourceDB1". Vlastnost "SourceMemberName" umožňuje vybrat požadovaný sloupec tabulky, zde sloupec s názvem "AKTIVNI". Nakonec určíme komponentu pro zobrazení dat "ControlComponent". Bude jím komponenta "CheckBox1". 

Konfigurace BindLink

Nyní je třeba doplnit výraz "ParseExpressions" pro uložení dat zpět do databáze. To můžeme provést rovněž prostřednictvím "Inspektora Objektů", nebo lépe v okně pro editaci propojení. Výhodou je možnost okamžité validace definovaných výrazů.

Okno BindingsList

V seznamu propojení klikneme dvakrát na v předchozím kroku vytvořený BindLink a do zatím prázdné kolekce přidáme výraz pro "Parse":

Přidání nového výrazu

Do pole pro "Control Expression" vložíme výraz "IfThen(Self.IsChecked, '1', '0')", který vyhodnotí stav komponenty "CheckBox1". Pokud bude zaškrtnuta (vlastnost "IsChecked" bude mít hodnotu "True"), bude výsledkem výrazu text "1", v opačném případě pak text "0". Do pole pro "Source Expression" napíšeme pouze "Text". Datový zdroj je tak informován o tom, že data obdrží jako string a do požadovaného typu (v tomto případě Integer) si je musí převést.

Úprava výrazů

Vyhodnocení vytvořeného výrazu si lze ověřit za pomoci tlačítek "Eval Control" a "Eval Source". Tlačítka "Assign to Control" a "Assign to Source" pak testují vlastní přiřazení hodnoty.

Validace pomocí "Eval Control"

Nyní můžeme aplikaci přeložit a ověřit si, že nyní již LiveBindings hodnotu zadanou prostřednictvím komponenty "CheckBox" interpretují správně a editované záznamy budou korektně uloženy do databáze.


čtvrtek 8. května 2014

Vývoj databázových aplikací VI

Práce s DataSety na straně klienta

Výsledková sada přenesená na klienta může být v řadě případů příliš rozsáhlá, aby se v ní uživatel jednoduše orientoval. FireDAC podobně jako DB Express nebo jeho předchůdce BDE nabízí funkce, které umožňují data třídit, filtrovat je dle určených kritérií nebo v nich vyhledávat.

FireDAC nabízí dvě varianty, jak výše uvedené funkce implementovat. Programátor může využít přímo metody třídy DataSet (či jejích potomků), nebo se i na klientské straně spolehnout na jazyk SQL.

DataSet - Třídění záznamů

Záznamy lze třídit podle jednoho nebo více sloupců, které byly přidány do pojmenovaného "Indexu". Indexů může být definováno více a následně mohou být aktivovány dle potřeby. Záznamy jsou setříděny podle určených sloupců. Pokud není uvedeno jinak, jsou záznamy třízeny vzestupně s ohledem na malá a velká písmena.


Příklad Delphi - Třídění dle indexu

procedure TForm1.SortClick(Sender: TObject);
begin
  // Definování indexu
  FDTable1.Indexes.Clear;
  FDTable1.Indexes.Add();
  FDTable1.Indexes.Items[0].Name := 'idxPrijmeni';
  // Záznamy budou setříděny nejprve podle příjmení a
  // potom podle jména
  FDTable1.Indexes.Items[0].Fields := 'PRIJMENI; JMENO';
  // Sloupec jména bude se bude třídit sestupně
  FDTable1.Indexes.Items[0].DescFields := 'JMENO';
  // Sloupec příjmení se bude třídit bez ohledu na velká
  // a malá písmena
  FDTable1.Indexes.Items[0].CaseInsFields := 'PRIJMENI';
  FDTable1.Indexes.Items[0].Active := True;
  FDTable1.Indexes.Items[0].Selected := True;
end;


Příklad C++ Builder

void __fastcall TForm1::SortClick(TObject *Sender)
{
  FDQuery1->Indexes->Clear();
  FDQuery1->Indexes->Add();
  FDQuery1->Indexes->Items[0]->Name = "idxPrijmeni";
  FDQuery1->Indexes->Items[0]->Fields = "PRIJMENI; JMENO";
  FDQuery1->Indexes->Items[0]->DescFields = "JMENO";
  FDQuery1->Indexes->Items[0]->CaseInsFields = "PRIJMENI";
  FDQuery1->Indexes->Items[0]->Active = True;
  FDQuery1->Indexes->Items[0]->Selected = True;
}

DataSet - Filtrování záznamů

Množinu zobrazovaných záznamů lze omezit použitím vhodného filtru. Filtr může obsahovat logické operátory, operátory LIKE, IN nebo zástupné znaky.


Příklady Delphi

// Osoby s příjmením začínajícím na 'Fa'
procedure TForm1.Filter1Click(Sender: TObject);
begin
  FDTable1.Filtered := False;
  FDTable1.Filter := 'PRIJMENI LIKE ' + QuotedStr('Fa%');
  FDTable1.Filtered := True;
end;

// Výběr výčtem
procedure TForm1.Filter2Click(Sender: TObject);
begin
  FDTable1.Filtered := False;
  FDTable1.Filter := 'OSOBA_ID < 10 AND OSOBA_ID NOT IN (5, 6, 8)';
  FDTable1.Filtered := True;
end;


Příklad C++ Builder

// Filtrování podle rozpětí (osoba_id od 5 do 12)
void __fastcall TForm1::Filter1Click(TObject *Sender)
{
  FDQuery1->IndexFieldNames = "osoba_id";
  FDQuery1->SetRangeStart();
  FDQuery1->KeyExclusive = False;
  FDQuery1->KeyFieldCount = 1;
  FDQuery1->FieldByName("osoba_id")->AsInteger = 5;
  FDQuery1->SetRangeEnd();
  FDQuery1->KeyExclusive = False;
  FDQuery1->KeyFieldCount = 1;
  FDQuery1->FieldByName("osoba_id")->AsInteger = 12;
  FDQuery1->ApplyRange();
}

DataSet -Vyhledání záznamu

Pro vyhledávání v datové sadě je možné použít více přístupů. Nejvíce možností však nabízí funkce "LocateEx". Pokud jsou pro datovou sadu definovány indexy, budou pro vyhledávání použity.


Příklad Delphi

// Vyhledání záznamu pomocí LocateEx
procedure TForm1.FindClick(Sender: TObject);
var
  lxo: TFDDataSetLocateOptions;
begin
  lxo := [lxoPartialKey,lxoCaseInsensitive, lxoFromCurrent];
  //Nalezne postupně první a následující výskyty
  if not FDTable1.LocateEx('PRIJMENI', 'mal', lxo) then
    ShowMessage('Hledaný záznam nebyl nalezen.');
end;



Příklad C++ Builder

void __fastcall TForm1::FindClick(TObject *Sender)
{
  TFDDataSetLocateOptions lxo;
  lxo << lxoPartialKey;
  lxo << lxoCaseInsensitive;
  lxo << lxoFromCurrent;
  if (FDQuery1->LocateEx("Prijmeni", Edit1->Text, lxo) == False)
 ShowMessage("Záznam nenalezen.");
}

Pro Search Options lze použít přepínače:
lxoCaseInsensitive - Hodnoty jsou vyhledány bez ohledu na použití malých nebo velkých písmen
lxoPartialKey - Vyhledávací podmínce budou odpovídat záznamy obsahující hledaný klíč
lxoFromCurrent - Další vyhledávání bude pokračovat od aktuálního záznamu
lxoCheckOnly - Je pouze ověřena existence záznamu splňujícího vyhledávací podmínky. Nedojde ke změně aktuálního záznamu ani spuštění s ní spojených událostí. DataSet zůstává v edit modu
lxoBackward - Vyhledávání bude probíhat od posledního záznamu
lxoNoFetchAll - Pokud je načtena pouze část záznamů, proběhne vyhledání pouze v nich. Nedojde k vynucenému načtení všech záznamů.

Lokální SQL

FireDAC podporuje používání jazyka SQL při práci s datovými sadami na klientské straně. Tuto vlastnost zajišťuje komponenta "FDLocalSQL", která jako databázový engine využívá SQLite. Kromě třídění, filtrování či vyhledávání dat lze za pomoci lokálních SQL dotazů řešit například také požadavky na:  

Heterogenní dotazy - Dotazy, které získávají data z různých databází
In-memory zpracování - Jako datové sady lze použít "TFDMemTable"
Offline zpracování dat - Aplikace pracuje s lokální kopií dat
Migrace dat - Přenesení dat mezi různými databázovými stroji


Příklad Delphi - Filtrování a setřídění záznamů


Vytvoříme aplikaci s komponentami "FDConnection1" připojenou na zvolenou DB a "FDTable1", která získá ze serveru požadovaná data. Do projektu přidáme komponenty "FDConnection2", "FDQuery1, "FDLocalSQL1", "DBGrid1" a "DataSource1".
procedure TForm1.Button1Click(Sender: TObject);
begin
  // Získání DataSetu z DB stroje
  FDConnection1.ConnectionDefName := 'FBDEMODB';
  FDTable1.Connection := FDConnection1;
  FDTable1.TableName := 'OSOBA';
  // Nastavení LocalSQL
  FDConnection2.DriverName := 'SQLite';
  FDLocalSQL1.Connection := FDConnection2;
  FDLocalSQL1.SchemaName := 'Local';
  // DataSety lze přidávat dle potřeby, přičemž mohou být
  // získány z různých DB strojů
  FDLocalSQL1.DataSets.Add(FDTable1, 'Local', 'Osoba');
  FDLocalSQL1.Active := True;
  // Realizace dotazu nad lokálním DataSetem
  FDQuery1.Connection := FDConnection2;
  FDQuery1.Open('select prijmeni, jmeno from osoba where jmeno like' +
  QuotedStr('Fra%') + 'order by prijmeni');
  // Zobrazení dat
  DataSource1.DataSet := FDQuery1;
  DBGrid1.DataSource := DataSource1;
end;



Příklad C++ Builder - heterogenní dotaz



Nastavení komponenty FDLocalSQL

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  // DataSet MS SQL Server
  FDConnection1->ConnectionDefName = "MSDEMODB";
  FDTable1->Connection = FDConnection1;
  FDTable1->TableName = "OSOBA";
  // DataSet Firebird
  FDConnection2->ConnectionDefName = "FBDEMODB";
  FDTable2->Connection = FDConnection2;
  FDTable2->TableName = "FIRMA";
  // Nastavení FDLocalSQL
  FDConnection3->DriverName = "SQLite";
  FDLocalSQL1->Connection = FDConnection3;
  FDLocalSQL1->SchemaName = "Local";
  FDLocalSQL1->DataSets->Add(FDTable1, "Local", "Osoba");
  FDLocalSQL1->DataSets->Add(FDTable2, "Local", "Firma");
  // Realizace dotazu nad heterogenními daty
  FDLocalSQL1->Active = True;
  FDQuery1->Connection = FDConnection3;
  FDQuery1->Open("select f.nazev, o.jmeno, o.prijmeni from Osoba o, Firma f where o.firma_id = f.firma_id");
  DataSource1->DataSet = FDQuery1;
  DBGrid1->DataSource = DataSource1;
}

Pro každý databázový stroj včetně SQLite musí aplikace obsahovat příslušnou knihovnu ovladače. V našem případě tedy třídy TFDPhysMSSQLDriverLink, TFDPhysFBDriverLink a TFDPhysSQLiteDriverLink.

středa 23. dubna 2014

Vývoj databázových aplikací V

Uložené procedury

Uložená procedura (Stored Procedure) je podobně jako tabulka nebo pohled databázový objekt, definovaný uvnitř databáze. Místo vlastních dat však obsahují kód (aplikační logiku). Uložené procedury mohou používat téměř libovolné dotazy (DQL) nebo příkazy pro manipulaci s daty (DML). Jejich použití je vhodné zvláště tam, kde by realizace úlohy na klientské straně znamenala zbytečný přenos většího objemu dat ze serveru a následně zpět na server.

Na rozdíl od příkazů DDL pro návrh struktury dat, jejichž převod lze snadno provést pomocí CASE nástrojů, jsou uložené procedury podobně jako spouště (triggery), funkce nebo balíčky obtížněji přenositelné. Dodavatelé aplikací, kteří musí podporovat databázové servery různých dodavatelů, se tak použití uložených procedur často vyhýbají. Bohužel se tak zároveň vzdávají řady výhod, které použití uložených procedur přináší:

Zpřehlednění systému

Uložené procedury zjednodušují a zpřehledňují architekturu systému.
- Změny lze provádět na serveru, aniž by tím byla dotčena aplikace
- Aplikační logiku lze konzistentně sdílet více moduly nebo aplikacemi
- Uložené procedury jsou validovány serverem a mají transparentní vazby
- Uložené procedury se snadno dokumentují
- Návrh uložených procedur lze do velké míry automatizovat  

Sdílení aplikační logiky

Zlepšení odezvy aplikace

Uložené procedury jsou předkompilované a typicky načtené v cache paměti DB serveru. V porovnání se standardním příkazem tak již není nutné ověřovat jejich syntaktickou správnost, provádět kontrolu existence dotčených databázových objektů nebo validovat správnost vazeb. Rovněž odpadá proces optimalizace, kdy se databázový server na základě statistických informací o jednotlivých objektech snaží navrhnout ideální exekuční plán a samozřejmě také vlastní kompilace. Provedení uložené procedury je tak zpravidla výrazně rychlejší než přímé spuštění identické sady příkazů.

Zpracování uložené procedury


Při větších změnách v DB, které by mohli mít dopad na stanovení optimálního exekučního plánu je doporučeno aktualizovat statistiky a provést rekompilaci všech uložených procedur.


Bezpečnost

Použití uložených procedur je mnohem bezpečnější než přímý přístup k datům. Pro každou proceduru je možné přesně stanovit, který uživatel nebo která role má právo ji spustit. Uložená procedura chrání systém před útoky typu SQL injection.

Řízení oprávnění


Uložené procedury je také možné využít ke skrytí aplikační logiky. Po zkompilování uložené procedury lze ze serveru odstranit zdrojový kód (typicky uložený v systémových tabulkách v textové podobě).

Produktivita

V současné době je pro většinu databázových serverů k dispozici široká nabídka nástrojů a utilit, které návrh uložených procedur zjednodušují a automatizují.

Generování uložených procedur

Pro volání uložených procedur databázových strojů Interbase a Firebird, které vracející datovou sadu nelze použít příkaz ExecProc. Místo toho stačí uloženou proceduru nastavit na "Active". Alternativně lze získat data z procedury s využitím FDQuery. Ve vlastnosti "SQL.text" pak provedeme standardní "select", například 'select * from SEL_TEST'.


Příklady Delphi (InterBase/Firebird)

// Načtení záznamů
procedure TForm1.selClick(Sender: TObject);
begin
  FDStoredProc1.Active := False;
  FDStoredProc1.StoredProcName := 'SEL_TEST';
  FDStoredProc1.Prepare();
  FDStoredProc1.Active := True;
end;

// Vložení nového záznamu
procedure TForm1.insClick(Sender: TObject);
begin
  FDStoredProc1.Active := False;
  FDStoredProc1.StoredProcName := 'INS_TEST';
  FDStoredProc1.Prepare();
  FDStoredProc1.ParamByName('IN_TXT').AsString := Edit1.Text;
  // Zobrazení výstupního parametru
  // v tomto případě ID vloženého záznamu
  ShowMessage(FDStoredProc1.ParamByName('RET').AsString);
  FDStoredProc1.ExecProc();
end;

// Aktualizace záznamu
procedure TForm1.updClick(Sender: TObject);
begin
  FDStoredProc1.Active := False;
  FDStoredProc1.StoredProcName := 'UPD_TEST';
  FDStoredProc1.Prepare();
  FDStoredProc1.ParamByName('IN_TXT').AsString := Edit1.Text;
  FDStoredProc1.ParamByName('IN_ID').AsInteger := Edit2.Text;
  FDStoredProc1.ExecProc();
end;

// Odebrání záznamu
procedure TForm1.delClick(Sender: TObject);
begin
  FDStoredProc1.Active := False;
  FDStoredProc1.StoredProcName := 'DEL_TEST';
  FDStoredProc1.Prepare();
  FDStoredProc1.Params.ParamByPosition(1).AsInteger := StrToInt(Edit2.Text);
  FDStoredProc1.ExecProc();
end;


Příklady C++ Builder (Microsoft SQL Server)

// Načtení záznamů
void __fastcall TForm1::selClick(TObject *Sender)
{
  FDStoredProc1->Active = False;
  FDStoredProc1->StoredProcName = "dbo.up_sel_edice";
  FDStoredProc1->Prepare();
  FDStoredProc1->ParamByName("@eid")->AsInteger = StrToInt(Edit1->Text);
  FDStoredProc1->Active = True;
}

// Vložení nového záznamu
void __fastcall TForm1::insClick(TObject *Sender)
{
  FDStoredProc1->Active = False;
  FDStoredProc1->StoredProcName = "dbo.up_ins_edice";
  FDStoredProc1->Prepare();
  FDStoredProc1->ParamByName("@edice")->AsString = Edit2->Text;
  FDStoredProc1->ExecProc();
}

// Aktualizace záznamu
void __fastcall TForm1::updClick(TObject *Sender)
{
  FDStoredProc1->Active = False;
  FDStoredProc1->StoredProcName = "dbo.up_upd_edice";
  FDStoredProc1->Prepare();
  FDStoredProc1->ParamByName("@eid")->AsInteger = StrToInt(Edit1->Text);
  FDStoredProc1->ParamByName("@edice")->AsString = Edit2->Text;
  FDStoredProc1->ExecProc();
}

// Odebrání záznamu
void __fastcall TForm1::delClick(TObject *Sender)
{
  FDStoredProc1->Active = False;
  FDStoredProc1->StoredProcName = "dbo.up_del_edice";
  FDStoredProc1->Prepare();
  FDStoredProc1->Params->ParamByPosition(2)->AsInteger = StrToInt(Edit1->Text);
  FDStoredProc1->ExecProc();
}

Uložené procedury (a další SQL příkazy) lze spouštět na základě událostí na straně serveru, nebo v požadovaných časech. Vhodné např. pro reporting nebo validaci a čištění dat.