COMPUTERWORLD
pod kapotou
Kurzor

Nahlédnutím do novodobých slovníků cizích slov si lze o pojmu kurzor pouze potvrdit představu, kterou o něm má většina uživatelů počítačů: kurzor je poziční ukazatel na obrazovce. Jistě v tuto chvíli nechápete, jak to souvisí s databázovou abecedou a proč bychom se takové nicotné věcičce měli podrobně věnovat. Mimochodem, slovník z r. 1930 nabízí trochu jiný význam: kursor (v starém pravopisu) je běhoun, posel. Dnes bychom spíše řekli běžec. Připojíme ještě třetí význam slova kurzor, tj. kurzor jako objekt jazyka SQL.

Je všeobecně známo minimálně trojí použití SQL:

  • SQL jako dotazovací (nebo obecněji manipulační) jazyk pro relační databáze,
  • SQL jako složka hostitelského jazyka pro programování databázových aplikací,
  • SQL jako jazyk komunikace mezi různými zdroji dat.

Připusťme, že první použití je stále řidší, i když ne zcela neaktuální. V některých aplikacích jsou stále potřeba prostředky umožňující formulovat netriviální ad hoc dotazy na které nestačí běžné formulářové jazyky či předem připravené uživatelské obrazovky. Třetí oblast pokrývá situace, kdy požadavky na data jsou automaticky přeformulovány do SQL a zaslány (vzdálenému) zdroji dat. Zaměříme se na oblast programování aplikací, kde hostitelským jazykem je běžný programovací jazyk (např. C) a nástrojem získání dat z databáze je SQL. Dialekt jazyka SQL zapojený do programovacího jazyka se nazývá vnořený SQL.

Pro získání data z databáze slouží v SQL příkaz SELECT, který vrací množinu, případně multimnožinu (obsahuje opakující se prvky) řádků. Tato nesporná výhoda jazyka, tj. jeho množinová orientace, je ovšem zcela degradována na úrovni jazyka programovacího. Programovací jazyk nemá totiž k dispozici přímé prostředky pro práci s množinami dat. Tato asymetrie, mimochodem nejčastěji kritizovaná, vlastně znamená, že je nutno disponovat mechanismem, který by umožnil zpracovávat výsledek SELECT příkazu přijatelným způsobem. A takovým mechanismem je právě kurzor.

Ještě než budeme kurzor definovat, vyřídíme jeden jednoduchý případ. Ve speciálním případě je výsledkem SELECT příkazu jeden řádek (přesněji jednoprvková množina). Pak není problém uložit komponenty řádku do proměnných programovacího jazyka.

Vzpomeňme relaci KINO(NÁZEV,ADRESA, JMÉNO_V). Chceme-li pracovat v rámci programu s jménem vedoucího nějakého kina, stačí napsat příkaz

EXEC SQL SELECT JMÉNO_V INTO :VEDOUCÍ

FROM KINO WHERE NÁZEV = :K_NÁZEV;

Předpokládejme, že před tímto příkazem někde předcházel příkaz připojení k potřebné databázi. Pro porozumění danému dotazu SQL je třeba vědět, že:

  • SQL příkazy vnořené do programovacího jazyka jsou předsazeny EXEC a jsou, např. v jazyku C, zakončeny znakem ;. Z toho plyne nutnost existence předkompilátoru, který takové příkazy přemění v posloupnost volání funkcí v daném programovacím jazyce. Vztah předkompilace/kompilace se liší u různých relačních SŘBD.
  • Použité příkazy SQL mohou obsahovat odkazy na proměnné z hostitelského jazyka. Tyto proměnné jsou předsazeny znakem : a jsou deklarovány ve speciální sekci ohraničené příkazy EXEC SQL BEGIN DECLARE SECTION a EXEC SQL END DECLARE SECTION. Příkazy deklarující proměnné se opět v různých relačních SŘBD vzájemně liší.
  • Proměnná K_NÁZEV umožňuje nastavit skutečný název kina před spuštěním příkazu SELECT. To znamená, že dotaz je vlastně jednoduše parametrizován.
  • Výsledek příkazu je nejvýše jedna hodnota (NÁZEV je v relaci KINO primárním klíčem), která se uloží v proměnné VEDOUCÍ programovacího jazyka.

I v obecném případě, kdy výsledkem SELECT příkazu je více řádků, dovoluje rozhraní mezi SQL a programovacím jazykem přenést pouze jeden řádek, buď z programu do databáze nebo naopak. A k tomu právě složí kurzor. Kurzor je objekt jazyka SQL, který čísluje záznamy v množině získané pomocí příkazu SELECT a dovoluje aktualizovat nebo odstraňovat běžný záznam adresovaný kurzorem.

Zamysleme se na chvíli, proč bylo zvoleno právě toto řešení. Důvod je prostý. Z hlediska současných (klasických) programovacích jazyků by bylo možné použít ještě pole, do kterého by se výsledek SELECT příkazu uložil. To je ovšem nevhodné, protože neznáme dopředu, jak velký bude výsledek, a pole, jak je známo, je datová struktura implementovaná ve vnitřní paměti.

Vysvětlíme pojem kurzoru na příkladě. Chceme zpracovávat informace o kinech spolu s jejich adresou, kde se hraje daný film. V programu definujeme kurzor pomocí SELECT příkazu.

