Adatbázis kezelő alkalmazásban gyakran van szükség arra, hogy egy lekérdezés eredményét mondjuk TDBGrid-ben mutassuk meg, a felhasználó igényei szerint szűrve. Ilyenkor egy TQuery komponenst szoktunk használni egy SELECT utasítással, és a szűrést a WHERE feltételen keresztül valósítjuk meg. A felhasználónak általában teljes szabadsága van a szűrés beállításában, lekérhet egy nagyon szűk részhalmazt, de lekérheti a teljes tábla vagy nézet tartalmát is. Mellékelt példában a nagy eredményhalmazok kezelésének nézünk utána.
A példaprogram futtatásához a pubs adatbázisban hozzunk létre egy táblát largeset néven (futtassuk le az SQL Serverben a mellékelt CreateTable.sql scriptet). A programban feltöltjük ezt a táblát 3000 teszt rekorddal, ehhez a 'tábla feltöltése adattal' gombot kell megnyomni. A gomb ismételt megnyomásával újabb 3000 adat íródik a táblába. Végül a táblát egy query-n keresztül kérdezzük le egy TDBGrid-be, miközben két szöveges mezőn keresztül szűrhetünk. Alaphelyzetben azokat a rekordokat listázzuk ki, amelyek s1 sztringje 'a'-val, s2 sztringje 'b'-vel kezdődik.
select i1, i2, i3, s1, s2, s3
from largeset
where s1 like ' a%' and s2 like ' b%'
Ilyen valószínűleg kevés van, tehát a megjelenítés egész gyors lesz. Próbáljuk ki, hogy a TEdit komponensekbe a %a%, illetve a %b% szövegeket írjuk.
A felhasználónak azonban lehetősége van a szűrést úgy beállítani, hogy minden rekord lejöjjön. Első körben ez nem is jelent problémát, mert a BDE van annyira okos, hogy az SQL Servertől csak annyi adatot kér el, amennyit éppen meg kell jelenítenie. Ha úgy nyitunk meg egy query-t, hogy nem jelenítjük meg, akkor egyetlen rekord sem jön át a query megnyitásakor! Ahogy a TDBGrid-ben lefelé mozgunk, mindig annyi rekordot kapunk az SQL Servertől, amennyi éppen a TDBGrid-nek kell.
Érdemes az SQLMonitort is bekapcsolni, és figyelni, hogy mi történik. A letöltött rekordokat a BDE puffereli, és ha a TDBGrid-ben visszafelé mozgunk, már nem kell a szerverhez fordulni.
A módszerrel akkor van probléma, ha a felhasználó az utolsó rekordokat szeretné látni, és Ctrl-End-et nyom. Ekkor ugyanis a BDE az összes rekordot lehozza. Ez már a 3000 rekordos példatáblánk esetén is eltart egy ideig, így nem nehéz elképzelni, hogy mennyit kéne várni egy milliós nagyságrendű rekordot tartalmazó tábla esetén. Ha a BDE nekiáll letölteni az összes rekordot, már nincs módunk a folyamat megállítására, mindenképpen végig kell várnunk. A nagyszámú rekord pufferelése miatt a nem annyira biztonságos Windows9x rendszerek akár be is dobhatják a törölközőt.
Ugyanez a gond, ha a felhasználó a scrollbart lefelé húzza. Ne felejtsük el, hogy query esetében a Delphi nem tudja kitalálni, hogy az eredményhalmazban mennyi rekord lesz, így a TDBGrid scrollbárjának csak három állapota van, a halmaz elején áll, valahol középen és a halmaz végén.
A harmadik veszélyes szituáció a TQuery recordcount metódusának meghívása. Ekkor ugyanis a BDE lekéri az összes rekordot és maga számolja azokat össze!
A példaprogramban ezt is ki lehet próbálni. A státuszsor frissítésére szolgáló metódus tartalma:
sb1.SimpleText := 'Rekordok száma: ' + inttostr(q1.Recordcount);
Ez minden lekérdezés indítása után meghívódik, hogy a felhasználó lássa, hogy mekkora eredményhalmazt produkált. Ha minden rekordot lekérünk (where s1 like '%' and s2 like '%'), akkor a recordcount hívása miatt lassú lesz a lekérdezésünk, míg ha ezt a sort kikommentezzük, akkor csak a TDBGrid kitöltéséhez szükséges rekordok jönnek le teljes halmaz lekérdezésekor is.
A TTable komponens használatakor más a helyzet. (A 16 bites Delphiben még nem, de a 2.0-tól kezdve már igen.) Ha a TTable RecordCount metódusát hívjuk, egy select count(*) query megy az SQL Servernek. Ugyan most is össze kell számolni a rekordokat, de a szerver ezt nagyságrendekkel gyorsabban képes elvégezni, mint a BDE. Ha böngészés során Ctrl-End-et nyomunk, a BDE valahogy képes a tábla végére ugrani, és az utolsó néhány rekordot elkérni. A rekordok azonban nem pufferelődnek, ahogy le-föl mozgunk a TDBGrid-ben, a BDE folyamatosan fetch-eli a megjelenítendő rekordokat.
Szerencsére a problémákra több megoldás is létezik.
TOP
Ha a BDE-t úgy paramétereztük, hogy az SQL query-ket ne ő, hanem az SQL Server hatjsa végre, akkor használhatjuk a SELECT utasítás TOP kulcsszavát.
SELECT TOP n [PERCENT] ...
Az n szám megadásával meghatározhatjuk, hogy az eredményhalmaz első hány rekordjára vagyunk kíváncsiak. Ha a PERCENT szót is kiírjuk, akkor az n nem a sorok számát, hanem az eredményhalmaz százalékban kifejezett hányadát jelenti.
select TOP 100 *
from largeset
Az eredményhalmaz első 100 sorát kérjük le.
select TOP 10 PERCENT *
from largeset
Az eredményhalmaz 10%-t kérjük el a szervertől.
Ha a SELECT utasításban az ORDER BY-t is használjuk, a TOP kulcsszó a már rendezett halmazon jut érvényre. Ilyenkor az SQL Server a teljes eredményhalmazt előállítja, rendezi, és utána leszámolja a megfelelő számú rekordot.
SET ROWCOUNT
Egy másik korlátozási módszer az SQL Server oldalán a SET ROWCOUNT n utasítás használata. Az utasítással azt adhatjuk meg, hogy legfeljebb hány soros lekérdezésekkel dolgozhat a szerver. Hatása ugyanaz, mint a TOP kulcsszóé, de egy kicsit másképp működik.
Ha ORDER BY-t használunk egy SELECT-ben, a SET ROWCOUNT n hatására csak n rekord válogatódik le, és az rendeződik sorba, vagyis a korlátozás hatása meg az ORDER BY kiértékelése előtt érvényesül. Továbbá amíg a TOP kulcsszó csak egy SELECT-re vonatkozik, addig a SET ROWCOUNT hatása addig tart, amíg egy másik SET ROWCOUNT utasítás azt felül nem bírálja. A korlátozást a SET ROWCOUNT 0 utasítással kapcsolhatjuk ki.
Max Rows
A BDE Administrator programban az SQL Links jellemzői között találjuk a Max Rows-t. Itt megadhatjuk, hogy az SQL utasítások végrehajtása során az SQL driver legfeljebb hány sort fetch-elhet le. Ez nemcsak a táblákra és nézetekre vonatkozik, hanem a sémainformációkra is, tehát túl kis értéket nem szerencsés beállítani.
Ha a lekérdezés eredménye több rekordból áll, mint a Max Rows értéke, az utolsó rekordnál a DBIERR_ROWFETCHLIMIT hibaüzenet generálódik. Ez ugyanaz, mint a DBIERR_EOF üzenet, de mégis jelzi, hogy egy kliens által kikényszerített fájlvége jelről van szó. A Delphi komponensei úgy fognak viselkedni, mintha az eredményhalmazban nem lenne több rekord.
MS SQL cikksorozat