16. Práce s tabulkami

V této kapitole popíšeme, jak používat ve svých databázových aplikacích komponentu datové množiny TTable. Komponenta tabulky zaobaluje úplnou strukturu a data v připojené databázové tabulce. Komponenta tabulky dědí mnoho svých základních vlastností a metod od TDataSet, TBDEDataSet a TDBDataSet. S těmito komponentami jsme se již seznámili.
S komponentou tabulky se seznámíme v těchto bodech:

Používání komponenty tabulky

Komponenta tabulky nám dává přístup ke každému řádku a sloupci v připojené databázové tabulce, která je v Paradoxu, dBASE, Accessu, FoxPro, ODBC databáze nebo SQL databáze na vzdáleném serveru, jako je InterBase, Sybase nebo SQL Server.
Můžeme editovat a prohlížet data v každém sloupci a řádku tabulky. Můžeme pracovat z rozsahem řádků v tabulce a můžeme filtrovat záznamy k získání podmnožiny řádků tabulky splňující zadané filtrovací kritérium. Můžeme vyhledávat záznamy, kopírovat, přejmenovávat a rušit celé tabulky a vytvářet vztah Master-detail mezi tabulkami.
Poznámka: Komponenta tabulky se vždy odkazuje na jednu databázovou tabulku. Pokud potřebujeme v jedné komponentě přistupovat k více tabulkám, nebo pokud nás zajímá podmnožina řádků a sloupců z jedné nebo více tabulek, pak místo komponenty tabulky můžeme použít komponentu dotazu.

Nastavování komponenty tabulky

Následující kroky jsou obecné instrukce pro nastavování komponenty tabulky při návrhu. Pro přizpůsobení vlastností podle požadavku naší aplikace mohou být požadovány další kroky. K vytvoření komponenty tabulky:
  1. Umístíme komponentu tabulky ze stránky Data Access Palety komponent do Datového modulu nebo na formulář a nastavíme její vlastnost Name na unikátní hodnotu v naší aplikaci.
  2. Nastavíme DatabaseName komponenty na jméno databáze, ke které budeme přistupovat.
  3. Nastavíme vlastnost TableName na jméno tabulky v databázi. Pokud vlastnost DatabaseName je již specifikována, pak jméno tabulky lze vybrat v rozbalovacím seznamu.
  4. Umístíme komponentu Datového zdroje do Datového modulu nebo na formulář a nastavíme její vlastnost DataSet na jméno komponenty tabulky. Komponenta Datového zdroje je použita k předání výsledkové množiny z tabulky do Datových ovladačů k zobrazení.
Pro zpřístupnění dat zaobalených komponentou tabulky:
  1. Umístíme komponentu Datového zdroje ze stránky Data Access Palety komponent do Datového modulu nebo na formulář a nastavíme její vlastnost DataSet na jméno komponenty tabulky.
  2. Umístíme Datové ovladače, jako je TDBGrid na formulář a nastavíme vlastnost DataSource ovladače na jméno komponenty Datového zdroje umístěné v předchozím kroku.
  3. Nastavíme vlastnost Active komponenty tabulky na true.
V tomto bodě se budeme zabývat:
Specifikování umístění tabulky
Vlastnost DatabaseName specifikuje, kde komponenta tabulky hledá databázovou tabulku. Pro Paradox a dBASE, DatabaseName může být přezdívka BDE nebo adresářová cesta. Pro tabulky SQL, DatabaseName musí být přezdívka BDE.
Výhodou používání přezdívek BDE ve všech případech je to, že můžeme změnit Datový zdroj pro celou aplikaci jednoduchou změnou definice přezdívky v Průzkumníku SQL.
K nastavení vlastnosti DatabaseName:
  1. Nastavíme vlastnost Active na false (pokud již není nastaveno).
  2. Specifikujeme přezdívku BDE nebo adresářovou cestu ve vlastnosti DatabaseName.
Tip: Pokud naše aplikace používá komponentu databáze k řízení databázových transakcí, pak DatabaseName může být nastaveno na lokální přezdívku definovanou pro komponentu databáze.
Specifikování jména tabulky
Vlastnost TableName specifikuje zpřístupňovanou tabulku databáze v komponentě tabulky. Pro specifikaci tabulky provedeme tyto kroky:
  1. Nastavíme vlastnost tabulky Active na false (pokud již není nastaveno).
  2. Nastavíme vlastnost DatabaseName na přezdívku BDE nebo adresářovou cestu.
  3. Nastavíme vlastnost TableName na zpřístupňovanou tabulku. Při návrhu můžeme volit z přípustných jmen v rozbalovacím seznamu vlastnosti TableName v Inspektoru objektů. Za běhu musíme specifikovat přípustnou hodnotu v kódu.
Po specifikaci přípustného jména tabulky můžeme nastavit vlastnost komponenty Active na true pro připojení k databázi, otevření tabulky a zobrazení a editaci dat.
Za běhu, můžeme nastavit nebo změnit přiřazenou tabulku ke komponentě tabulky: Např. následující kód mění jméno tabulky pro komponentu tabulky OrderOrCustTable na základě jejího současného jména tabulky:
OrderOrCustTable->Active = false; // Uzavření tabulky
if (OrderOrCustTable->TableName == "CUSTOMER.DB")
  OrderOrCustTable->TableName = "ORDERS.DB";
else
  OrderOrCustTable->TableName = "CUSTOMER.DB";
OrderOrCustTable->Active = true; // Opětovné otevření nové tabulky
Specifikování typu tabulky pro lokální tabulky
Pokud aplikace přistupuje k tabulkám Paradoxu, dBASE, FoxPro nebo textovým tabulkám ASCII, pak BDE používá vlastnost TableType k určení typu tabulky (její očekávané struktury). TableType není používáno, když aplikace přistupuje k SQL tabulce na databázovém serveru.
Implicitně TableType je nastaveno na ttDefault. Když TableType je ttDefault, pak BDE určuje typ tabulky podle přípony souboru tabulky. Následující tabulka sumarizuje přípony jmen souborů rozeznávaných BDE a určení typu tabulky:
 
Přípona Typ tabulky
Soubor bez přípony Paradox.DB
.DB Paradox
.DBF dBASE
.TXT ASCII text

