Induktiivsed andmebaasid ja päringud loomulikus keeles

Andmebaaside tähtsaim päringukeel SQL on mitmes suhtes üsna jäik:
- raskusi on rekursiivsete päringutega (SQL:1999 toetab vaid lieaarset rekursiooni, s.t. rekuriooni üle ühe muutuja), näiteks kõigi esivanemate või järglaste (need mõisted on rekursiivsed; näiteks järglased on lapsed ja juba leitud järglaste lapsed, vt loeng 1) võib tekkida probleeme;
- ei ole võimalik katkestada päringule vastuse otsimist mingite loogiliste tingimuste abil (nii nagu seda saab teha Prologis lõike, !-operatsiooni abil;
- universaalsuse kvantort ("leia vanemad, kelle kõik lapsed on pojad") ei ole ja seda peab simuleerima eksistentsi-kvantori ja eituse abil abil ("leia vanemad, kes ei kuulu nende vanemate hulka, kellel on vähemalt üks tütar), ja kui tarvis on mitut sisestatud universaalsuse kvantorit, läheb asi väga segaseks.
SQL standard ei ole vaba (selle peab ostma) ja enamus SQL-realisatsioonidest ei jälgi seda, vaid kasutab oma formaate ja süntaksit, sellepärast on SQL-is kirjutatud infosüsteemide ülekandmine ühest andmebaasist teise tavaliselt üsna töömahukas.

Andmebaaside kasutamine ja keerukate päringute tegemine muutub palju lihtsamaks, kui päringukeelena kasutada Prolog-i. Prologi jaoks on loodud vabalt mahalaaditavad (levitatakse "GNU General Public License" alusel), vt näit "PrologSQL: SWI-Prolog to SQL bridge", ) nn. sillad Oracle ja ODBS-süsteemi andmebaaside jaoks. Need impordivad/ekspordivad andmebaasisüsteemi tabelid, nii et Prologis need pistavad nagu tavalised faktid, mida saab lisada ja kustutada assert-, retract- lausete abil. Prolog-Oracle-ODBS silla loomiseks tuleb lihtsalt mahalaaditud paketi PrologSQL lahtipakkimisel tekkinud failid OracleProlog.dll, oracle.pl, ODBCProlog.dll, odbc.pl, PrologSQL.pl kopeerida kas töökataloogi või mõnesse Prologi otsimisteel olevasse kataloogi; seejärel saab kasutada andmebaasitabeleid nagu Prologi fakte.

Prologis saab teha ka nn meta-päringuid, mis SQL-is pole üldse võimalikud, näiteks otsida attribuute (s.t. attribuutide nimesid): "millistes perekondlikes suhetes mingi isik üldse on?". Prologis on see sisseehitatud predikaati =.. kasutades lihtne. Kui näiteks isikute andmebaas on kirjeldatud tabeliga person[Isik, Sugu, Ema, Isa, Abikaasa]:

person('Henry VII', male, 'Margaret Beaufort', 'Edmund Tudor', 'Elizabeth of York').
person('James IV', male, 'Elizabeth of York', 'Henry VII', 'Margaret Tudor').
person('Henry VIII', male, 'Elizabeth of York', 'Henry VII', 'Catherine of Aragon').
person('Mary', female, 'Elizabeth of York', 'Henry VII', 'Charles Brandon').
person('James V', male, 'Margaret Tudor', 'James IV', 'Mary of Guise').

ja isikute vahel on määratud suhted

relations([parent, wife, husband, ancestor, descendent, full_sibling,
half_sibling, sibling, sister, brother, step_sibling, uncle,
aunt, mother, father, child, son, daughter, step_parent,
step_child, step_mother, step_father, step_son, step_daughter,
nephew, niece, cousin, grandmother, grandfather, grandparent,
grandson, granddaughter, grandchild]).

siis predikaat
relation(R, X, Y) :-
relations(Rs),
member(R,Rs),
Q =.. [R,X,Y],
call(Q).

võimaldab leida kõik suhted, mis mingit isikut seovad. Kui defineerida suhe parent järgmiselt:

parent(Person, Parent):-
person(Person,_,Parent,_,_);person(Person,_,_,Parent,_).

saame leida kõik isiku lapsed:

?- relation(parent,Child,'Henry VII').

Y = 'James IV'
Y = 'Henry VIII'
Y = 'Mary'

Päringus kasutatud suhet muutes saab sama päringuga leida ka isiku esivanemad jne.

Predikaadi =.. abil on lihtne koostada ka süsteemi, mis vastaks loomulikus keeles esitatavatele SQL-stiilis päringutele. Mingi objekti (näiteks "kaup") kirjeldus andmebaasina on tabel, mille ridadeks on erinevad seda tüüpi objektid (erinevad kaubaliigid) ja veergudeks on vastavate objektide (kaubaliikide) omadused: nimi, kood, hind, asukoht (ladu) jne, seega kaupu võiks iseloomustada tabel

Predikaadi =.. abil on lihtne koostada ka süsteemi, mis vastaks loomulikus keeles esitatavatele SQL-stiilis päringutele. Mingi objekti (näiteks " kaup ") kirjeldus andmebaasina on tabel, mille ridadeks on erinevad seda tüüpi objektid (erinevad kaubaliigid) ja veergudeks on vastavate objektide (kaubaliikide) omadused (atribuudid): nimi, kood,hind, asukoht (ladu) jne, näiteks kaupu võiks iseloomustada tabel

kaup

Ese

Kood

Ladu

Hind

Jalgratas

JR-213

Tihniku

3500.00

Paat

SP-21

Aiaäärne

5300.00

ja kauplusi - tabel "kauplus" (attribuutidega Nimi, Asukoht, Telefon jne).

Päring iseloomustab, mida soovitakse teha ("näita", "trüki"), millist tüüpi objekte soovitakse saada (kaup, kauplus) ja millist tingimust need objektid peavad rahuldama ("ladu = tihniku", "hind < 5000"). Operatsioone sisaldavatel avaldistel on tõeväärtus ("Tihniku = Aiaäärne" on väär ja "500 < 5300" - töene), siis võib päringu lõpuosas olev tingimus koostatud sellistest elementaartingimustest loogiliste operaatorite &, V jne abil, kuid esituse lihtsustamiseks neid siin ei kasutata. Päringul võib olla ka modifikaator: kas soovitakse saada kõiki antud tingimust rahuldavaid objekte, ainult ühte (esimest), milliseid tingimust rahuldava objekti attribuute soovitakse saada (vaikimisi esitatakse siin vaid esimene) jne, seega on andmebaasi päringul järgmised komponendid :

käsk modifitseerija objekt attribuut operaator väärtus

Sellise struktuuriga päringud oleks näiteks

"näita kõik kaup ladu = tihniku"
"näita esimene kaup hind > 5000"
"näita telefon kauplus nimi=raudteepood"

Sellistele päringutele vastuse leidmiseks tuleb koostada Prologi predikaadid

käsk(Modifitseerija, Objekt, Attribuut, Operaator, Väärtus).

Loomulikus keeles antud sisendi (lõpus punkt) teisendab selles esinevate sõnade, matemaatiliste märkide (>, =) ja arvude nimistuks Swi-Prologi moodulis readln kirjeldatud predikaat readln(Sõnad) (sellel on palju lisavõimalusi: saab määrata, milliste sümbolitega võib rida lõppeda, kas teisendada sisend suurtähtedeks jne, vt mooduli readln.pl kommentaare), seega programm algab reaga:

:- use_module(library(readln)).

Tavaliselt tehakse andmebaasipäringuid järjest mitu, sellepärast on predikaat päringrekursiivne ja käivitab pärast iga päringut end uuesti; kui kasutaja soovib lõpetada, sisestab ta sõna lopp (kuna täpitähtede kasutamisel teevad paljud süsteemid veel vigu, on päringutes kõik täpitähed asendatud vastavate täppideta tähtedega). Predikaat päring loeb predikaadiga readln kasutaja sisestatud päringu, eemaldab readlnpoolt loodud nimistu lõpust punkti (aatomi '.') ja kontrollib, kas tegemist on lõpetuskäsuga lopp. Kui ei, moodustakse päringusõnade nimistust Prologi predikaat ja käivitatakse see Prologi süsteemipredikaadiga call.

päring:-
readln(Sõnad0),
eemalda_punkt(Sõnad0,Sõnad),
(Sõnad = [lopp],! ;
töötle(Command,Sõnad),
call(Command),
päring).

Päringu sisestamise võib lõpetada kas punktiga või reavahetusega. Predikaat readln esitab sisestatud punkti ka loetud nimistu viimase elemendina. Nimistu lõpust punkti '.' eemaldamiseks on kõige lihtsam keerata nimistu tagurpidi; kui punkti pole, nimistut ei muudeta:

eemalda_punkt(L,L1):-
reverse(L,['.'|LL]),!,
reverse(LL,L1).

eemalda_punkt(L,L).

Enamuse programmeerimissüsteemide suureks puuduseks on nende äärmine jäikus - pisemgi viga katkestab tavaliselt programmi töö. Eri programeerimissüsteemide süntaksi meelespidamine on raske, eriti kui erinevates süsteemides kasutatakse (sisuliselt) samade asjade jaoks erinevaid märke ja sõnu (kas tsükkel algas "for ..." või "step..." ?) Siin on päringukeel muudetud paendlikumaks alternatiivide lubamisega:naita asemel võib öelda ka esitle või loetle, yks asemel võib võib öeldamingi, esimene, one jne. Predikaadid käsk, modifier, attribut, operaator, value asendavad need sünonüümid nn "normaalkujudega" - nendega, mille abil on koostatud predikaadid käsk(Modifitseerija, Objekt, Attribuut, Operaator, Väärtus)., mis annavad päringu vastuse.

töötle(C,Wordlist):-
get_command(L,Wordlist,[]),
C=..L.

get_command([K,M,A,Op,V]) -->
käsk(K),
modifier(M),
attribuut(A),
operaator(Op,Tyyp), %muutuja Tyyp kontrollib, et operaatorit rakendatakse õiget tyypi väärtusega!
value(V,Tyyp).

käsk(naita,[naita|X],X).

käsk(naita,[loetle|X],X).

käsk(naita,[esitle|X],X).

käsk(naita,[anna|X],X).

modifier(koik,[koik|X],X).

modifier(koik,[all|X],X).

modifier(yks,[yks|X],X).

modifier(yks,[esimene|X],X).

modifier(yks,[mingi|X],X).

modifier(A,[A|X],X):-attribuut(A).

attribuut(A,[A|X],X):-attribuut(A).

attribuut(A):-
member(A,[ese,kood,ladu, hind]).

Attribuudi väärtused on alati mingit tüüpi - kas arvud (Hind) või tekstistringid (Ladu); vastavalt attribuudi väärtuse tüübile on neile kasutatavad erinevad operaatoreid, näiteks stringe saab võrrelda (binaarne operaator "=") ja leksikograafiliselt järjestada (binaarne operaator"<"); arvudele saab kasutada samuti operatsioone "=", ">" jne, kuid siin nende tähendus (semantika) on hoopis teistsugune; samuti peavad mõlemad operatsiooni argumendid olema sama tüüpi (kas tekstistringid või numbrid). Operaatori ja sellele järgneva väärtuse ühtesobivust kontrollib muutuja Tyyp. Mõnd operaatorit (=, "on") saab kasutada nii arvude kui ka tekstiliste väärtustega, s.t. lubatud on nii "ladu = tihniku", "ladu on tihniku" (tekstilised väärtused) kui ka "hind = 300" ja "hind on 300" (arvväärtused):

operaator(gt,arv,['>'|X],X).

operaator(gt,arv,[suurem|X],X).

operaator(gt,arv,[suurem, kui|X],X).

operaator(eq,arv,[=|X],X).

operaator(eq,tekst,[=|X],X).

operaator(eq,tekst,[on|X],X).

operaator(eq,arv,[on|X],X).

value(V,arv,[V],[]):-number(V).

value(V,tekst,[V],[]):-string(V);atom(V).


Ülesandeid:
1. Täienda ülalesitatud programmi nii, et saaks kasutada mitut erineva struktuuriga andmebaasi tabelit
2. Koosta predikaat näita nii, et selle kirjelduses attribuut ja operaator oleksid muutujad, s.t. et iga attribuudi (ese, kood, ladu, hind) ja iga päringus kasutatud operaatori (>, =, >= jne) jaoks ei peaks koostama "oma" päringule vastust otsivat predikaati.
3. Täienda päringu struktuuri nii, et selles kasutatavad sõnad ei peaks tingimata olema nimetavas ja võiks kasutada ka nn. "täitesõnu" - sõnu, mille lisamine/ärajärmine tulemust ei mõjusta (selline on sõna kui ülaltoodud väljendites suurem kui), näiteks oleks lubatud (ja ülaltoodutega samaväärsed) päringud:
naita koik kaubad mille hind on suurem kui 3000 krooni
% mille, on, krooni - täitesõnad


Küsimused, probleemid: ©2004 Jaak Henno