EXEC SQL DECLARE CURSOR KDE_TO_HRAJÍ FOR

SELECT (NÁZEV,ADRESA) FROM PŘEDSTAVENÍ, KINO

WHERE PŘEDSTAVENÍ.NÁZEV = KINO.NÁZEV AND JMÉNO_F = :X;

Předpokládejme, že proměnná X byla definována a že bude sloužit pro parametrizaci podle jména filmu, pro který chceme získat množinu dvojic (název kina, adresa kina), kde se daný film hraje. Pozor, jde pouze o deklaraci a žádný pohyb dat z databáze ještě není aktivizován. Na to je třeba nejprve kurzor KDE_TO_HRAJÍ otevřít příkazem OPEN. Teprve v tomto okamžiku databázový monitor připraví vyhodnocení příkazu SELECT a to s právě danou hodnotou proměnné X. Jednotlivé řádky výsledku se získávají příkazem FETCH-INTO, kde za INTO uvedeme proměnné, do kterých chceme uložit komponenty právě získaného řádku. V jazyku C pak získáme část programu

EXEC OPEN KDE_TO_HRAJÍ;

WHILE (TRUE) {

EXEC SQL FETCH KDE_TO_HRAJÍ

INTO(:INAZEV_K,:IADRESA)

IF (SQLCA.SQLCODE == 100)

BREAK;

zpracování proměnných

}

EXEC SQL CLOSE KDE_TO_HRAJÍ;

Ukázka okamžitého nastavení kurzoru je na obr. 1.


směr pohybu kurzoru

Obr. Kurzor na dané pozici

Za povšimnutí stojí velmi primitivní příkaz testující úspěšnost provedení příkazu FETCH. SQLA (SQL Communication Area) je datová struktura obsahující proměnné použitelné pro komunikaci mezi programem a databázovým monitorem. Obsahuje-li její složka SQLCODE hodnotu 100, žádný další řádek již nebyl vybrán a program vyskočí z cyklu. Obecnější přístup k řešení chybových situací je pomocí příkazu EXEC WHENEVER podmínka akce;. Ten umožňuje pokrýt více chybových situací a reakce na ně (např. skok do jiné části programu, zastavení programu, volání naprogramované funkce). Pomocí CLOSE se kurzor zavírá, tj. znepřístupní se aktivní množina řádků daných SELECT příkazem.

Kurzor je jakési pojmenované posuvné okénko (běžec) umožňující pomocí příkazu SELECT získat jistá data, příkazem FETCH se po nich pohybovat stylem “jeden řádek v čase” a umisťovat data do připravených proměnných.

Princip kurzoru připomíná čtení sekvenčního souboru v klasických jazycích hromadného zpracování dat, tj. po jednom a v jednom směru o začátku do konce. Jeden řádek odpovědi může být čten pouze jednou. Jinak je třeba kurzor zavřít a znovu otevřít. Podobnost se sekvenčním souborem ale nelze zaměnit za ekvivalenci. Při použití FETCH nemusí být výsledek příkazu SELECT k dispozici, řádky výsledku se mohou konstruovat dynamicky.

V SQL92 je koncepce kurzoru dovedena dál. Je již simulován jistý "přímý přístup". Je možné se pohybovat od dané pozice dopředu a zpět, dokonce i o více míst, případně absolutně na řádek zadaný číslem. Tyto možnosti (SCROLL CURSOR) se dnes již vyskytují v řadě implementací (i když nesplňují celý standard SQL92).

S kurzorem také souvisí příkazy DELETE a UPDATE. Lze je použít tak, že se odstraní, resp. aktualizuje řádek relace, na který právě ukazuje daný kurzor. Kurzor ovšem musí být definován FOR UPDATE.

SQL92 vyřešil také další důležitý problém. Jak se má kurzor chovat u transakčního zpracování, když jiná transakce mění řádky relace, na které zrovna ukazuje nebo bude ukazovat můj kurzor? V definici kurzoru lze zadat klíčové slovo INSENSITIVE, což znamená, že řádky přístupné kurzoru se nebudou měnit během doby, kdy je kurzor otevřen. Bez INSENSITIVE je možné např. dosáhnout, že obdržení 23. řádku použitím FETCH na KDE_TO_HRAJÍ (v případě, že je definován jako SCROLL) může vést při druhém požadavku na týž řádek k jeho jiné hodnotě. Další chování kurzoru je spojeno s jeho použitím v transakčním zpracování (úrovně isolace), což ovšem již překračuje cíl dnešní úvahy.

Jaká je budoucnost kurzorů? Jistě se budou ještě nějaký čas používat. Blíží se však standard pracovně zvaný SQL3, který předkládá zvláštní jazyk pro psaní transakcí, což povede k tomu, že aplikace se obejde bez vnějšího programovacího jazyka či jazyka 4GL. Bude znamenat jistou revoluci v programování, ale i krok kupředu směrem ke kooperativnímu zpracování. Pomocí SQL bude možné získávat nejen data ze vzdálené databáze, ale obdržet je např. zpracované tam zaslaným vlastním aplikačním programem.



<seznam dílů seriálu>   <COMPUTERWORLD>