Pokud naše lokální tabulky používají přípony souborů podle předchozí tabulky, pak můžeme nastavit TableName na ttDefault. Jinak, naše aplikace musí nastavit TableType k indikaci správného typu tabulky (podle následující tabulky).
 

Hodnota TableType Typ tabulky
ttDefault Typ je určen automaticky BDE
ttParadox Paradox
ttDBase dBASE
ttFoxPro FoxPro
ttASCII ASCII text
Otevírání a uzavírání tabulky
Pro zobrazování a editování dat tabulky v Datových ovladačích jako je TDBGrid, tabulku otevřeme. Jsou dva způsoby otevírání tabulek. Můžeme nastavit její vlastnost Active na true nebo můžeme volat metodu Open. Otevřením tabulku uvedeme do stavu dsBrowse a všechny aktivní ovladače přiřazené k Datovému zdroji tabulky zobrazí data.
Při ukončování zobrazování a editace dat nebo ke změně základních vlastností komponenty tabulky (např. DatabaseName, TableName a TableType), nejprve odešleme nebo zrušíme zatím neprovedené změny. Pokud jsou povoleny odložené aktualizace, pak voláme ApplyUpdates k zápisu neprovedených změn do databáze. Nakonec tabulku uzavřeme.
Jsou dva způsoby uzavření tabulky. Můžeme nastavit její vlastnost Active na false nebo můžeme volat metodu Close. Uzavřením tabulky uvedeme tabulku do stavu dsInactive. Aktivní ovladače přiřazené k Datovému zdroji tabulky jsou vyprázdněny.

Řízení čtecího/zápisového přístupu k tabulce

Implicitně, když tabulka je otevřena, je požadován čtecí a zápisový přístup k připojené databázové tabulce. V závislosti na charakteristikách připojené databázové tabulky, požadované zápisové privilegium nemusí být přiděleno (např. když je požadován přístup k SQL tabulce na vzdáleném serveru a server omezí přístup k tabulce pouze na čtení).
Jsou tři vlastnosti komponenty tabulky, které ovlivňují čtecí a zápisový přístup k tabulce: CanModify, ReadOnly a Exclusive.
CanModify je vlastnost, která určuje zda komponenta tabulky má čtecí a zápisový přístup do připojené databázové tabulky. Po otevření tabulky za běhu, naše aplikace může testovat CanModify k zjištění zda tabulka má zápisový přístup. Pokud CanModify je false, pak aplikace nemůže zapisovat do databáze. Když CanModify je true, pak naše aplikace může do databáze zapisovat (pokud ReadOnly tabulky je false).
ReadOnly určuje, zda uživatel může editovat data. Když ReadOnly je false (implicitně), pak uživatel může prohlížet i editovat data. K omezení uživatele na prohlížení dat, nastavíme ReadOnly na true před otevřením tabulky.
Exclusive řídí zda aplikace získá výhradní přístup k tabulce Paradoxu, dBASE nebo FoxPro. K získání výhradního přístupu pro tyto typy tabulek nastavíme vlastnost Exclusive komponenty tabulky na true před otevřením tabulky. Po úspěšném otevření tabulky s výhradním přístupem, ostatní aplikace nemohou číst nebo zapisovat data tabulky. Požadavek na výhradní přístup není akceptován, pokud při pokusu o otevření tabulky, je tabulka již používána.
Následující příkazy otevírají tabulku pro výhradní přístup:
CustomersTable->Exclusive = true; // Nastavení požadavku na výhradní přístup
CustomersTable->Active = true;    // Otevření tabulky
Poznámka: Můžeme se pokusit nastavit Exclusive pro SQL tabulku, ale některé servery nemají podporu úrovně výlučného uzamčení tabulky. Jiné mohou přidělit výlučný zámek, ale umožní ostatním aplikacím číst data z tabulky.

Vyhledávání záznamů

Záznamy v tabulce lze vyhledávat různými způsoby. Nejpružnější a preferovaný způsob vyhledávání záznamu, je použití obecných metod Locate a Lookup. Tyto metody umožňují prohledávat libovolný typ položky v libovolné tabulce a to bez ohledu na to, zda jsou indexované nebo klíčované.
Locate hledá první řádek vyhovující specifikovaným kritériím a přesouvá kurzor na tento řádek. Lookup vrací hodnoty z prvního řádku vyhovujícího specifikovaným kritériím, ale nepřesouvá kurzor na tento řádek.
Locate a Lookup můžeme použít s libovolným typem Datové množiny (nejen s TTable). Úplný popis najdeme v kapitole Seznámení s datovými množinami.
Komponenty tabulek také podporují metody Goto a Find. Tyto metody jsou již zastaralé a je vhodné místo nich používat ve svých aplikacích Lookup a Locate. Pokud chceme zvýšit výkonnost existujících aplikací (které je používají), pak aplikaci můžeme převést na používání nových metod.
S vyhledávání záznamů se seznámíme v těchto bodech:
Vyhledávání záznamů na základě indexovaných položek
Komponenty tabulek podporují množinu vyhledávacích metod Goto pro zachování zpětné kompatibility. Metody Goto umožňují vyhledání záznamu na základě indexovaných položek a udělání prvního nalezeného záznamu novým současným záznamem.
Pro tabulky Paradoxu a dBASE, klíč musí být vždy indexem, který můžeme specifikovat ve vlastnosti IndexName komponenty tabulky. Pro SQL tabulky, klíč může být také seznam položek specifikovaných ve vlastnosti IndexFieldNames. Můžeme také specifikovat položkový seznam pro tabulky Paradoxu a dBASE, ale položky musí mít na sobě definované indexy.
Tip: K vyhledávání na neindexovaných položkách tabulky Paradoxu nebo dBASE, použijeme Locate. Alternativně, můžeme k hledání na neindexovaných položkách v tabulkách Paradoxu a dBASE použít komponentu TQuery a příkaz SELECT.
Následující tabulka uvádí šest metod svázaných s Goto a Find, které aplikace může použít k vyhledávání záznamů:
 
