In SQL dienen cursors als een aanwijzer die de programmeertaal van een applicatie in staat stelt om de queryresultaten rij voor rij af te handelen. Dit artikel onderzoekt snel het concept erachter en laat zien hoe cursors kunnen worden gedeclareerd, geopend, gegevens kunnen worden opgehaald en vervolgens kunnen worden gesloten.
SQL-cursors
De gegevens in de relationele database worden beheerd in de vorm van sets. Als gevolg hiervan worden queryresultaten die door SQL SELECT-instructies worden geretourneerd, resultatensets genoemd. De resultaatsets zijn niets anders dan combinaties van een of meer rijen en kolommen die zijn geëxtraheerd uit een of meer tabellen. U kunt door de resultatensets bladeren om de informatie te extraheren die u nodig hebt. De geretourneerde gegevenselementen worden gebruikt door programmeertalen zoals Java of andere voor specifieke toepassingsdoeleinden. Maar hier ligt het probleem van impedantie-mismatch vanwege het verschil in constructie tussen databasemodel en programmeertaalmodel.
Een SQL-databasemodel heeft drie hoofdconstructies:
- kolommen (of attributen) en hun gegevenstypes
- rijen (records of tupels)
- tabellen (verzameling records)
Daarom zijn de primaire mismatch tussen twee modellen:
- De datatypes van attributen die beschikbaar zijn in het databasemodel zijn niet hetzelfde als de variabeletypes die in programmeertalen worden gebruikt. Er zijn veel hosttalen en elk heeft een ander gegevenstype. De gegevenstypen van C/C++ en Java zijn bijvoorbeeld verschillend en dat geldt ook voor SQL-gegevenstypen. Daarom is een bindend mechanisme nodig om het incompatibiliteitsprobleem op te lossen.
- Het resultaat dat wordt geretourneerd door SQL SELECT-instructies zijn meerdere sets records waarbij elk record een verzameling kenmerken is. Hostprogrammeertalen werken meestal op individuele gegevenswaarden van tuple die door de query worden geretourneerd. Daarom is het essentieel dat de resultaten van SQL-query's overeenkomen met de gegevensstructuur die door de programmeertaal wordt ondersteund. Het mechanisme van het doorlopen van tupels is nodig om over tupels en hun attribuutwaarden te itereren.
De cursor fungeert als een iteratorvariabele om tupels te doorlopen die door de SQL-query worden geretourneerd en individuele waarden binnen elke tupel te extraheren, die vervolgens kunnen worden toegewezen aan het juiste type programmavariabelen.
De cursor dient daarom als een aanwijzer waarmee de programmeertaal het queryresultaat record voor record kan verwerken. Een cursor kan door alle rijen van een queryresultaat gaan, waarbij de nadruk op één rij tegelijk ligt. Overweeg de volgende SQL-query:
SELECT emp_no, first_name, last_name, birth_date FROM employees WHERE MONTH(birth_date) = MONTH(CURRENT_DATE) AND DAY(birth_date) = DAY(CURRENT_DATE);
Het zoekresultaat van de bovenstaande verklaring retourneert werknemersgegevens van al die werknemers wiens geboortedatum op de huidige dag van een bepaalde maand valt. Het resultaat kan veel rijen bevatten, maar de taal van de hosttoepassing kan rij voor rij afhandelen. Als gevolg hiervan wordt de cursor gedeclareerd als een ingesloten SQL-instructie in de programmeertaal van de toepassing. De cursor wordt dan geopend als een bestand en haalt een enkele rij uit het queryresultaat. Andere rijen worden vervolgens geëxtraheerd, in volgorde totdat de cursor wordt gesloten.
Een cursor declareren
Cursors worden net als een variabele gedeclareerd. Er wordt een naam gegeven, er zijn instructies om de cursor te openen, het zoekresultaat op te halen en tenslotte de cursor te sluiten. Merk op dat verschillende SQL-implementaties het gebruik van cursors op een andere manier ondersteunen. Maar er is een algemene overeenstemming over hoe de cursor moet worden geschreven.
We moeten SQL-instructies gebruiken om de cursorfunctionaliteit volledig te implementeren, omdat het eenvoudigweg declareren van een cursor niet voldoende is om gegevens uit een SQL-database te extraheren. Er zijn vier basisstappen om een cursor te declareren:
CURSOR VERKLAREN: De declaratie begint door de cursor een naam te geven en de query-expressie toe te wijzen die moet worden aangeroepen wanneer de cursor wordt geopend.
OPEN: De open-instructie voert de toegewezen query-expressie uit en maakt het queryresultaat gereed voor daaropvolgende FETCH.
OPHALEN: Haalt gegevenswaarden op in variabelen die vervolgens kunnen worden doorgegeven aan de programmeertaal van de host of aan andere ingesloten SQL-instructies.
SLUITEN: De cursor kan geen zoekresultaat meer ophalen.
De syntaxis is als volgt:
DECLARE <cursor_name> [SENSITIVE | INSENSITIVE | ASENSITIVE] [SCROLL | NO SCROLL] CURSOR [ WITH HOLD | WITHOUT HOLD] [ WITH RETURN | WITHOUT RETURN] FOR <sql_query_expression> [ ORDER BY <sort_expression>] [ FOR {READ ONLY | UPDATE [ OF <list_of_column>]}]
Het essentiële onderdeel van een cursordeclaratie is als volgt:
DECLARE <cursor_name> FOR <sql_query_expression>
Het optionele gedeelte zoals [SENSITIVE | ONGEVOELIG | ASENSITIVE] geeft aan of de cursor gevoelig is voor wijzigingen en of deze in het queryresultaat moeten worden weergegeven. GEVOELIG betekent dat de cursor wordt beïnvloed door wijzigingen, INSENSITIVE betekent dat de cursor niet wordt beïnvloed en ASENSITIEF betekent dat wijzigingen al dan niet zichtbaar zijn voor de cursor. Indien niet gespecificeerd, wordt de ASENSITIEVE optie aangenomen.
De optionele [SCROLL | NOSCROLL] definieert het scrollvermogen van de cursor. Indien niet gespecificeerd, wordt de GEEN SCROLL-optie aangenomen.
De optionele [ WITH HOLD | WITHOUT HOLD] bepaalt of de transactie moet worden vastgehouden of automatisch moet worden gesloten wanneer de transactie vanwege de cursor wordt vastgelegd. Indien niet gespecificeerd, behoudt het de ZONDER HOLD-optie.
De optionele [ MET RETOUR | ZONDER RETURN] bepaalt of de cursorresultaatset naar de aanroeper moet worden geretourneerd, zoals een andere SQL-routine of hosttaal. Indien niet gespecificeerd betekent dit ZONDER RETOUR.
De ORDER BY-component wordt gebruikt om het geretourneerde zoekresultaat te sorteren volgens de opgegeven sorteertechniek.
De optie UPDATE verwijst naar het gebruik van de UPDATE- of DELETE-instructie die wordt geassocieerd met de rijen die worden geretourneerd door de SELECT-instructie van de cursor. Een dergelijke wijziging is niet mogelijk als we de optie ALLEEN LEZEN specificeren. Indien niet gespecificeerd, wordt standaard de UPDATE-optie aangenomen.
Daarom kan een eenvoudige cursor als volgt worden gedeclareerd:
DECLARE mycursor CURSOR FOR SELECT emp_no, first_name, last_name, birth_date FROM employees WHERE MONTH(birth_date) = MONTH(CURRENT_DATE) AND DAY(birth_date) = DAY(CURRENT_DATE);
Cursors in MySQL
Meestal zijn er twee soorten cursors in MySQL:alleen-lezen en alleen vooruit-cursors. Deze cursors kunnen worden gebruikt voor MySQL-opgeslagen procedures. Deze cursors helpen ons om de zoekresultaten rij voor rij te herhalen en in variabelen op te halen voor verdere verwerking. Het is mogelijk om meer dan één cursor te declareren en deze in lussen te nesten. Merk op dat cursors alleen-lezen zijn omdat ze worden gebruikt om tijdelijke tabellen te doorlopen. De cursor voert de zoekopdracht meestal uit terwijl we deze openen.
Een van de problemen met cursor in MySQL is dat ze de prestaties van de query kunnen vertragen vanwege extra I/O-bewerkingen die ze uitvoeren. Dit geldt met name voor echte grote gegevenstypen zoals BLOB en TEXT. Omdat cursors werkt met tijdelijke tabellen, worden deze typen niet ondersteund in tabellen in het geheugen. Daarom moet MySQL tijdens het werken met deze typen tijdelijke tabellen op schijf maken en dat vereist veel I/O-bewerkingen en dat ook op langzame apparaten zoals schijven. Dit is de belangrijkste reden voor de trage prestaties van de cursor.
MySQL ondersteunt ook geen cursors aan de clientzijde, maar de client-API kan ze indien nodig emuleren. Maar dan is dit niet veel anders dan het resultaat ophalen in een array in programmeertaal zoals Java en ze daar in plaats daarvan manipuleren.
Hier is een voorbeeld van het schrijven van cursors in MySQL.
CREATE PROCEDURE 'cursor_demo'() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE id INT(11); DECLARE fn varchar(14); DECLARE ln varchar(16); DECLARE bdate date; DECLARE mycursor CURSOR FOR SELECT emp_no, first_name, last_name, birth_date FROM employees WHERE MONTH(birth_date)=MONTH(CURRENT_DATE) AND DAY(birth_date)=DAY(CURRENT_DATE); DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN mycursor; fetch_loop: LOOP FETCH mycursor INTO id, fn, ln, bdate; IF done THEN LEAVE fetch_loop; END IF; SELECT id, fn, ln, bdate; END LOOP; CLOSE mycursor; END
Roep de opgeslagen procedure als volgt aan:
mysql> CALL cursor_demo
De procedure haalt de rijen op uit een tabel met de naam employee wiens geboortedatum overeenkomt met de huidige dag en maand in een cursor met de naam mijncursor en drukt ze eenvoudig af met de SELECT-instructie.
Raadpleeg de MySQL-documentatie op Cursor voor meer informatie.
Conclusie
Cursors zijn niets anders dan verwijzingen naar de recordsets die door de SQL-query worden geretourneerd. De aanwijzer wijst doorgaans naar één rij tegelijk en kan in een lus worden doorlopen om afzonderlijke records op te halen. SQL wordt normaal gesproken gebruikt voor directe aanroep om toegang te krijgen tot gegevensobjecten en deze te maken. De cursors bieden de techniek van interactieve SQL, waarbij het ad-hoc uitvoeren van SQL-instructies mogelijk maakt via een clienttoepassing. Het mechanisme van de cursor maakt gebruik van het gegevenstoegangsmodel waarbij SQL-instructies zijn ingebed in de hosttaal zoals C, C++ of Java enz. Dit is slechts een glimp van waar de cursor allemaal mee begint. Raadpleeg de juiste SQL-databasedocumentatie voor details over specifieke normen van verschillende implementaties.
# # #