č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.