Metoda Popis
EditKey Chrání současný obsah vyrovnávací paměti vyhledávacího klíče a uvádí tabulku do stavu dsSetKey, aby aplikace mohla modifikovat existující vyhledávací kritéria před provedením vyhledávání.
FindKey Kombinuje metody SetKey a GotoKey do jedné metody.
FindNearest Kombinuje metody SetKey a GotoNearest do jedné metody.
GotoKey Hledá první záznam v datové množině, který přesně odpovídá vyhledávacímu kritériu a přesouvá kurzor na tento záznam, pokud je nalezen.
GotoNearest Vyhledávání na řetězcové položce na neúplnou shodu hodnoty klíče a přesouvá kurzor na tento záznam.
SetKey Vyprazdňuje vyrovnávací paměť vyhledávacího klíče a uvádí tabulku do stavu dsSetKey, aby aplikace mohla specifikovat nová vyhledávací kritéria před provedením vyhledávání.

GotoKey a FindKey jsou logické funkce, které v případě úspěchu, přesouvají kurzor na nalezený záznam a vracejí true. Pokud hledání je neúspěšné, pak kurzor není přesunut a funkce vrací false.
GotoNearest a FindNearest vždy mění pozici kurzoru, a to na první přesně odpovídající záznam (je-li nalezen) nebo na první záznam, který je větší než specifikovaná vyhledávací kritéria.

Provedení vyhledávání metodou Goto
K provedení vyhledávání pomocí metody Goto musíme provést tyto obecné kroky:

  1. V případě potřeby ve vlastnosti IndexName specifikujeme index použitý při vyhledávání. Pro SQL tabulky seznam položek použitých jako klíč nalezneme v IndexFieldNames. Pokud použijeme primární index tabulky není nutno nastavovat tyto vlastnosti.
  2. Otevřeme tabulku.
  3. Pomocí SetKey uvedeme tabulku do stavu dsSetKey.
  4. Ve vlastnosti Fields specifikujeme hodnotu (hodnoty) k vyhledávání. Fields je seznam řetězců, který je indexován pořadovými čísly odpovídající sloupcům (jsou číslovány od 0).
  5. Pomocí GotoKey nebo GotoNearest hledáme a přesuneme se na první vyhovující záznam.
Např. následující kód, spojený s obsluhou stisku tlačítka, přemístí kurzor na první záznam obsahující hodnotu položky, která se přesně shoduje s textem v editačním ovladači na formuláři:
void __fastcall TSearchDemo::SearchExactClick(TObject *Sender)
{
  Table1->SetKey();
  Table1->Fields[0]->AsString = Edit1->Text;
  if (!Table1->GotoKey())
    ShowMessage("Záznam nenalezen");
}
GotoNearest je podobné. Vyhledává nejbližší shodu v částečné hodnotě položky. Může být použita pouze na řetězcových položkách. Např.
Table1->SetKey();
Table1->Fields[0]->AsString = "Sm";
Table1->GotoNearest();
Pokud existuje záznam s "Sm" jako prvními dvěmi znaky, pak kurzor je umístěn na tento záznam. Jinak, pozice kurzoru se nemění a GotoNearest vrací false.

Provedení vyhledávání metodou Find
K provedení vyhledávání metodou Find provedeme následující kroky:

  1. V případě potřeby ve vlastnosti IndexName specifikujeme index použitý při vyhledávání. Pro SQL tabulky seznam položek použitých jako klíč nalezneme v IndexFieldNames. Pokud použijeme primární index tabulky není nutno tyto vlastnosti nastavovat.
  2. Otevřeme tabulku.
  3. Pomocí FindKey nebo FindNearest hledáme a přesuneme se na první nebo nejbližší záznam. Obě metody přebírají jeden parametr (čárkami oddělený seznam hodnot položky), kde každá hodnota odpovídá indexovaným sloupcům v připojené tabulce.
Poznámka: FindNearest můžeme používat pouze na řetězcové položky.
Specifikování současného záznamu po úspěšném vyhledávání
Implicitně, úspěšné vyhledávání umisťuje kurzor na první záznam vyhovující vyhledávacím kritériím. Pokud požadujeme, můžeme nastavit vlastnost tabulky KeyExclusive na true k umístění kurzoru na následující záznam za prvním vyhovujícím záznamem. Implicitně, KeyExclusive je false, což znamená, že kurzor je umístěn na první vyhovující záznam.
Vyhledávání na částečných klíčích
Pokud tabulka má více než jeden sloupec klíče a chceme vyhledávat hodnoty na podmnožině těchto klíčů, pak nastavíme KeyFieldCount na počet sloupců, které budeme prohledávat. Např. pokud tabulka má třísloupcový primární klíč a chceme vyhledávat hodnoty pouze v prvním sloupci, pak nastavíme KeyFieldCount na 1.
Pro tabulky s vícesloupcovými klíči, můžeme vyhledávat hodnoty pouze v sousedních sloupcích klíče, počínaje od prvního. Např. pro třísloupcový klíč můžeme vyhledávat hodnoty v prvním sloupci, v prvním a druhém sloupci nebo ve všech třech sloupcích, ale ne např. v prvním a třetím sloupci.
Vyhledávání na alternativních indexech
Pokud chceme vyhledávat na jiných indexech než na primárním klíči tabulky, pak musíme specifikovat jméno použitého indexu ve vlastnosti IndexName tabulky. Tabulka musí být uzavřena při specifikování hodnoty pro IndexName. Např. pokud tabulka CUSTOMER má sekundární index nazvaný CityIndex, na kterém chceme vyhledávat hodnoty, pak musíme nastavit hodnotu vlastnosti IndexName tabulky na "CityIndex":
Customer->Close();
Customer->IndexName = "CityIndex";
Customer->Open();
Customer->SetKey();
Customer->FieldValues["City"] = Edit1->Text;
Customer->GotoNearest();
Místo specifikace jména indexu, můžeme ve vlastnosti IndexFieldNames uvést seznam používaných položek jako klíč. Pro tabulky Paradoxu a dBASE, položky seznamu musí být indexovány nebo při provádění hledání je generována výjimka. Pro SQL tabulky, položky v seznamu nemusí být indexovány.
Pokaždé, když voláme SetKey nebo FindKey, pak jsou vyprázdněny všechny předchozí hodnoty ve vlastnosti Fields. Pokud chceme zopakovat vyhledávání pomocí dříve nastavených položek, nebo chceme něco přidat k položkám použitým k vyhledávání, pak voláme EditKey místo SetKey a FindKey. Např. pokud index CityIndex obsahuje položky City i Country, pak k nalezení záznamu se specifikovaným jménem firmy ve specifikovaným městě použijeme následující kód:
Customer->EditKey();
Customer->FieldValues["Country"] = Variant(Edit2->Text);
Customer->GotoNearest();

Řazení záznamů

Index určuje zobrazovací pořadí záznamů v tabulce. Obecně, záznamy jsou zobrazovány ve vzestupném pořadí primárního indexu (pro tabulky dBASE bez primárního indexu, pořadí je založeno na fyzickém pořadí záznamu). Toto implicitní pořadí nevyžaduje žádnou intervenci aplikace. Pokud požadujeme jiné pořadí, pak musíme specifikovat: Specifikace jiného řadícího pořadí vyžaduje následující kroky:
  1. Určení dostupných indexů.
  2. Specifikování alternativního indexu nebo seznamu sloupců k používání.
V této části se budeme zabývat:
Získávání seznamu dostupných sloupců pomocí GetIndexNames
Za běhu, naše aplikace může volat metodu GetIndexNames k získání seznamu dostupných indexů pro tabulku. GetIndexNames vrací seznam řetězců obsahující přípustná jména indexů. Např. následující kód určuje seznam indexů dostupných Datovou množinou CustomerTable:
TStringList *IndexList = new TStringList();
CustomersTable->GetIndexNames(IndexList);
Poznámka: Pro tabulky Paradoxu, primární index je nepojmenovaný a tedy není vracen GetIndexNames. Pokud se potřebujeme vrátit k používání primárního indexu tabulky Paradoxu po používání alternativního indexu, nastavíme vlastnost IndexName tabulky na prázdný řetězec.
Specifikace alternativního indexu pomocí IndexName
Pro specifikaci, že tabulka bude řazena použitím alternativního indexu, specifikujeme jméno indexu ve vlastnosti IndexName komponenty tabulky. Během návrhu můžeme specifikovat toto jméno v Inspektoru objektů a za běhu můžeme k vlastnosti přistupovat našim kódem. Např. následující kód nastaví index pro CustomersTable na CustDescending:
CustomersTable->IndexName = "CustDescending";
Pro tabulky dBASE, které používají ne-vytvářené indexy, nastavíme vlastnost IndexFiles na jméno používaného indexového souboru před nastavením IndexName. Během návrhu můžeme kliknout na tlačítko se třemi tečkami u vlastnosti IndexFiles v Inspektoru objektů k vyvolání Editoru indexových souborů. Uvidíme seznam dostupných indexových souborů, zvolíme Add a vybereme jeden nebo více indexových souborů ze seznamu. Indexový soubor dBASE může obsahovat vícenásobné indexy. K výběru indexu z indexového souboru, vybereme jméno indexu z rozbalovacího seznamu IndexName v Inspektoru objektů. Můžeme také specifikovat více indexů v souboru zadáním požadovaných jmen indexů (oddělovaných středníky).
Můžeme také nastavit IndexFiles a IndexName za běhu. Např. následující kód nastavuje IndexFiles pro tabulku AnimalsTable na ANIMALS.MDX a pak nastavuje IndexName na NAME:
AnimalsTable->IndexFiles->Add("ANIMALS.MDX");
AnimalsTable->IndexName = "NAME";
Specifikace řadícího pořadí pro tabulky SQL
V SQL řadící pořadí řádků je určeno klauzulí ORDER BY. Můžeme specifikovat index použitý touto klauzulí pomocí: IndexName a IndexFieldNames se vzájemně vylučují. Nastavením jedné vlastnosti zrušíme nastavenou hodnotu v druhé vlastnosti.
IndexFieldNames je vlastnost seznamu řetězců. Ke specifikaci řadícího pořadí, zapíšeme seznam všech jmen sloupců použitých k určení řazení do vlastnosti a jména oddělujeme středníky. Řazení je pouze ve vzestupném pořadí. Rozlišování velikosti písmen při řazení závisí na schopnostech našeho serveru. Více informací nalezneme v dokumentaci serveru.
Následující kód nastavuje řadící pořadí pro PhoneTable na LastName a potom na FirstName:
PhoneTable->IndexFieldNames = "LastName;FirstName";
Poznámka: Pokud použijeme IndexFieldNames pro tabulky Paradoxu a dBASE, pak C++ Builder se pokusí nalézt index používající námi specifikovaný sloupec. Pokud index není nalezen, pak je generována výjimka.
Zkoumání seznamu položek na indexy
Když naše aplikace používá za běhu indexy, pak může testovat: IndexFields je seznam řetězců obsahující jména sloupců pro index. Následující kód ukazuje jak můžeme použít IndexFieldCount a IndexFields k procházení seznamem jmen sloupců v aplikaci:
AnsiString ListOfIndexFields[20];
for (int i = 0; i < CustomersTable->IndexFieldCount; i++)
  ListOfIndexFields[i] = CustomersTable->IndexFields[i]->FieldName;
Poznámka: IndexFieldCount není přípustný pro tabulky otevřené na výrazových indexech.

Práce s podmnožinou dat

Vytvořené tabulky mohou být rozsáhlé a aplikace může požadovat omezení počtu řádků se kterými pracuje. Pro komponenty tabulek jsou dvě možnosti omezení záznamů používaných aplikací: rozsahy a filtry. Filtry mohou být používány s libovolným typem datových množin, včetně komponent TTable, TQuery a TStoredProc. Protože jsou aplikovatelné na všechny datové množiny, najdeme jejich úplný popis v Seznámení s datovými množinami.
Rozsahy jsou aplikovatelné pouze na komponenty TTable. I když se podobají, rozsahy a filtry mají různé použití. V této části jsou následující body:
Seznámení s rozdíly mezi rozsahy a filtry
Rozsahy i filtry omezují viditelnost záznamů v tabulce na podmnožinu všech dostupných záznamů, ale způsob provedení se liší. Rozsah je množina souvisle indexovaných záznamů, které se nacházejí mezi specifikovanými hodnotami mezí. Např. v databázi zaměstnanců indexované jmény, můžeme aplikovat rozsah k zobrazení všech zaměstnanců jejichž jména jsou větší než "Jones" a menší než "Smith". Protože rozsahy závisí na indexech, rozsahy mohou být aplikovány pouze na indexované tabulky Paradoxu a dBASE (pro SQL tabulky, rozsahy mohou být aplikovány na libovolné položky v seznamu vlastnosti IndexFieldNames). Rozsahy mohou být řazeny pouze na základě existujících indexů.
Filtr, na druhé straně, je libovolná množina souvisejících nebo nesouvisejících záznamů, které sdílejí specifikované datové prvky a to bez ohledu na indexaci. Např. máme filtr v databázi zaměstnanců, který zobrazuje všechny zaměstnance, které žijí v Kalifornii a během pěti nebo více let pracovali v našem podniku. I když filtry mohou používat indexy, filtry na nich nejsou závislé. Filtry jsou aplikovány, záznam po záznamu, jak aplikace prochází datovou množinou.
Obecně filtry jsou flexibilnější než rozsahy. Rozsahy ale mohou být efektivnější, když datová množina je značně velká a záznamy nás zajímající, jsou seskupeny do skupin sousedních indexů. Pro značně rozsáhlé datové množiny je ale mnohem výhodnější použít komponentu dotazu k výběru dat pro zobrazení i editaci.
Vytváření a aplikování nového rozsahu
Proces vytváření a aplikování rozsahu vyžaduje tyto obecné kroky:
  1. Uvedeme datovou množinu do stavu dsSetKey a nastavíme počáteční hodnotu indexu pro rozsah.
  2. Nastavíme koncovou hodnotu indexu pro rozsah.
  3. Aplikujeme rozsah na datovou množinu.
Nastavování počátku rozsahu
Voláním SetRangeStart uvedeme datovou množinu do stavu dsSetKey a zahájíme vytváření seznamu počátečních hodnot pro rozsah. Po volání SetRangeStart následující řada přiřazení vlastnosti Fields jsou chápány jako hodnoty počátečních indexů k použití při aplikování rozsahu. Když používáme tabulky Paradoxu nebo dBASE, pak specifikované položky musí být indexované položky.
Např. předpokládejme, že naše aplikace používá komponentu tabulky nazvanou Customers, spojenou s tabulkou CUSTOMER a že máme vytvořené trvalé položkové komponenty pro všechny položky v datové množině Customers. CUSTOMER je indexován na svém prvním sloupci (CustNo). Formulář aplikace má dvě editační komponenty nazvané StartVal a EndVal pro specifikaci počáteční a koncové hodnoty rozsahu. Následující kód může být použit k vytvoření a aplikování rozsahu:
Customers->SetRangeStart();
Customers->FieldValues["CustNo"] = StrToInt(StartVal->Text);
Customers->SetRangeEnd();
if (!EndVal->Text.IsEmpty())
  Customers->FieldValues["CustNo"] = StrToInt(EndVal->Text);
Customers->ApplyRange();
Tento kód testuje zda text zadaný v EndVal není prázdný před přiřazením nějaké hodnoty Fields. Pokud text zadaný pro StartVal je prázdný, pak všechny záznamy od začátku tabulky jsou vloženy do rozsahu, neboť všechny hodnoty jsou větší než null. Ale pokud do EndVal je přiřazen prázdný text, pak do rozsahu není vložen žádný záznam, neboť žádný není menší než null.
Pro vícesloupcové indexy můžeme specifikovat počáteční hodnotu pro všechny nebo některé položky v indexu. Pokud nepřiřadíme hodnotu pro položku použitou v indexu, pak je použita hodnota null při aplikování rozsahu. Pokud nastavíme více hodnot než je hodnot v indexu, pak nadbytečné položky jsou při výpočtu rozsahu ignorovány.
K ukončení specifikace počátku rozsahu, voláme SetRangeEnd nebo ApplyRange. Tyto metody budou popsány dále.
Tip: K zahájení od začátku datové množiny, vynecháme volání SetRangeStart.
Můžeme také nastavit počáteční (a koncové) hodnoty pro rozsah pomocí volání SetRange (viz dále).

Nastavování konce rozsahu
Volání SetRangeEnd uvádí datovou množinu do stavu dsSetKey a zahajuje vytváření seznamu koncových hodnot pro rozsah. Po volání SetRangeEnd následuje řada přiřazení do vlastnosti Fields chápaných jako koncové hodnoty použitých indexů při aplikování rozsahu. Specifikované položky musí být pro tabulky Paradoxu a dBASE indexovanými položkami.
Poznámka: Vždy specifikujte koncovou hodnotu pro rozsah, i když rozsah chceme končit posledním záznamem v datové množině. Pokud nedodáme koncovou hodnotu, pak C++ Builder předpokládá, že koncová hodnota rozsahu je null. Rozsah s koncovou hodnotou rovnou null je vždy prázdný.
Koncovou hodnotu nejsnadněji přiřadíme voláním metody FieldByName. Např.
Table1->SetRangeStart();
Table1->FieldByName("LastName")->Value = Edit1->Text;
Table1->SetRangeEnd();
Table1->FieldByName("LastName")->Value = Edit2->Text;
Table1->ApplyRange();
Pro vícesloupcové indexy, můžeme specifikovat počáteční hodnotu pro všechny nebo pro některé položky v indexu. Pokud nezadáme hodnotu pro některou položku použitou v indexu, pak se zde při aplikování rozsahu předpokládá hodnota null. Pokud nastavíme více hodnot než je položek v indexu, pak je generována výjimka.
Po dokončení specifikace konce rozsahu voláme ApplyRange.
Poznámka: Koncovou (a také počáteční) hodnotu pro rozsah můžeme také nastavit voláním SetRange.

Nastavování počáteční a koncové hodnoty rozsahu
Místo odděleného volání SetRangeStart a SetRangeEnd ke specifikací hranic rozsahu, můžeme volat SetRange k uvedení datové množiny do stavu dsSetKey a nastavení počáteční a koncové hodnoty rozsahu v jediném volání. SetRange přebírá dva parametry konstantních polí: množina počátečních hodnot a množina koncových hodnot. Např. následující příkazy zřizují rozsah pro dvousloupcový index:
TVarRec StartVals[2];
TVarRec EndVals[2];
StartVals[0] = Edit1->Text;
StartVals[1] = Edit2->Text;
EndVals[0] = Edit3->Text;
EndVals[1] = Edit4->Text;
Table1->SetRange(StartVals, 1, EndVals, 1);
Pro vícesloupcové indexy, můžeme specifikovat počáteční a koncovou hodnotu pro všechny nebo pro některé položky v indexu. Pokud nezadáme hodnotu pro některou položku použitou v indexu, pak se zde předpokládá hodnota null při aplikování rozsahu. K vynechání hodnoty pro první položku v indexu a specifikaci hodnot pro následující položky, předáme pro vynechanou položku hodnotu null. Pokud nastavíme více hodnot než je položek v indexu, pak nadbytečné položky jsou ignorovány.
Poznámka: Vždy specifikujte koncovou hodnotu pro rozsah, i když rozsah chceme končit posledním záznamem v datové množině. Pokud nedodáme koncovou hodnotu, pak C++ Builder předpokládá, že koncová hodnota rozsahu je null. Rozsah s koncovou hodnotou rovnou null je vždy prázdný, protože počáteční hodnota je větší nebo rovna koncové hodnotě.

Specifikace rozsahu na základě částečných klíčů
Pokud klíč je tvořen jednou nebo více řetězcovými položkami, pak metoda SetRange podporuje částečné klíče. Např. pokud index je založen na sloupcích LastName a FirstName, pak následující specifikace rozsahu je přípustná:
Table1->SetRangeStart();
Table1->FieldValues["LastName"] = "Smith";
Table1->SetRangeEnd();
Table1->FieldValues["LastName"] = "Zzzzzz";
Table1->ApplyRange();
Tento kód zahrnuje všechny záznamy v rozsahu kde LastName je větší nebo rovno "Smith". Hodnota specifikace může také být:
Table1->FieldValues["LastName"] = "Sm";
Tento příkaz zahrnuje záznamy, které mají LastName větší nebo rovno "Sm. Následující příkaz vkládá záznamy s LastName větším nebo rovno "Smith" a FirstName je větší nebo rovno "J":
Table1->FieldValues["LastName"] = "Smith";
Table1->FieldValues["FirstName"] = "J";

Zahrnování nebo vylučování záznamů odpovídajících hraničním hodnotám
Implicitně rozsah zahrnuje všechny záznamy, které jsou větší nebo rovny specifikované počáteční hodnotě a menší nebo rovny specifikované koncové hodnotě. Toto chování je řízeno vlastností KeyExclusive. KeyExclusive má implicitně hodnotu false. Pokud to požadujeme, pak můžeme nastavit KeyExclusive pro komponentu tabulky na true k vyloučení záznamu odpovídajícímu konečné hodnotě. Např.
Table1->SetRangeStart();
Table1->KeyExclusive = true;
Table1->FieldValues["LastName"] = "Smith";
Table1->SetRangeEnd();
Table1->FieldValues["LastName"] = "Tyler";
Table1->ApplyRange();
Tento kód vkládá všechny záznamy v rozsahu kde LastName je větší nebo rovno "Smith" a menší než "Tyler".

Aplikování rozsahu
Metoda SetRange zřizuje hraniční podmínky pro rozsah, ale neuvádí je v činnost. Aby se rozsah projevil musíme volat ApplyRange. ApplyRange bezprostředně omezuje uživatelův pohled a přístup k datům na specifikovanou podmnožinu datové množiny.

Zrušení rozsahu
Metoda CancelRange ukončuje rozsah a obnovuje přístup k úplné datové množině. I když zrušením rozsahu obnovíme přístup ke všem záznamům v datové množině, hraniční podmínky pro rozsah jsou stále dostupné a rozsah můžeme kdykoli obnovit. Meze jsou chráněny dokud nezadáme nové meze nebo existující meze nemodifikujeme. Např. následující kód je přípustný:
...
Table1->CancelRange();
...
// později aplikujeme stejný rozsah znovu. Není nutno volat SetRangeStart ...
Table1->ApplyRange();
...

Modifikování rozsahu
Dvě funkce povolují modifikovat existující hraniční podmínky pro rozsah: EditRangeStart pro změnu počáteční hodnoty rozsahu a EditRangeEnd pro změnu koncové hodnoty rozsahu. Proces editace a aplikování rozsahu vyžaduje obecně tyto kroky:
  1. Uvedeme datovou množinu do stavu dsSetKey a modifikujeme počáteční hodnotu indexu pro rozsah.
  2. Modifikujeme koncovou hodnotu pro rozsah.
  3. Aplikujeme rozsah na datovou množinu.
Můžeme modifikovat počáteční nebo koncové hodnoty rozsahu nebo můžeme modifikovat obě hraniční podmínky. Pokud modifikujeme hraniční podmínky pro rozsah, který je právě aplikován na datovou množinu, pak změny se neprojeví dokud znova nevoláme ApplyRange.

Editace počátku rozsahu
Voláním EditRangeStart uvedeme datovou množinu do stavu dsSetKey a začneme modifikování současného seznamu počátečních hodnot pro rozsah. Přiřazení do vlastnosti Fields následující po volání EditRangeStart přepisují současné počáteční hodnoty rozsahu. Při používání Paradoxu nebo dBASE, položky musí být indexovanými položkami.
Tip: Pokud vytváříme rozsah založený na částečných klíčích, pak můžeme použít EditRangeStart k rozšíření počátečních hodnot pro rozsah.

Editace konce rozsahu
Voláním EditRangeEnd uvedeme datovou množinu do stavu dsSetKey a začneme modifikování současného seznamu koncových hodnot pro rozsah. Přiřazení do vlastnosti Fields následující po volání EditRangeEnd přepisují současné koncové hodnoty rozsahu. Při používání Paradoxu nebo dBASE položky musí být indexovanými položkami.
Poznámka: Vždy specifikujte koncovou hodnotu pro rozsah, i když rozsah chceme končit posledním záznamem v datové množině. Pokud nedodáme koncovou hodnotu, pak C++ Builder předpokládá, že koncová hodnota rozsahu je null. Rozsah s koncovou hodnotou rovnou null je vždy prázdný, protože počáteční hodnota je větší nebo rovna koncové hodnotě.

Zrušení všech záznamů v tabulce

Pro zrušení všech řádků dat v tabulce voláme metodu EmptyTable komponenty tabulky. Pro SQL tabulky je tato metoda úspěšná pouze tehdy, pokud k tabulce máme privilegium DELETE. Např. následující příkaz zruší všechny záznamy v datové množině:
PhoneTable->EmptyTable();
Pozor: Zrušení dat pomocí EmptyTable nelze odvolat.

Zrušení tabulky

Při návrhu, k zrušení tabulky v databázi, klikneme pravým tlačítkem na komponentě tabulky a v zobrazené místní nabídce zvolíme Delete Table. Tato volba v nabídce je dostupná pouze pokud komponenta tabulky reprezentuje existující databázovou tabulku (vlastnosti DatabaseName a TableName specifikují existující tabulku).
Pro zrušení tabulky za běhu, voláme metodu DeleteTable komponenty tabulky. Např. následující příkaz odstraňuje tabulku připojené datové množiny:
CustomersTable->DeleteTable();
Pozor: Když zrušíme tabulku pomocí DeleteTable, pak tabulka a všechna její data jsou ztracena.

Přejmenování tabulky

K přejmenování tabulky Paradoxu a dBASE při návrhu, v místní nabídce komponenty tabulky zvolíme Rename Table. Můžeme také přejmenovat tabulku přepsáním vlastnosti TableName existující tabulky v Inspektoru objektů. Když změníme vlastnost TableName, pak v zobrazeném dialogovém okně jsme dotázáni, zda chceme tabulku přejmenovat. Lze zde zvolit přejmenování tabulky, nebo zrušení operace měnící vlastnost TableName (např. pro vytvoření nové tabulky) beze změny jména tabulky reprezentované starou hodnotou TableName.
Pro přejmenování tabulky Paradoxu nebo dBASE za běhu, voláme metodu RenameTable komponenty tabulky. Např. následující příkaz přejmenovává tabulku Customer na CustInfo:
Customer->RenameTable("CustInfo");

Vytváření tabulky

Nové tabulky databáze lze vytvářet při návrhu nebo za běhu. Příkaz Create Table (při návrhu) nebo metoda CreateTable (za běhu) umožňuje vytvářet tabulky bez nutnosti znát SQL. Vyžaduje to ale dobrou znalost vlastností, událostí a metod komponent datových množin. Nejprve je nutno takto definovat vytvářenou tabulku: Poznámka: Při návrhu, můžeme předzavést definice položek a definice indexů existující tabulky ve vlastnostech FieldDefs a IndexDefs. Nastavíme vlastnosti DatabaseName a TableName na specifikaci existující tabulky. V místní nabídce komponenty tabulky zvolíme Update Table Definition. To automaticky nastavuje hodnoty vlastností FieldDefs a IndexDefs na popis položek a indexů existující tabulky. Dále nastavíme DatabaseName a TableName na specifikaci vytvářené tabulky, a zrušíme všechny dotazy na přejmenování existující tabulky. Pokud chceme uložit tyto definice s komponentou tabulky (např. pokud naše aplikace je pak použije k vytvoření tabulek na systému uživatele), pak nastavíme vlastnost StoreDefs na true.
Když již máme tabulku úplně popsanou, pak ji můžeme vytvořit. Při návrhu v místní nabídce komponenty tabulky zvolíme Create Table. Za běhu voláme metodu CreateTable pro generování specifikované tabulky.
Varování: Pokud vytváříme tabulku, která duplikuje jméno existující tabulky, pak existující tabulka a všechna její data jsou přepsána nově vytvořenou tabulkou. Starou tabulku a její data nelze obnovit.
Následující kód vytváří novou tabulku za běhu a přiřazuje ji k přezdívce BCDEMOS. Před vytvořením nové tabulky ověříme zda její jméno se neshoduje se jménem již existující tabulky.
TTable *NewTable = new TTable(Form1);
NewTable->Active = false;
NewTable->DatabaseName = "BCDEMOS";
NewTable->TableName = Edit1->Text;
NewTable->TableType = ttDefault;
NewTable->FieldDefs->Clear();
TFieldDef *NewField=NewTable->FieldDefs->AddFieldDef();//definování 1.položky
NewField->DataType = ftInteger;
NewField->Name = Edit2->Text;
NewField = NewTable->FieldDefs->AddFieldDef(); // definování druhé položky
NewField->DataType = ftString;
NewField->Size = StrToInt(Edit3->Text);
NewField->Name = Edit4->Text;
NewTable->IndexDefs->Clear();
TIndexDef *NewIndex = NewTable->IndexDefs->AddIndexDef(); // přidání indexu
NewIndex->Name = "PrimaryIndex";
NewIndex->Fields = Edit2->Text;
NewIndex->Options << ixPrimary << ixUnique;
// Testujeme zda tabulka již neexistuje
bool CreateIt = (!NewTable->Exists);
if (!CreateIt)
   if (Application->MessageBox((AnsiString("Overwrite table ") + Edit1->Text +
                               AnsiString("?")).c_str(),
                               "Table Exists", MB_YESNO) == IDYES)
    CreateIt = true;
if(CreateIt)
  NewTable->CreateTable(); // vytvoření tabulky

Importování dat z jiné tabulky

Metodu BatchMove komponenty tabulky můžeme použít k importování dat z jiné tabulky. BatchMove může: BatchMove přebírá dva parametry: jméno tabulky, ze které chceme data importovat a specifikace režimu operace importu. Následující tabulka popisuje možné nastavení pro specifikace režimu.
 
Hodnota Význam
batAppend Připojení všech záznamů ze zdrojové tabulky na konec této tabulky.
batAppendUpdate Připojení všech záznamů ze zdrojové tabulky na konec této tabulky a aktualizace existujících záznamů v této tabulce stejnými záznamy ze zdrojové tabulky.
batCopy Kopíruje všechny záznamy ze zdrojové tabulky do této tabulky.
batDelete Ruší všechny záznamy v této tabulce, které se také vyskytují ve zdrojové tabulce.
batUpdate Aktualizace existujících záznamů v této tabulce jejich protějšky ve zdrojové tabulce.

Např. následující kód aktualizuje záznamy v současné tabulce záznamy z tabulky Customer:
Table1->BatchMove("CUSTOMER.DB", batUpdate);
BatchMove vrací počet úspěšně importovaných záznamů.
Pozor: Importované záznamy pomocí režimu batCopy přepisují existující záznamy. K ochránění existujících záznamů použijeme batAppend.
BatchMove provádí pouze některé funkce dostupné naší aplikací přímo prostřednictvím komponenty TBatchMove. Pokud potřebujeme přesouvat větší množství dat mezi tabulkami, pak použijeme komponentu dávkového přesunu, místo volání funkce BatchMove komponenty tabulky.

Synchronizování tabulek připojených ke stejné databázové tabulce

Pokud více než jedna komponenta tabulky je spojena se stejnou tabulkou databáze pomocí jejich vlastností DatabaseName a TableName a tabulky nesdílí komponentu datového zdroje, pak každá tabulka má svůj vlastní pohled na data a svůj vlastní aktuální záznam. Při procházení záznamy uživatelem se aktuální záznamy komponent tabulek liší.
K vnucení aktuálního záznamu pro každou z těchto komponent tabulky na stejnou hodnotu voláme metodu GotoCurrent. GotoCurrent nastavuje svůj vlastní aktuální záznam tabulky na aktuální záznam komponenty jiné tabulky. Např. následující kód nastavuje aktuální záznam CustomerTableOne na aktuální záznam  CustomerTableTwo:
CustomerTableOne->GotoCurrent(CustomerTableTwo);
Tip: Pokud naše aplikace potřebuje synchronizovat komponenty tabulek tímto způsobem, pak vložíme komponenty do datového modulu a vložíme hlavičku datového modulu do každé jednotky, ve které přistupujeme k tabulkám.
Pokud musíme synchronizovat komponenty tabulek na oddělených formulářích, pak musíme vložit hlavičkový soubor jednoho formuláře do jednotky druhého formuláře a alespoň jedno jméno tabulky musíme kvalifikovat jménem formuláře. Např.
CustomerTableOne->GotoCurrent(Form2->CustomerTableTwo);

Vytváření formulářů Master-detail

Vlastnosti MasterSource a MasterFields komponenty tabulky mohou být použity k zřízení vazby 1 : N mezi dvěmi tabulkami. Vlastnost MasterSource je použita ke specifikaci datového zdroje, ze kterého tabulka bude získávat data pro tabulku Master. Např. pokud spojíme dvě tabulky do vazby Master-detail, pak tabulka Detail může zachycovat události nastalé v tabulce Master specifikací datového zdroje tabulky Master v této vlastnosti.
Vlastnost MasterFields specifikuje sloupce společné v obou tabulkách použité pro zřízení vazby. Pro spojení tabulek na základě více jmen sloupců použijeme středníky oddělovaný seznam:
Table1->MasterFields = "OrderNo;ItemNo";
Pro pomoc při vytváření smysluplných vazeb mezi dvěmi tabulkami, můžeme použít Návrhář vazby položek.

Příklad formuláře Master-detail
Následující kroky vytvářejí jednoduchý formulář, ve kterém uživatel může procházet záznamy zákazníků a zobrazovat všechny objednávky od aktuálního zákazníka. Tabulka Master je tabulka CustomersTable a tabulka Detail je OrdersTable:

  1. Umístíme dvě komponenty TTable a dvě komponenty TDataSource do datového modulu.
  2. Nastavíme vlastnosti první komponenty TTable takto:
  3. Nastavíme vlastnosti druhé TTable takto:
  4. Nastavíme vlastnosti první TDataSource na:
  5. Nastavíme vlastnosti druhé TDataSource na:
  6. Umístíme dvě komponenty TDBGrid na formulář.
  7. Zvolíme File | Include Unit Hdr k určení, že formulář bude používat datový modul.
  8. Nastavíme vlastnost DataSource první komponenty mřížky na DataModule2->CustSource a nastavíme vlastnost DataSource druhé komponenty mřížky na DataModule2->OrdersSource.
  9. Nastavíme vlastnost MasterSource komponenty OrdersTable na CustSource. Tím propojíme tabulku CUSTOMER (tabulka Master) s tabulkou ORDERS (tabulka Detail).
  10. Dvojitě klikneme na hodnotu vlastnosti MasterFields v Inspektoru objektů k vyvolání Návrháře vazby položek k nastavení následujících vlastností:
  11. Nastavíme vlastnosti Active komponent CustomersTable a OrdersTable na true k zobrazení dat v mřížkách na formuláři.
  12. Přeložíme a spustíme aplikaci.
Pokud aplikaci spustíme nyní, pak uvidíme, že tabulky jsou spojeny a když přejdeme na jiný záznam v tabulce CUSTOMER, pak vidíme pouze ty záznamy tabulky ORDERS, které se vztahují k aktuálnímu zákazníku.

Práce s vnořenými tabulkami

Vnořené komponenty tabulek poskytují přístup k datům ve vnořené datové množině tabulky. Vlastnost NestedDataSet trvalé položky vnořené datové množiny obsahuje odkaz na vnořenou datovou množinu. Jelikož TNestedDataSet je odvozena od TBDEDataSet, vnořené tabulky dědí funkčnost BDE a můžeme použít BDE pro přístup k datům vnořené tabulky. Vnořené tabulky poskytují více funkčnosti tabulky komponenty, ale zpřístupňovaná data jsou uložena ve vnořené tabulce.
Následující kroky jsou obecné instrukce pro nastavování komponent vnořených tabulek při návrhu. Komponenta tabulky nebo živého dotazu musí být dostupná v datové množině obsahující datovou množinu nebo odkazovanou položku. Trvalé položky pro TDataSetField nebo TReferenceField musí také existovat. K použití komponenty vnořené tabulky:
  1. Umístíme komponentu vnořené tabulky ze stránky Data Access Palety komponent do datového modulu nebo na formulář a nastavíme její vlastnost Name na unikátní hodnotu v naší aplikaci.
  2. Nastavíme DataSetField komponenty na jméno trvalé položky datové množiny nebo položkový odkaz k přístupu. Položky můžeme vybrat z rozbalovacího seznamu.
  3. Umístíme komponentu datového zdroje do datového modulu nebo na formulář a nastavíme vlastnost DataSet na jméno komponenty vnořené tabulky. Komponenta datového zdroje je použita k předání výsledkové množiny z tabulky do datových ovladačů k zobrazení.

  1. V adresáři Program files\Borland\CBuilder\Examples\DBTask\Find nalezneme demonstrační aplikaci hledající záznamy na základě hodnot v tabulce Customer. Prohlédněte si tuto aplikaci a zjistěte jak pracuje.
16. Práce s tabulkami