Sunteți pe pagina 1din 62

BAZE DE DATE 1

ÎN VIZUAL FOXPRO

1.1. Elemente de teoria b azelor de date 7

Evoluţia metodelor şi tehnicilor de organizare a datelor a fost determinată de necesitatea de a avea un


acces cât mai rapid şi mai uşor la un volum din ce în ce mai mare de informaţii.
În esenţă conceptul de bază de date 1 poate fi definit ca fiind una sau mai multe colecţii de date aflate
în interdependenţă, împreună cu descrierea datelor precum şi a relaţiilor dintre acestea. Baza de date astfel
definită trebuie să îndeplinească următoarele condiţii:
• să asigure o independenţă sporită a datelor faţă de programe şi invers;
• structura bazei de date trebuie să asigure informaţiile necesare şi suficiente pentru cerinţele de
informare şi decizie;
• să asigure o redundanţă minimă şi controlată a datelor;
• să permită accesul rapid la informaţiile memorate.
Bazele de date sunt extrem de variate, existând diferite criterii de clasificare, şi anume:
• după orientare: generalizate, specializate;
• după modelul de date: ierarhice, reţea, relaţionale, orientate obiect;
• după amploarea geografică: locale, distribuite;
• după limbajele utilizate: autonome (limbaje proprii), cu limbaj gazdă, mixte.
Arhitectura bazelor de date evidenţiază componentele acestora, cuprinzând în general următoarele :
• baza de date propriu-zisă în care se memorează datele;
• un dicţionar al bazei de date (metabaza de date), ce conţine informaţii despre date, structura
acestora, elementele de descriere a semanticii, statistici, documentaţie, etc.;
• sistemul de gestiune al bazei de date, reprezentând ansamblul de programe cu ajutorul căruia se
realizează gestiunea şi prelucrarea complexă a datelor;
• un set de proceduri manuale şi automate destinate bunei funcţionări a întregului sistem;
• mijloacele hard utilizate;
• personalul implicat: deosebim următoarele categorii de utilizatori: finali (neinformaticieni), de
specialitate (administratori), analişti-programatori, gestionari, operatori.
Într-o bază de date, organizarea datelor poate fi analizată din mai multe puncte de vedere. De obicei,
abordarea efectuându-se pe trei nivele: fizic sau intern, conceptual sau global şi extern.
Nivelul fizic este un nivel de abstractizare al datelor elementar la care pot fi considerate datele,
reprezentând modalitatea efectivă în care acestea sunt memorate pe suportul infomaţional. Structura datelor
este descrisă detaliat, fiind accesibilă numai specialiştilor (ingineri de sistem, programatori în limbaj de
asamblare sau alte limbaje apropiate de “maşină”). La acest nivel, structura bazei de date se concretizează în
aşa-numita schemă internă.

7
Nivelul conceptual (sau global) este nivelul imediat superior celui fizic, datele fiind privite prin
prisma semanticii lor, punându-se accent pe conţinutul lor efectiv şi pe relaţiile care le leagă de alte date.
Întreaga bază de date este descrisă prin intermediul unui număr restrâns de structuri care, deşi relativ simple,
prin transpunerea la nivel fizic devin extrem de complexe. Toţi utilizatorii îşi exprimă cerinţele de date la
nivel conceptual, prezentându-le administratorului bazei de date, acesta fiind cel care are o viziune globală
asupra organizării bazei concretizată printr-o schemă conceptuală aferentă.
Nivelul extern este ultimul nivel de abstractizare la care poate fi descrisă o bază de date. El este dat de
viziunea programatorului de aplicaţii, care realizează programele de aplicaţii pentru manipularea datelor şi
structura logică (subschema) corespunzătoare descrierii datelor aplicaţiei. Dacă la nivel conceptual baza de
date era abordată în ansamblul ei, în practică, un utilizator sau un grup de utilizatori lucrează numai cu o
porţiune specifică a bazei de date, în funcţie de departamentul în care îşi desfăşoară activitatea şi atribuţiile
sale (lor). Pentru a simplifica interacţiunea utilizator-bază de date se recurge la nivelul extern, în care baza de
date se prezintă sub diferite machete, sub-scheme sau scheme externe specifice necesităţilor fiecăriu
utilizator sau grup de utilizatori.
Sistemul de gestiune al bazei de date reprezintă software-ul propriu-zis al acesteia asigurând
realizarea următoarelor activităţi:
• definirea structurii bazei de date;
• încărcarea datelor în baza de date;
• accesul la date (consultare, interogare);
• întreţinerea bazei de date (colectarea şi refolosirea spaţiilor goale, refacerea bazei de date în
cazul unui incident);
• reorganizarea bazei de date (restructurarea şi modificarea strategiei de acces);
• securitatea datelor.
Aşadar, sistemul de gestiune al bazei de date apare ca un sistem complex de programe care asigură
interfaţa între o bază de date şi utilizatorii acestuia. În acest context, sistemului de gestiune al bazei de date îi
revin o serie de obiective:
• asigurarea independenţei datelor: independenţa datelor trebuie privită din două puncte de
vedere: independenţa fizică a datelor ce face ca memorarea datelor precum şi tehnicile fizice de
memorare să poată fi modificate fără a determina rescrierea programelor de aplicaţie, iar
independenţa logică a datelor ce se referă la posibilitatea adăugării de noi articole de date sau
extinderea structuri conceptuale (globale), fără ca aceasta să impună rescrierea programelor
existente;
• asigurarea unei redundanţe minime şi controlate a datelor din baza de date: memorarea
datelor, în cazul bazelor de date, se face astfel încât fiecare dată să apară o singură dată; totuşi,
nu sunt excluse nici cazurile în care, pentru a realiza performanţe sporite, referitoare la timpul de
acces la date şi răspuns la solicitările uilizatorilor, să se accepte o anumită redundanţă a datelor,
în acest caz însă instituidu-se un control automat în vederea asigurării coerenţei datelor din bază;
• asigurarea unor facilităţi sporite de utilizare a datelor: aceasta presupune:
– folosirea datelor de către mai mulţi utilizatori în diferite scopuri;
– accesul cât mai simplu al utilizatorului la date;
– existenţa unor limbaje performante de regăsire a datelor;
– posibilitatea unui acces multicriterial, fără sortări suplimentare;
– utilizarea unui limbaj cât mai apropiat de limbajul natural, cu posibilitatea exploatării
în mod facil a bazei de date şi de către utilizatorii neinformaticieni;
• sporirea gradului de securitate a datelor împotriva accesului neautorizat la ele;
• asigurarea integrităţii datelor împotriva unor ştergeri intenţionate sau neintenţionate, prin
intermediul unor proceduri de validare, a unor protocoale de control concurent şi a unor
proceduri de refacere a bazei de date după incidente;
• asigurarea partajabilităţii datelor: partajabilitatea datelor trebuie înţeleasă nu numai sub
aspectul asigurării accesului mai multor utilizatori la aceleaşi date, ci şi acela al posibilitătii
dezvoltării unor aplicaţii fără a se modifica structura bazei de date.
Pentru realizarea obiectivelor enumerate mai sus, sistemele de gestiune a bazelor de date dispun de
o serie de componente ce permit efectuarea numeroaselor operaţii. Se pot astfel deduce câteva funcţii cu
caracter de generalitate pentru toate sistemele de gestiune a bazelor de date, şi anume:
• funcţia de descriere a datelor: permite definirea structurii bazei de date cu ajutorul limbajului
de definire; definirea datelor poate fi realizată la nivel logic, conceptual şi fizic; la nivelul acestei
funcţii se descriu multitudinea atributelor (câmpurilor) din cadrul structurii bazei de date,
legăturile dintre entităţile de date sau dintre atributele aceleaşi entităţi, se definesc eventualele
criterii de validare a datelor, metodele de acces la date, aspectele referitoare la asigurarea
integrităţii şi confidenţialităţii datelor, etc; rezultatul acestei funcţii va fi schema bazei de date,
memorate în cod intern;
• funcţia de manipulare a datelor este cea mai complexă funcţie şi realizează următoarele
activităţi:
– crearea (încărcarea) bazei de date;
– adăugarea de noi înregistrări (tupluri);
– suprimarea unor înregistrări;
– modificarea valorilor corespunzătoare unor câmpuri;
– căutarea, sortarea şi editarea parţială şi totală a unei înregistrări virtuale etc.; funcţia de
manipulare a datelor se realizează prin intermediul limbajului de manipulare a datelor;
• funcţia de utilizare asigură mulţimea interfeţelor necesare pentru comunicarea tuturor
utilizatorilor cu baza de date; în cadrul realizării acestei funcţii, apar mai multe categorii de
utilizatori:
– utilizatori conversaţionali: aceştia reprezintă categoria beneficiarilor de informaţii care
utilizează limbajele de interogare a bazei de date într-o formă simplistă;
– utilizatori programatori, care utilizează limbajele de manipulare, realizând proceduri
complexe de exploatare a bazei de date;
– administratorul bazei de date apare ca un utilizator special şi are rolul hotărâtor în ceea
ce priveşte funcţionarea optimă a întregului ansamblu;
• funcţia de administrare a bazei de date apare ca o funcţie complexă şi este de competenţa
administratorului acesteia.

1.1.1 Diagrama Entitate - Asociere


Nivelul conceptual este nivelul central care reflectă datele structurate astfel încât acestea să poată fi
preluate şi prelucrate cu ajutorul unui SGBD. În proiectarea bazelor de date, la nivel conceptual, se pot
utiliza mai multe modele, dintre care cel mai frecvent utilizat în ultima vreme este modelul entitate - asociere
(EA). Modelul EA 2 abstractizează lumea reală şi o transpune, pe de o parte, în angregări de date, numite
entităţi, iar pe de altă parte, în legături între entităţi, denumite corespondenţe, asocieri sau relaţii.
O entitate este un model de obiect identificat în lumea reală - material, imaterial (eveniment) sau
abstract - al cărui tip este definit de un nume şi de o listă de proprietăţi (atribuite). Un atribut se defineşte ca
fiind o proprietate a unei entităţi sau a unei corespondenţe, mulţimea realizărilor (valorilor) sale formând un
domeniu. Mulţimea formată din câte o valoare a fiecărui atribut al unei entităţi reprezintă o realizare
(instanţiere) a entităţii respective. Spunem că există o dependenţă funcţională între două atribute atunci
când unei valori a unei proprietăţi îi corespunde o singură valoare a altei proprietăţi. Identificatorul unei8
entităţi este atributul (sau grupul de atribute) a cărui realizare caracterizează în mod unic o realizare a

8
entităţii. Putem avea o dependenţă funcţională între identificatorii a două entităţi. O corespondenţă sau
asociere sau relaţie reprezintă o legătură logică între două sau mai multe realizări de entităţii. Rolul unei
entităţi este un nume care desemnează modul de participare al entităţii la o asociere. Identificarea asocierilor
se realizează prin rolurile entităţilor participante deci, concret, cu ajutorul identificatorilor entităţilor
respective. Mulţimea entităţilor care participă la o asociere formează colecţia acesteia, numărul acestora
reprezentând gradul sau dimensiunea asocierii. O corespondenţă între realizări diferite ale aceleeaşi
entităţi se numeşte asociere reflexivă, caz care se recomandă precizarea rolurilor fiecărei entităţi în cadrul
asocierii. O entitate poate participa la mai multe corespondenţe; în acelaşi timp, pot exista mai multe asocieri
între aceleaşi entităţi. Un subansamblu de entităţi ale aceluiaşi tip de entitate formează un subtip al entităţii
respective cu proprietăţi specifice.
În cadrul modelului EA de abstractizare a unui ansamblu de date trebuie să se ţină cont de
următoarele reguli:
• o asociere (relaţie) nu poate exista decât o singură dată între aceleaşi entităţi;
• numele entităţilor, corespondenţele (relaţiilor), rolurilor, atributelor trebuie să fie unice în cadrul
modelului conceptual, iar apoi în baza de date definită.

1.1.2 Modelul relaţional


Acesta a fost conceput şi dezvoltat de Codd, constituind un model formal de organizare conceptuală a
datelor, destinat reprezentării legăturilor dintre date, bazat pe teoria matematică a relaţiilor. Este modelul cel
mai accesibil pentru utilizatorul bazei de date, deoarece are aceeaşi structură fizică cu datele care trebuie
prelucrate. În general, datele se prezintă sub forma unor tabele (relaţii 3 ) în care liniile constituie entităţi, iar
coloanele atribute ce caracterizează aceste entităţi.
Spre deosebire de modelul ierarhic şi reţea unde apar două elemente, şi anume tipul entităţii şi relaţiile
dintre două entităţi, modelul relaţional este alcătuit numai din relaţii şi prin urmare, orice integrare asupra
bazei de date este tot o relaţie. Modelul relaţional a fost definit cu o deosebită rigoare matematică, furnizând
un mijloc performant de studiu al proprietăţilor logice ale unui sistem de baze de date.
Definirea unui SGBD relaţional impune analizarea caracteristicilor pe care trebuie să le prezinte un
model de date pentru a fi considerat relaţional. Dintre diferitele modalităţi de definire ale acestui concept,
exemplificăm cu:
• prezentarea datelor în tabele supuse anumitor operaţii: proiecţie, selecţie, reuniune, compunere,
intersecţie etc (definiţie simplă);
• un sistem de baze de date ce suportă un limbaj SQL - Structure Query Language (definiţie
practică);
• un sistem de baze de date care respectă principiile modelului relaţional introdus de Codd
(definiţia cea mai frecvent folosită).
Codd a publicat un set de 13 reguli, în raport cu care un SGBD poate fi apreciat ca fiind relaţional.
Ulterior, cele 13 reguli de fidelitate ale lui Codd au fost extinse la un număr de 100. Trebuie remarcat că nu
există un SGBD care respectă toate regulile definite de Codd. De fapt, nu trebuie să apreciem un SGBD ca
fiind relaţional sau nu, ci măsura în care acesta este relaţional.
Un model relaţional este caracterizat de trei elemente:
• structura relaţională a datelor
• operatorii modelului relaţional
• regulile de integritate care guvernează folosirea cheilor în model.
Prezentarea structurii datelor impune definirea noţiunilor de domeniu, relaţie, schemă relaţională,
valoarea null şi tabel view. Un domeniu 4 este o mulţime de valori care poate fi definită fie enumerând
elementele componente, fie de9finind o proprietate distinctă a domeniului valorilor. Fie D1, D2, ......., Dn
domenii finite, nu neapărat disjuncte. Produsul cartezian D 1 x D2 x ......x Dn al domeniilor D1, D2, ...., Dn este
definit de mulţimea tuplurilor (V1,V2,.....,Vn) , unde V1є D1, …, Vn є Dn. O relaţie R pe mulţimile D1,
D2,....., Dn este o submulţime a produsului cartezian D1 x D2 x ......x Dn, deci o mulţime de tupluri. Relaţia
poate fi definită şi ca o mulţime finită de funcţii R = {f1, f2 ,...., fm} cu fj : {A1, A2,..., An} → D1 ∪ D2
∪ ....... ∪ Dn şi fj (Ai) є Di , în care asociem fiecărui domeniu Di un atribut Ai. Mulţimea numelor atributelor
corespunzătoare unei relaţii o numim schemă relaţională şi o notăm cu R (A1, A2,....., An). Putem reprezinta
o relaţie printr-un tabel bidimensional în care fiecare linie corespunde unui tuplu, iar fiecare coloană unui
domeniu din produsul cartezian, de fapt unui atribut. Numele atributului exprimă de obicei semnificaţia
valorilor din calcul coloanei respective. Numărul atributelor defineşte gradul relaţiei, iar numărul de tupluri
din relaţie defineşte cardinalitatea relaţiei.

FORMAL UZUAL FIZIC


relaţie tabel Fişier
tuplu linie Inregistrare
atribut coloană câmp
domeniu tip de dată tip de dată

Fig. 1.1 Concepte de bază

Atunci când inserăm tupluri într-o relaţie, de multe ori un atribut este necunoscut sau neaplicabil.
Pentru a reprezenta acest atribut a fost introdusă o valoare convenţională în relaţie, şi anume valoarea null.
În ceea ce priveşte un tabel de tip view (vizualizare, filtru, relaţie virtuală) acesta reprezintă o filtrare a
tabelului iniţial necesară unei anumite abordări, unei anumite aplicaţii. Spunem că un tabel de tip view
reprezintă o relaţie virtuală, deoarece datele pe care le conţine nu sunt în realitate memorate, fiind stocată
numai definiţia vizualizării. Utilizarea vizualizărilor este, în anumite situaţii, avantajoasă, asigurând
securitatea tabelului iniţial, care este astfel protejat de ştergeri, modificări, etc., dar prezintă şi limitări în
prelucrarea datelor.
Operatorii modelului relaţional definesc operaţiile care se pot efectua asupra relaţiilor, în scopul
realizării funcţiilor de prelucrare asupra bazei de date. Modelul relaţional oferă două mulţimi de operatori pe
relaţii şi anume: algebra relaţională şi calculul relaţional. Operatorii algebrei relaţionale sunt fie operatori
tradiţionali pe mulţimi (UNION, INTERSECT, PRODUCT, DIFFERENCE), fie operatori relaţionali
speciali (PROJECT, SELECT, JOIN, DIVISION). Calculul relaţional este de două tipuri : orientat pe tupluri
sau orientat pe domenii, reprezentând o adaptare a calculului predicatelor la domeniul bazelor de date. Pe
baza unor predicate iniţiale, prin aplicarea unor operatori ai calculului cu predicate (conjuncţia, disfuncţia,
10
negaţia, cuantificatorul existenţial şi cel universal) se pot defini noi predicate, noi relaţii. Conform
echivalenţei dintre algebra relaţională şi calculul relaţional demonstrate de Ullmann, orice relaţie posibil de
definit în algebra relaţională poate fi definită şi în cadrul calculului relaţional, şi reciproc.

10
Regulile de integritate sunt aserţiuni pe care datele conţinute în baza de date trebuie să le satisfacă.
Trebuie făcută distincţia între regulile structurale care sunt inerente modelării datelor şi regulile de
funcţionare (comportament) care sunt specifice unei aplicaţii particulare. Există trei tipuri de constângeri
structurale (de cheie, de referinţă, de entitate) ce constituie mulţimea minimală de reguli de integritate pe
care trebuie să le respecte un SGBD relaţional şi care sunt definite în raport cu noţiunea de cheie a unei
relaţii. O mulţime minimală de atribute ale căror valori identifică un tuplu unic într-o relaţie reprezintă o
cheie pentru relaţia respectivă. Fiecare relaţie are cel puţin o cheie de identificare. Dacă există mai multe
chei de identificare, atunci acestea se numesc chei candidat. Una dintre cheile candidat va fi aleasă de către
administratorul bazei de date pentru a identifica efectiv tupluri şi ea va primi numele de cheie primară, iar
celelalte chei candidat vor purta numele de chei alternative sau alternate. Modelul relaţional serveşte la
reprezentarea entităţilor din lumea reală şi a asocierilor dintre acestea. Modelarea asocierilor dintre entităţi
impune recurgerea la conceptul de cheie externă. Aceasta reprezintă un atribut/grup de atribute dintr-o
relaţie R1 ale cărui/căror valori sunt definite pe acelaşi domeniu/aceleaşi domenii ca şi cheia primară a unei
alte relaţii R2 şi care are rolul de a modela asocierea dintre entităţile reprezentate prin relaţiile R1 şi R2. În
acest context, R1 este denumită relaţie care referă, iar R2 poartă numele de relaţie referită.
Cu aceste considerente, cele trei reguli de integritate structurală se referă la :
• unicitatea cheii: cheia primară trebuie să fie unică şi minimală;
• integritatea entităţii: atributele cheii primare trebuie să fie diferite de valoarea null;
• integritatea referirii: o cheie externă trebuie ori să fie null în întregime, ori să corespundă la o
valoare a cheii primare asociate.

1.1.3 Caracteristicile unui SGBD relaţional


Deoarece regulile lui Codd sunt prea severe pentru a fi respectate de un SGBD relaţional, s-au
formulat criterii minimale de definire a unui sistem relaţional.
Un SGBD este minimal relaţional dacă satisface următoarele condiţii:
 Toate datele din cadrul bazei de date sunt reprezentate prin valori în tabele.
 Nu există pointeri observabili de către utilizatori.
 Sistemul suportă operatorii relaţionali de proiecţie, selecţie şi compunere naturală, fără
limitări impuse din considerente interne.
Un SGBD este complet relaţional dacă este minimal relaţional şi satisface în plus următoarele
condiţii:
 Sistemul suportă restricţiile de integritate de bază (unicitatea cheii primare, constrângerile de
referinţă, integritatea entităţii).
 Sistemul suportă toate operaţiile de bază ale algebrei relaţionale.
Un SGBD relaţional îndeplineşte funtiile unui SGBD, cu anumite particularităţi care decurg din
concepţia de organizare a datelor, respectiv din modelul relaţional. Fiecare SGBD relaţional implementează
modelul relaţional într-o manieră proprie care îl diferenţiază de restul sistemelor relaţionale.
Caracterizarea unui SGBD relaţional se poate realiza la nivelul clasei SGBD-urilor relaţionale, în
sensul caracterizării globale, unitare în raport cu celelalte tipuri de SGBD-uri, sau la nivelul unui SGBD
relaţional individual în sensul caracterizării particularităţilor sale, în raport cu alte SGBD-uri relaţionale.
Realizarea funcţiilor SGBD-urilor relaţionale se face cu ajutorul unor mecanisme de lucru specifice,
care le separă de sistemele nerelaţionale. Dintre instrumentele şi mecanismele de lucru de care dispune un
SGBD relaţional se disting:
 un limbaj relaţional pentru descrierea datelor la nivel fizic, logic şi conceptual;
 un limbaj relaţional pentru manipularea datelor;
 mecanisme pentru controlul integrităţii semantice a datelor;
 mecanisme pentru optimizarea cererilor de date;
 mecanisme pentru asigurarea coerenţei datelor în condiţiile accesului la date;
 utilitare pentru generarea de rapoarte şi de etichete, utilitare pentru generarea de aplicaţii,
utilitare pentru generarea unor statistici referitoare la starea şi activitatea bazei de date, etc.

1.1.4 Normalizarea relaţiilor


Codd a arătat că intr-o anumită formă relaţiile posedă proprietăţi nedorite, pe care le-a numit
anomalii de actualizare:
 anomalia de ştergere constă în faptul că anumite date care urmează să fie şterse, fac parte din
tupluri în care se găsesc şi alte date care mai sunt necesare în continuare, ori ştergerea făcându-se
la nivelul tuplului, acestea se pierd;
 anomalia de adăugare constă în faptul că anumite date care urmează să fie adăugate fac parte
din tupluri incomplete (pentru care nu se cunosc toate datele), ceea ce face ca acestea să nu poată
fi adăugate;
 anomalia de modificare rezultă din faptul că este dificil de modificat o valoare a unui atribut
atunci când ea apare în mai multe tupluri ale relaţiei.
Pentru a înlătura aceste anomalii, Codd a stabilit trei forme normale pentru relaţii şi a introdus
procesul de normalizare care se bazează pe noţiunea de dependenţă funcţională ca relaţie între atributele unei
entităţi ce are un caracter invariant.
Procesul de normalizare a relaţiilor se realizează în mai mulţi paşi, începând cu forma normală unu
(FN1) şi ajungând (după ultimele cercetări) la forma normală cinci (FN5). Teoria normalizării se ocupă cu
îmbunătăţirea succesivă a schemei conceptuale, find satisfăcute, în acelaşi timp, următoarele condiţii:
 conservarea datelor, adică în schema conceptuală finală să existe toate datele din cadrul schemei
iniţiale;
 conservarea dependenţelor dintre date, adică să se păstreze tipurile de relaţii dintre entităţi;
 descopunerea minimală a relaţiilor iniţiale, adică în schema conceptuală finală nici o relaţie nu
trebuie să fie conţinută într-alta.
O relaţie este în forma normală 1 (FN1), dacă şi numai dacă toate atributele ei conţin numai valori
atomice. În plus, un tuplu nu trebuie să conţină atribute sau grupuri de atribute repetitive. Este forma de bază
a relaţiilor, care figurează ca cerinţă minimală la majoritatea sistemelor de gestiune a bazelor de date
relaţionale.
O relaţie este în a doua formă normală FN2, dacă este în FN1 şi oricare dintre atributele non-cheie
este dependent funcţional complet de cheia primară a relaţiei.
O relaţie este în forma normală 3 (FN3), dacă se găseşte în FN2 şi toate atributele non-cheie sunt
dependente tranzitiv de cheia primară.
În afara acestor trei forme normale au fost introduse şi formele FNBC (forma normală Boyce –
Codd), FN4 şi FN5 care conduc la diminuarea redundanţei în baza de date. Trecerea de la forma FN3 la
FN4 şi FN5 se face operând asupra cheilor compuse între ale căror atribute apar relaţii care generează
dependenţe ce nu sunt funcţionale, numite dependenţe multi-valoare. O relaţie este în forma normală FNBC,
dacă fiecare determinant (acesta este un atribut sau o mulţime de atribute neredundante care constituie un
identificator unic pentru alt atribut sau altă mulţime de atribute ale unei relaţii) este o cheie candidat.
A patra formă normală FN4 elimină redundanţele datorate relaţiilor de tip n:m, iar a cincea formă
normală FN5 este mai mult „academică“ şi apare destul de rar în practică.
1.2. Crearea bazelor de date
În Visual FoxPro o bază de date are asociat un fişier special (.DBC) în care sunt memorate date
referitoare la baza de date în ansamblul ei, cum ar fi: tabelele componente, relaţiile permanente între tabele,
dicţionarul de date asociat bazei de date, etc. În acest fel, între date şi programele de prelucrare se interpune
un nou nivel, care asigură independenţa bazelor de date faţă de programele de prelucrare.
Un program care prelucrează o bază de date relaţională citeşte mai întâi informaţiile înscrise în fişierul
.DBC asociat acesteia şi numai apoi, după ce cunoaşte structura complexă a bazei de date, trece la
prelucrarea efectivă a datelor din tabele.
O dată cu introducerea în Visual FoxPro a noului concept de bază de date, o tabelă clasică a fost
îmbunătăţită cu o serie de caracteristici suplimentare, care sunt disponibile numai dacă tabela respectivă este
inclusă într-o bază de date. Aceste facilităţi suplimentare se manifestă la nivelul câmpurilor componente sau
la nivelul tabelei în ansamblul ei. Prin urmare, în Visual FoxPro se disting două tipuri de tabele:
♦ cele clasice, simple (din versiunile FoxPro pentru DOS şi WINDOWS)
♦ cele incluse în baze de date (tipice pentru Visual FoxPro).
În vederea construirii unei baze de date trebuie parcurşi următorii paşi:
• mai întâi se construieşte un fişier bază de date nou, în care vor fi memorate datele referitoare la
baza de date şi la tabelele componente;
• apoi se încorporează pe rând tabelele simple create anterior sau se construiesc tabele noi, direct
încorporate în noua bază de date;
• pentru tabele simple urmează specificarea acelor caracteristici noi, care nu se puteau declara la
construirea lor ca tabele izolate;
• se stabilesc legăturile, relaţiile între tabele;
• se pot preciza o serie de caracteristici noi ale bazei de date în ansamblul ei;
• se pot construi şi incorpora în baza de date o serie de elemente speciale, precum vederile,
conexiunile, etc.
Fig. 1.2 Crearea unei baze de date. Definirea structurii unei tabele componente

1.2.1 Stabilirea structurii unui tabel şi a caracteristicilor


acestuia
Fiecare câmp al unei tabele este caracterizat prin:
• nume (Name): reprezintă identificatorul prin care se face referire la câmpul respectiv, la datele
memorate în acesta;
• tip (Type): tipul datelor care pot fi memorate în câmpul respectiv; acesta poate fi: şir de
caractere, numeric de diferite feluri, dată calendaristică sau moment de timp, logic, „memo”, sau
„general”:
• lungime (Width): reprezintă numărul de caractere pe care îl ocupă fiecare dată memorată în
câmpul respectiv;
• numărul de zecimale (Decimal): caracteristică valabilă în cazul câmpurilor numerice, care
indică numărul de cifre de după punctul zecimal memorate în câmp; lungimea câmpului,
precizată anterior, trebuie să includă şi această valoare precum şi punctul zecimal propriu-zis;
• fanionul de indexare (Index): indică dacă se stabileşte un index pe baza câmpului respectiv;
poziţionarea acestui indicator implică stabilirea câmpului ca şi cheie de indexare şi determină
crearea unui tag (etichetă index) cu acelaşi nume ca şi câmpul, în cadrul fişierului index
structural asociat tabelei;
• fanionul de valoare nulă (NULL): arată dacă în câmpul respectiv poate fi memorată sau nu o
valoare nulă; această caracteristică a fost introdusă datorită necesităţii diferenţierii între un câmp
lăsat necompletat de utilizator şi unul completat cu valoarea 0.

TIP CÂMP VALORI ADMISE


Character şiruri de caractere cu lungime între 1 şi 254 caractere
Familia tipurilor numere întregi sau reale:
numerice - Integer (4 octeţi): valori întregi din intervalul
[-2147483647, +2147483646];
- Double (8 octeţi): valori reale în dublă precizie din intervalele
[-4.94065645841247·10-324, +4.94065645841247·10-324]
[–8.9884656743115 ·10307, +8. 9884656743115 ·10307]
- Numeric şi Float: tipuri numerice standard; zona de memorie
alocată pentru un câmp de acest tip este dependentă de lungimea
declarată de utilizator la crearea tabelei respective (de la 1 la 20
de octeţi); pentru fiecare cifră sistemul alocă în tabelă 1 octet; se
pot reprezenta valori din intervalul
[– 0.9999999999 ·1019, 0.9999999999·1020]
Currency tipul monetar folosit pentru memorarea valorilor exprimate în bani; zona
de memorie alocată are dimensiunea de 8 octeţi; valorile de acest tip
trebuie să se încadreze în intervalul
[– 922337203685477.5808 , 922337203685477.5807]
Logical valori logice (1 octet)
Date valori de tip dată calendaristică (8 octeţi)
DataTime valori de tip dată calendaristică urmate de marci de timp (8 octeţi)
General acest tip de date permite memorarea în tabele a unor elemente create cu
ajutorul altor programe, cum ar fi documente, foi de calcul tabelar,
imagini, etc., utilizându-se tehnologia OLE de incorporare şi de legare a
obiectelor (4 octeţi)
Memo acest tip de date se foloseşte pentru prelucrarea textelor, adică a şirurilor
de caractere de dimensiuni mari sau variabile, în câmpul de tip „memo”
memorându-se un indicator spre textul salvat într-un fişier distinct de
tipul .FTP (4 octeţi)

Pe lângă aceste caracteristici, pentru tabele incluse într-o bază de date se pot efectua şi următoarele
precizări pentru câmpurile din structura lor:
• pentru fiecare câmp se poate stabili un format implicit de afişare a datelor (Display → Format);
• pentru fiecare câmp în parte se poate stabili o machetă pentru introducerea datelor (Display →
Input Mask);
• se poate stabili un titlu nou pentru fiecare câmp; acesta va apare în antetul vizualizării
conţinutului tabelei (Display → Caption);
• o valoare iniţială implicită pentru fiecare câmp; la adăugării unei noi înregistrări, câmpurile
acesteia vor fi iniţializate cu aceste valori implicite (Field Validation → Default value);
• pentru fiecare câmp se poate impune o regulă de validare a valorilor (Field Validation → Rule)
şi eventual un mesaj care să fie afişat pe ecran în cazul nerespectării condiţiei de validare impuse
(Field Validation → Message); regula de validare la nivel de câmp constă dintr-o expresie
logică, ce va fi evaluată la orice modificare a valorii câmpului respectiv; dacă valoarea rezultată
în urma evaluării expresiei de validare este false, atunci noua valoare a câmpului nu va fi
acceptată şi se va afişa un mesaj de eroare standard sau unul specificat de utilizator.
La nivelul tabelei în ansamblu (figura 1.3), pot fi precizate: o regulă de validare la nivel de
înregistrare (la modificarea unei înregistrări) şi, eventual, un mesaj de eroare, în cazul nerespectării acestei
reguli, precum şi secvenţe de cod care să fie executate la inserarea unei noi înregistrări, la actualizarea
(modificarea) sau la ştergerea uneia existente. Validarea la nivel de înregistrare (Record Validation → Rule)
se declanşează după cea la nivel de câmp, dar înaintea secvenţelor de cod asociate evenimentelor de
manipulare a tabelei.

Fig. 1.3 Facilităţi suplimentare la nivelul unei tabele în ansamblul ei


Adăugarea unei înregistrări, modificarea datelor dintr-o înregistrare existentă precum şi ştergerea unei
înregistrări a tabelei constituie trei evenimente de manipulare a datelor dintr-o tabelă inclusă într-o bază de
date. Pentru fiecare dintre aceste evenimente se poate specifica câte o secvenţă de cod care să fie executată la
apariţia acestuia. Secvenţele de cod asociate evenimentelor de manipulare a unei tabele se declanşează după
validările la nivel de câmp şi la nivel de înregistrare.

1.2.2 Indexarea tabelelor


Indexarea reprezintă o tehnică de ordonare logică a datelor dintr-o tabelă, după diferite criterii,
operaţie care însă nu afectează ordinea fizică a datelor din tabelă, ci doar modul în care acestea sunt văzute
de utilizator.
Ordonarea unei tabele presupune stabilirea unui criteriu după care să fie parcurse înregistrările ei.
Acest criteriu poate fi un câmp sau o combinaţie de câmpuri ale tabelei. Criteriul de ordonare reprezintă de
fapt o expresie, numită cheie de ordonare sau de indexare, în alcătuirea căreia intră câmpuri ale tabelei.
Pentru stabilirea ordinii înregistrărilor tabelei se evaluează expresia cheii de ordonare pentru fiecare
înregistrare în parte, valorile obţinute se compară între ele, iar ordinea înregistrărilor în tabelă se stabileşte în
funcţie de rezultatul obţinut.
Există două tipuri de ordonări ale unei tabele:
• fizică, numită şi sortare, ce constă în rearanjarea fizică a datelor din tabelă într-o altă ordine,
dată de criteriul respectiv; în urma sortării rezultă o nouă tabelă, cu aceeaşi structură ca şi cea de
bază, dar cu înregistrările aranjate în ordinea dorită;
• logică, numită şi indexare, care constă în construirea unui fişier special, folosit la regăsirea
datelor din tabelă în ordinea dorită; prin indexare, ordinea fizică a înregistrărilor din tabelă nu se
modifică, se schimbă însă modul în care utilizatorul are acces la datele acesteia.
În mod simplificat, putem considera tabela index formată din cheia de indexare şi un număr de
înregistrare (pointer) din tabela de date.
Fie tabela “date_pers” din figura nr. 1.2 pentru care s-a stabilit câmpul “nr_leg” ca şi cheie de
indexare. Pe baza valorilor, în ordine crescătoare, a acesteia, s-a construit tag-ul (eticheta index) “nr_leg” al
fişierului index structural “date_pers.CDX” asociat tabelei. “Prima” înregistrare a tabelei indexate o
constituie cea corespunzătoare celei mai mici valori a cheii de indexare, iar “ultima” înregistrare a fişierului
indexat este cea care conţine cea mai mare valoare a cheii de indexare (figura 1.4).
O tabelă poate avea mai mulţi indecşi asociaţi, dar numai unul singur va fi activ la un moment dat.
Ordinea în care va fi parcursă tabela este dată de indexul activ.
În Visual FoxPro, prin indexarea unei tabele, se crează în mod automat o etichetă index în cadrul
fişierul index structural asociat tabelei (.CDX). Prin comenzi explicite de indexare, se poate solicita crearea
unui index simplu (.IDX) sau a unui index compus nestructural (memorat într-un fişier de tipul .CDX având
însă un nume diferit de cel al tabelei care se indexează). Un index poate fi considerat un filtru aplicat tabelei,
filtru prin care datele respective sunt pribite într-o anumită ordine. Văzuţi din această perspectivă, indecşii
pot chiar anula accesul la unele înregistrări ale tabelei.
Un tip special de filtrare prin indexare este acela al înregistrărilor cu aceeaşi valoare a cheii de
indexare. Din acest punct de vedere, indecşii pot fi de două feluri:
• normali (Regular), care construiesc pentru fiecare înregistrare a tabelei câte o
înregistrare în fişierul index respectiv, indiferent de duplicarea (sau multiplicarea) valorii cheii
de indexare; prin urmare, într-o astfel de tabelă indexată toate înregistrările sunt accesibile,
indiferent de valoarea cheii de indexare;
• unici (Unique), care permit o unică valoare a cheii de indexare; dacă două sau mai
multe înregistrări ale tabelei corespund aceleiaşi valori a cheii de indexare, numai prima
dintre acestea va fi disponibilă (accesibilă), restul neapărând în tabelă (deşi fizic există).
Fig. 1.4 Localizarea, în acces direct, a înregistrării ce conţine în nr_leg valoarea 1005

Legarea tabelelor între ele în cadrul modelului relaţional a impus alte două tipuri de indecşi,
cel candidat şi cel cheie:
• indexul candidat în cadrul unei tabele pot exista mai multe câmpuri (sau mai precis mai multe
criterii) care să asigure identificarea unică a înregistrărilor tabelei şi care vor sta la baza definirii
indexilor de acest tip; spre deosebire de indexul unic, cel candidat va împiedeca încărcarea într-o
înregistrare a unor valori care, după evaluarea cheii de indexare, conduc la valori existente în
tabelă;
• indexul de tip cheie primară: se va alege dintre potenţialii indecsi de tip candidat şi va juca un
rol important în legarea tabelelor.
Tipurile de indecşi prezentate (normali, unici, candidaţi şi de tip cheie primară) impun anumite
restricţii de acces, în funcţie de valorile cheii de indexare. Accesul la înregistrările tabelei este din ce în ce
mai restrictiv, în ordinea: indecşi normali, unici, candidat sau de tip cheie primară.

1.2.3 Relaţii între tabele


Modelul relaţional al bazelor de date implică organizarea datelor în tabele legate între ele prin relaţii.
Scopul stabilirii relaţiilor este acela de coordonare, după diferite criterii, a datelor citite din tabelele aflate în
legătură. Fie două tabele T1 şi T2, A un câmp din primul tabel şi B un câmp din cel de-al doilea tabel, cele
două câmpuri având valori de acelaşi tip. Tabelul T1 se va numi tabel primar (principal), iat T2 tabel referit
(secundar) 11. Unei înregistrări din T1, cu valoarea V în câmpul A, i se pot pune în corespondenţă anumite
înregistrări din tabela T2. Câmpul A din tabela T1 se numeşte cheie externă, deoarece valorile sale se vor
căuta în T2, printre valorile câmpului B.

11
Fig. 1.5 Definirea unei relaţii între două tabele

Cu ajutorul câmpurilor A şi B, legătura dintre tabelele T1 şi T2 poate fi unul dintre următoarele trei
tipuri:
• 1:1 (one-to-one): unei valori V din câmpul A îi corespunde o unică valoare din câmpul B;
• 1:n (one-to-many): unei valori V din câmpul A îi corespund mai multe valori din câmpul B;
• n:1 (many-to-one): unei valori V din câmpul B îi corespund mai multe valori din câmpul A;
• m:n (many-to-many): unei valori din câmpul A, ce se regăseşte în mai multe înregistrări ale
tabelei T1, îi corespund mai multe valori din câmpul B.
Dintre acestea, în Visual FoxPro sunt implementate doar primele trei tipuri de relaţii. Lipsesc relaţiile
de tipul m:n, deoarece ele se pot reduce la două relaţii, una de tipul n:1 şi alta de tipul 1:n prin intermediul
unor tabele suplimentare intercalate între primele două.
Un alt criteriu de clasificare a relaţiilor dintre tabele este cel al momentului definirii lor. Din acest
punct de vedere există:
• relaţii temporare sau dinamice, care sunt create prin comenzi introduse în programele de
prelucrare, deci sunt disponibile numai la rularea acestora. În afara programelor de prelucrare,
relaţiile respective nu există;
• relaţii permanante, care sunt create automat de sistem la deschiderea bazei de date care conţine
tabelele legate; aceste relaţii sunt disponibile imediat ce este deschisă baza de date, deoarece
sunt memorate în fişierul bazei de date.
La crearea unei relaţii permanente între două tabele este necesară îndeplinirea unor condiţii
prealabile şi anume:
• tabelul primar trebuie să fie indexat cu un index candidat sau primar;
• tabelul referit poate fi indexat cu orice fel de index, de acesta depinzând însă tipul relaţiei
definite.
Condiţia indexării tabelului primar cu un index candidat sau primar determină stabilirea numai a unor
relaţii permanente de tip 1:1 sau 1:n, deoarece acest tip de index împiedică existenţa, în tabelul primar, a
mai multor înregistrări cu aceeaşi valoare a cheii de indexare. Dacă dorim crearea unei relaţii de tip n:1,
aceasta trebuie văzută dinspre tabelul referit înspre tabelul primar. Desigur că prin aceasta se inversează
rolurile primar – referit între tabele. Dacă dorim neapărat ca tabelul primar să-şi păstreze acest rol pentru o
altă relaţie, se poate crea o vedere (view) din cele două tabele legate, vedere care va deveni noul tabel primar
pentru relaţia respectivă.
În esenţă, legătura între două tabele constă într-un cậmp comun ce există în ambele tabele. În felul
acesta se leagă o înregistrare din primul tabel de (prima) înregistrarea corespunzătoare din tabelul referit,
adică legătura se stabileşte pe baza unor valori egale în cậmpul de legătură.
Fig. 1.6 Definirea relaţiilor permanente

1.2.4 Integritatea referenţială a tabelelor legate între ele


prin relaţii
Integritatea referenţială reprezintă un ansamblu de reguli impuse tabelelor între care s-au stabilit
relaţii. Aceste reguli sunt necesare deoarece deseori se doreşte modificarea unor date dintr-o tabelă, date care
afectează relaţia dintre această tabelă şi o alta.
Printre evenimentele care conduc la modificări ale cheii şi deci reprezintă evenimente ce trebuie
tratate prin integritatea referenţială se numără următoarele 12:
• adăugarea unei înregistrări noi;
• ştergerea unei înregistrări;
• modificarea datelor unei înregistrări, date care afectează relaţia.
În cazul adăugării unei noi înregistrări în tabelul referit printr-o relaţie, există următoarele
opţiuni:
• ignorare (Ignore) – cu alte cuvinte, se permite adăugarea noii înregistrări în tabelul referit,
indiferent dacă există sau nu o înregistrare corespunzătoare în tabelul primar;
• restricţionare (Restrict) – se generează o eroare atunci când se încearcă adăugarea unui
înregistrări în tabelul referit printr-o relaţie, fără corespondent în tabelul primar.

12
Fig. 1.7 Adăugarea unei noi înregistrări în tabela copil a unei relaţii

Ştergerea unei înregistrări din tabelul primar al unei relaţii este de asemenea gestionată prin
intermediul integrităţii referenţiale. În acest caz, avem la dispoziţie următoarele opţiuni:
• ignorare (Ignore) – se permite această ştergere, indiferent dacă în tabelul referit există sau nu
înregistrări legate de înregistrarea primară ştearsă;
• restricţionare (Restrict) – opreşte ştergerea, generând un mesaj de eroare, atunci când există
înregistrări corespunzătoare în tabela referită;
• ştergere în cascadă (Cascade) – se şterg automat toate înregistrările din tabelul referit legate de
înregistrarea primară ştearsă.
Modificarea unor date din tabelul primar, ce afectează relaţia dintre tabele, este de asemenea tratată
prin regulile integrităţii referenţiale în felul următor:
• ignorare (Ignore) – se permit modificările respective, chiar dacă prin aceasta înregistrările
referite corespunzătoare rămân „în aer“, adică fără corespondent în tabelul primar;
• restricţionare (Restrict) – atunci când există înregistrări corespunzătoare în tabela referită,
modificarea este oprită şi este generat un mesaj de eroare;
• modificare în cascadă (Cascade) – sunt modificate automat toate înregistrările din tabelul
referit conform noii valori a cheii relaţiei.

1.2.5 Crearea prin intermediul limbajului Visual FoxPro a


unei BD
1. Crearea bazei de date de tip .DBC

CREATE DATABASE <bază_de_date>

2. Crearea tabelelor, a indecşilor şi a relaţiilor permanente dintre tabele

CREATE TABLE | DBF <tabelă1>


(<câmp1> <tip> (<lungime> [, <zecimale>])
[NULL | NOT NULL]
[CHECK <expl1> [ERROR <text1>]]
[DEFAULT <expr2>]
[PRIMARY KEY | UNIQUE]
[REFERENCES <tabelă2> [TAG <etichetă_index1>]]
[, <câmp2> ...]
[, PRIMARY KEY <expr3> TAG <etichetă_index2>
|, UNIQUE <expr4> TAG <etichetă_index3>]
[, FOREIGN KEY <expr5> TAG <etichetă_index4> [NODUP]
REFERENCES <tabelă3> [TAG <etichetă_index5>]])
| FROM ARRAY <tablou>

În cadrul bazei de date curent deschise, <bază_de_date> se va crea tabela <tabelă1>. Aceasta va
conţine câmpurile <câmp1>, <câmp2>, ...<câmpn>. Fiecărui câmp i se vor preciza tipul <tip>, lungimea
<lungime> şi dacă este cazul numărul de zecimale <zecimale>.
Pentru ca într-un câmp să poată fi memorată o valoare nulă, se va apela la argumentul NULL din
sintaxa generală a comenzii. Împiedicarea acestui lucru se va face specificând clauza NOT NULL pentru
câmpul respectiv. În absenţa acestor specificări, câmpul se va comporta conform setării curente stabilite prin
comanda SET NULL ON | OFF. Pentru fiecare câmp se poate impune o regulă de validare a valorilor
acestora – CHECK <expl1>. Dacă condiţia <expl1> nu permite existenţa unui câmp necompletat, atunci
adăugarea unei înregistrări vide (de exemplu prin comanda APPEND BLANK) nu va fi posibilă şi va fi
generat un mesaj de eroare. Acest mesaj de eroare poate fi stabilit de către programator prin intermediul
clauzei ERROR <text1> (ERROR <text2>).
De asemenea, pentru fiecare câmp poate fi stabilită o valoare iniţială implicită – DEFAULT <expr2>,
la adăugarea unui noi înregistrări, câmpurile acesteia vor fi iniţializate cu valorile implicite specificate.
Dacă se doreşte definirea unui index de tip cheie primară, atunci se va apela la clauza PRIMARY KEY
în definirea câmpului ale cărui valori vor sta la baza generării indexului. Acesta va avea acelaşi nume ca şi
câmpul.
Se poate defini un index unic pe baza valorilor unui câmp al tabelei (UNIQUE), acesta va face ca
numai prima dintre înregistrările ce conţin aceeaşi valoare a cheii de indexare să fie accesibilă.
Tabela <tabelă1> creată prin comanda CREATE TABLE | DBF poate constitui un tabel referit într-o
relaţie permanentă, al cărei tabel primar se specifică prin <tabelă 2>. Revenind la figura 1.5, câmpul B este
cel căruia i s-a ataşat clauza REFERENCE <tabelă2>, iar câmpul A din tabelul primar, este cel pe baza
valorilor căruia s-a construit indexul <etichetă_index1>. În absenţa clauzei TAG <etichetă_index1> se ia în
considerare indexul de tip cheie primară al tabelului primar <tabelă2>.
Odată cu creerea tabelei <tabelă1> se poate defini şi un index de tip cheie primară al acesteia, ce are
are la bază o combinaţie de câmpuri ale tabelei şi care va asigura identificarea unică a înregistrărilor tabelei.
Cu valorile lui <expr3>, reprezentând combinaţia de câmpuri dorită, se va forma <etichetă_index2>. Clauza
PRIMARY KEY <expr3> TAG <etichetă_index2> nu va putea fi folosită, dacă s-a definit deja un index de
tip cheie primară cu valorile unui câmp (clauza PRIMARY KEY ataşată câmpului respectiv). Reamintim, că
o tabelă poate avea un singur index de tip cheie primară. Clauza UNIQUE <expr4> TAG <etichetă_index3>
crează un index candidat pe baza valorilor lui <expr4>, ce reprezintă un câmp sau o combinaţie de câmpuri
din tabela nou creată. Trebuie menţionat că, câmpul/câmpurile pe baza căruia/cărora s-a construit indexul de
tip cheie primară nu poate/pot apare în <expr4>. De asemenea, o tabelă poate avea mai mulţi indecşi de tip
candidat.
Clauza FOREIGN KEY <expr5> TAG <etichetă_index4> [NODUP] permite definirea unui index
normal (regular) <etichetă_index4>, pe baza valorilor cheii de indexare <expr5>, pentru tabela <tabelă1>,
fiind esenţială în stabilirea unei relaţii de tipul 1:n cu tabela primară <tabelă3>. Dacă se include specificarea
NODUP în clauză, se va creea un index de tip candidat pentru <tabelă1>, ce va constitui tabelul referit din
relaţia stabilită cu tabela primară <tabelă3>. Noua tabelă <tabelă1> poate fi creată şi cu ajutorul unui tablou,
ce conţine numele, tipul, lungimea şi precizia pentru fiecare câmp al tabelei. Conţinutul lui <tablou> va fi
stabilit în prealabil cu ajutorul funcţiei AFIELDS( ).
Considerând situaţia prezentată în figura 1.6, la nivel de comenzi Visual FoxPro, avem următoarele:

CREATE DATABASE studenti


CREATE TABLE date_pers ;
(nr_leg N(6) PRIMARY KEY, nume C(35), prenume C(35), adresa C(50))
CREATE TABLE anul1 (nr_leg N(6) PRIMARY KEY REFERENCES date_pers, ;
nota1 N(2), nota2 N(2), nota3 N(2), nota4 N(2), nota5 N(2))
CREATE TABLE anul2 (nr_leg N(6) PRIMARY KEY REFERENCES date_pers, ;
nota1 N(2), nota2 N(2), nota3 N(2), nota4 N(2), nota5 N(2))
CREATE TABLE anul3 (nr_leg N(6) PRIMARY KEY REFERENCES date_pers, ;
nota1 N(2), nota2 N(2), nota3 N(2), nota4 N(2), nota5 N(2))
CREATE TABLE anul4 (nr_leg N(6) PRIMARY KEY REFERENCES date_pers, ;
nota1 N(2), nota2 N(2), nota3 N(2), nota4 N(2), nota5 N(2))
Considerăm situaţia din figura 1.8, căreia îi va corespunde următoarea secvenţă de comenzi :

Fig. 1.8 Relaţie permanentă de tipul 1:n

CREATE DATABASE conta


CREATE TABLE conturi (simbol N(8) PRIMARY KEY, ;
tip C(1) CHECK tip="A" OR tip="P" DEFAULT "A", ;
soldi N(10), debit N(10), credit N(10), soldf N(10))
CREATE TABLE operatii (simbol N(8), data_op D(8) DEFAULT { / / }, ;
tip_op C(1) CHECK tip_op="D" OR tip_op="C" DEFAULT "D", ;
valoare N(10), ;
FOREIGN KEY simbol TAG simbol REFERENCES conturi)
CLOSE TABLES
CLOSE DATABASES

Baza de date <conta> conţine tabelele <conturi> şi <operatii>, între care s-a definit o relaţie de
tipul 1:n pe baza valorilor câmpului <simbol> ce apare în ambele tabele. Ambele tabele sunt indexate după
câmpul <simbol>, pentru tabela primară s-a generat o etichetă index de tip cheie primară, iar pentru tabelul
referit s-a creat un index normal (Regular), în conformitate cu tipul relaţiei 1:n.
Odată creată tabela <tabela1>, se poate apela la comenzile COPY STRUCTURE EXTENDED TO şi
CREATE …FROM pentru a crea o nouă tabelă, <tabela2>, cu o structură identică, utilizậnd următoarea
secvenţă de comenzi :
USE <tabela1>
COPY STRUCTURE EXTENDED TO <tabelă_structură>
CREATE <tabela2> FROM <tabelă_structură>

3. Posibilităţi de indexare ulterioară creării tabelei

INDEX ON <expr> TAG <etichetă_index>


[UNIQUE | CANDIDATE] [ASCENDING | DESCENDING]

Comanda INDEX ON nu permite definirea unui index de tip cheie primară, foarte important în
problemele ce vizează integritatea referenţială a tabelelor legate între ele prin relaţii. Pe baza valorilor lui
<expr>, care poate reprezenta un câmp sau o combinaţie de câmpuri ale tabelei curent deschise, se poate
genera o <etichetă_index>, în cadrul indexului structural ataşat tabelei (.CDX), de tip unic sau candidat.

4. Activarea unui index


O tabelă poate avea mai mulţi indecşi asociaţi (etichete index), dar numai unul va fi activ la un
moment dat. Ordinea în care va fi parcursă tabela este dată de indexul activ.

SET INDEX TO <etichetă_index> [ASCENDING | DESCENDING]

5. Definiri ulterioare de relaţii temporare între tabele


Relaţiile temporare sau dinamice sunt create prin comenzi introduse în programele de prelucrare, fiind
disponibile numai la rularea acestora. În afara programelor de prelucrare, aceste relaţii nu există. Printr-o
relaţie temporară pot fi legate atât două tabele ce fac parte dintr-o bază de date, cât şi tabele independente.
Spre deosebire de relaţiile permanente între tabele legate, cele temporare nu sunt memorate în baza de
date .DBC.
Pentru stabilirea unei relaţii temporare între două tabele oarecare, acestea trebuie mai întâi deschise în
două zone de lucru distincte. Relaţia stabilită între cele două tabele nu este o relaţie de egalitate, ci una de
subordonare: una dintre tabele va fi primară (părinte), iar cealaltă referită (copil). Mutarea indicatorului de
înregistrări al tabelei părinte pe o anumită înregistrare determină mutarea indicatorului de înregistrări al
tabelei copil pe înregistrarea corespunzătoare, dar nu şi invers.
Înainte de stabilirea relaţiei, pe lângă deschiderea celor două tabele mai trebuie îndeplinită o condiţie,
şi anume indexarea tabelei copil cu aceeaşi cheie de indexare cu cea a relaţiei.

OPEN DATABASE <bază_de_date>


USE <tabelă1> ORDER <etichetă_indexA> IN 1
USE <tabelă2> ORDER <etichetă_indexB> IN 2
SELECT 1
SET RELATION TO <câmpA> INTO 2
SET SKIP TO <tabela2>
< prelucrări >
SET SKIP TO
SET RELATION TO
CLOSE TABLES
CLOSE DATABASES
<câmpA> face parte din structura tabelului primar <tabelă1>, iar valorile sale se vor căuta printre
valorile câmpului <câmpB> din tabela referită <tabelă2>. Pe baza valorilor din <câmpB> s-a definit indexul
<etichetă_indexB>. Cu aceste considerente, se poate defini o relaţie temporară între <tabelă1> şi <tabelă2>
conform grupului de comenzi Visual FoxPro de mai sus. Relaţia dintre cele două tabele funcţionează în
modul următor: mutarea indicatorului de înregistrări din tabelul primar determină evaluarea succesivă a lui
<câmpA>. În funcţie de valorile acestuia, indicatorul de înregistrări al tabelului referit se va plasa pe prima
înregistrare pentru care valoare (<câmpB>) = valoare (<câmpA>).
Exemplificând această metodologie pentru baza de date din figura 1.8, vom obţine următoarea
secvenţă de comenzi :

OPEN DATABASE conta


USE conturi ORDER simbol IN 1
USE operatii ORDER simbol IN 2
SELECT 1
SET RELATION TO simbol INTO 2
SET SKIP TO operatii
<prelucrări >
SET SKIP TO
SET RELATION TO
CLOSE TABLES
CLOSE DATABASES

1.3. Modificarea structurii tabelelor


Modificarea structurii unei tabele se poate efectua în mod interactiv cu ajutorul comenzii MODIFY
STRUCTURE sau prin intermediul comenzii SQL ALTER TABLE.
Prima comandă activează fereastra Table Designer (figura 1.2), în cadrul căreia se vizualizează
structura tabelei precizate prin comandă.
MODIFY STRUCTURE <tabelă>
În cadrul acestei ferestre se pot adăuga sau şterge cậmpuri şi se pot modifica cele existente din punct
de vedere al numelui, tipului şi mărimii. Totodată se poate interveni asupra indecsilor existenţi precum şi
asupra restricţiilor impuse tabelei.
În ceea ce priveşte comanda SQL ALTER TABLE trebuie făcute următoarele precizări:

1. Adăugarea unui nou atribut


ALTER TABLE <tabelă1>
ADD [COLUMN] <câmp1> <tip> (<lungime> [, <zecimale>])
[NULL | NOT NULL]
[CHECK <expl1> [ERROR <text1>]]
[DEFAULT <expr2>]
[PRIMARY KEY | UNIQUE]
[REFERENCES <tabelă2> [TAG <etichetă_index1>]]
Se va modifica structura tabelei specificate prin <tabelă1> prin adăugarea cậmpului <cậmp1>.
Acesta va prezenta caracteristicile definite prin clauzele ataşate lui în sintaza comenzii.

2. Ştergerea unui atribut


ALTER TABLE <tabelă1>
DROP [COLUMN] <câmp1>
Nucleul SQL din Visual FoxPro, spre deosebire de alte sisteme de gestiune a bazelor de date
relationale, prevede posibilitatea de ştergere a unui cậmp din structura unei tabele.

6. Modificarea numelui/tipului/dimensiunii unui atribut


ALTER TABLE <tabelă1>
RENAME COLUMN <cậmp1_vechi> TO <cậmp1_nou>

ALTER TABLE <tabelă1>


ALTER [COLUMN] <câmp1> <tip> (<lungime> [, <zecimale>])

Este permisă modificarea tipului oricărui cậmp din structura unei tabele. Dacă cậmpul conţine
valori, se va încerca conversia acestora în conformitate cu noul tip stabilit pentru cậmp. În cazul unei
incompatibilităţi între noul tip şi vechiul tip al cậmpului, informaţia din cậmpul supus modificării de tip se
va pierde. Nu este recomandată modificarea tipului cậmpurilor de tip cheie primară sau candidat.
În ceea ce priveşte lungimea cậmpurilor, acestea pot fi deopotrivă mărite sau micşorate, la
micşorare trebuie avută însă în vedere trunchierea ce operează inevitabil.

7. Adăugarea/modificarea/anularea valorii implicite


ALTER TABLE <tabelă1>
ALTER [COLUMN] <câmp1> SET DEFAULT <expr1>
ALTER TABLE <tabelă1>
ALTER [COLUMN] <câmp1> DROP DEFAULT

8. NULL şi neNULL
Pentru unele atribute poate fi instituită la crearea tabelei obligativitatea valorilor nenule. În timp,
aceasta poate fi modificată într-un sens sau în celălalt.

ALTER TABLE <tabelă1>


ALTER [COLUMN] <câmp1> NULL | NOT NULL

9. Adăugarea/anularea restricţiilor
Restricţiile de tip cheie primară (PRIMARY KEY), de unicitate (UNIQUE), referenţială (FOREIGN
KEY) şi de comportament (CHECK) pot fi declarate ulterior creării tabelei sau pot fi anulate.
• Stabilirea unei chei primare şi crearea indexului corespunzător
ALTER TABLE <tabelă1>
ADD PRIMARY KEY <expr1> TAG <etichetă_index1>

• Anularea cheii primare şi a indexului aferent acesteia


ALTER TABLE <tabelă1>
DROP PRIMARY KEY

• Adăugarea unui index candidat


ALTER TABLE <tabelă1>
ADD UNIQUE <expr1> TAG <etichetă_index1>

• Anularea unui index candidat


ALTER TABLE <tabelă1>
DROP UNIQUE TAG <etichetă_index1>

• Stabilirea unei noi restricţii de validare pentru un cậmp


ALTER TABLE <tabelă1>
ALTER [COLUMN] <câmp1>
ADD CHECK <expl1> [ERROR <text1>]

• Anularea restricţiei de validare stabilite pentru un cậmp


ALTER TABLE <tabelă1>
ALTER [COLUMN] <câmp1>
DROP CHECK

• Adăugarea unei relaţii permanente


ALTER TABLE <tabelă1>
ADD FOREIGN KEY <expr1> TAG <etichetă_index1>
REFERENCES <tabelă2> [TAG <etichetă_index2>]
• Anularea unei relaţii permanente
ALTER TABLE <tabelă1>
DROP FOREIGN KEY TAG <etichetă_index1>

1.4 Utilizarea macrosubstituţiei în referirea


tabelelor şi a câmpurilor acestora
Macrosubstituţia permite printr-un mecanism de adresare indirectă referirea dinamică, în timpul
execuţiei comenzii, a unei tabele sau a unui cậmp al acesteia.
În figura 1.9 se propune o bază de date şi o procedură de evidenţă a intrărilor/ieşirilor de material
cât şi a stocului curent. Materialul se specifică cu ajutorul unui control de tip ComboBox legat de cậmpul
"cod" al tabelei "stocuri". Se vor seta corespunzător valorile proprietăţilor ControlSource (stocuri.cod),
RowSource Type (6-Fields) şi RowSource (stocuri.cod) ale controlului. Luarea în gestiune a unui nou
material (tastarea unui nou cod în zona de editare a combobox-ului) implică crearea unei noi tabele
corespunzător setărilor din forma de introducere.

procedura – frmmain_Activate
open database gest_mat
use stocuri order cod alias stocuri in 1
procedura – cmdInregistrare_Click
mat = val (thisform.cbocod.text)
if thisform.optiongroup1.option1.value = 1
nume_tabela = rtrim(thisform.cbocod.text) + "_Intrari.dbf"
cantitate = "Intrare"
else
nume_tabela = rtrim(thisform.cbocod.text) + "_Iesiri.dbf"
cantitate = "Iesire"
endif
if not file ((nume_tabela))
create table &nume_tabela (cod_mat N(10), data_op D(8) default { / / },;
&cantitate N(10), foreign key cod_mat tag cod_mat references stocuri)
use
select 1
seek mat
if not found( )
append blank
replace cod with mat
endif
endif
select 2
use &nume_tabela alias operatii
append blank
replace cod_mat with mat,;
&cantitate with val (thisform.txtcantitate.value),data_op with date( )
select 1
replace stoc with stoc + operatii.&cantitate
use in 2

Fig. 1.9 Crearea tabelelor utilizând macrosubstituţia

procedura – cmdQuit_Click
close tables
close databases
thisform.release
1.5 Validări la nivel de câmp şi la nivel de
înregistrare

Fig. 1.10 Tabele legate şi reguli de validare

Fig. 1.11 Definirea regulilor de validare

Dintr-o bază de date necesară unei contabilităţi informatizate fac parte şi tabelele “plan_ctb” şi
“solduri” – figura 1.10, în prima tabelă fiind memorat planul de conturi, iar în cea de-a doua tabelă soldurile
iniţiale la începutul exerciţiului contabil. Între cele două tabele se defineşte o relaţie de tipul 1 :1 pe baza
simbolurilor conturilor.
Pentru tabela “plan_ctb” se vor defini validări la nivel de cậmp, iar pentru tabela referită “solduri”
validări la nivel de înregistrare. Acestea din urmă sunt impuse de faptul că, un cont de activ poate avea
numai un sold iniţial debitor, iar un cont de pasiv un sold iniţial creditor.
Condiţia de validare la nivel de cậmp este dată de expresia
tip_cont = “Activ” .or. tip_cont = “Pasiv”,
iar regula impusă la nivelul înregistrărilor tabelei “solduri” este următoarea :
plan_ctb.tip_cont = “Activ” .and. sid >= 0 .and. sic = 0 .or.;
plan_ctb.tip_cont = “Pasiv” .and. sic >= 0 .and. sid = 0

În formularea celei de-a doua condiţii s-a apelat la cậmpul “tip_cont” al tabelei “plan_ctb”, acest
lucru fiind posibil datorită relaţiei existente între cele două tabele.

Fig. 1.12 Efectul regulii de validare definite la nivelul înregistrărilor tabelei “solduri”

1.6 Crearea dinamică a bazelor de date


Există situaţii în care structura unei baze de date se cunoaşte doar în momentul execuţiei unei
aplicaţiei în funcţie de anumite opţiuni specificate de către utilizator.
Considerăm următorul exemplu de evidenţă a unei situaţii scolare – figura 1.13. Numărul de
discipline din fiecare an de studiu se va specifica de către utilizator şi în funcţie de acesta se vor crea tabelele
anuale de evidenţă scolară.
Baza de date “evid” va conţine tabela primară “date_pers” şi tabele fiu “anul1”, “anul2”, “anul3”,
“anul4”. În procedura de creare a bazei de date s-au utilizat macrosubstituţia şi comanda ALTER TABLE de
ajustare a structurii tabelelor.

procedura – cmdcreare_Click
nr1 = val(thisform.text1.text)
nr2 = val(thisform.text2.text)
nr3 = val(thisform.text3.text)
nr4 = val(thisform.text4.text)
for i = 1 to 4
nr_note = "nr"+ltrim (str(i))
nume_tabela = "anul"+ltrim(str(i))
create table &nume_tabela (nr_leg N(10) primary key ;
references date_pers, n1 N(2) check n1>0 and n1<11)
for j = 2 to &nr_note
nume_camp = "n"+ltrim(str(j))
alter table &nume_tabela add column &nume_camp N(2);
check &nume_camp >0 and &nume_camp<11
next j
next i

Fig. 1.13 Crearea dinamică a unei baze de date


PRELUCRAREA
2
DATELOR

2.1. Deschiderea / închiderea unei tabele


Orice prelucrare a unei tabele trebuie să fie precedată de operaţia de deschidere a acesteia. În urma
acestei operaţii utilizatorul are acces la conţinutul tabelei, adică la orice înregistrare a acesteia, cu precizarea
că, la un moment dat este accesibilă doar o singură înregistrare, numită înregistrarea curentă. De asemenea,
încheierea oricărei prelucări asupra unei tabele este marcată prin închiderea acesteia.
În Visual FoxPro, spre deosebire de versiunile pentru DOS şi WINDOS, baza de date se
concretizează într-un fişier distinct, reprezentând un mijloc de organizare a unui volum mare de informaţie
într-o structură coerentă. Din punct de vedere logic, o bază de date (.DBC) reprezintă o mulţime de obiecte
conform SGBD-ului folosit pentru gestiunea ei. In sistemul Visual FoxPro, o bază de date poate conţine
obiecte de tip: tabelă (table) sau vedere (view).

OPEN DATABASE <bază_de_date>


USE <tabelă>
[IN <zonă_de_lucru>]
[ORDER [TAG] <etichetă_index>
[ASCENDING│DESCENDING]]
[ALIAS <alias>]
USE │CLOSE TABLES
CLOSE DATABASES

Pentru a opera cu datele dintr-o tabelă deja creată, ea trebuie mai întâi deschisă. La deschidere,
sistemul îi alocă o zonă de memorie specială, numită zonă de lucru. În această zonă de memorie sunt
memoraţi o serie de parametri ai tabelei, parametri necesari gestiunii tabelei, ca de exemplu numărul
înregistrării curente, numărul total de înregistrări din tabelă etc. Într-o zonă de lucru nu poate fi deschisă
decât o singură tabelă. Lucrul cu două sau mai multe tabele simultan implică folosirea a două sau mai multe
zone de lucru distincte. Visual FoxPro posedă o multitudine de zone de lucru (de ordinul zecilor de mii),
astfel încât, practic, nu există restricţii din acest punct de vedere.
După deschidere, asupra tabelei se pot executa o serie de operaţii de prelucrare dintre care cele mai
importante sunt: adăugarea de noi înregistrări, modificarea unor date din înregistrările existente, ştergerea
unor înregistrări, căutarea anumitor date, ordonarea datelor după diferite criterii etc. Când se termină lucrul
cu o tabelă, aceasta trebuie închisă, eliberându-se astfel zona de lucru respectivă.
O observatie importantă legată de lucrul cu datele din tabele este că operaţiile se efectuează la nivel
de înregistrare. Prin urmare, într-o tabelă nu putem adăuga sau şterge decât o înregistrare completă. Pentru a
ţine evidenţa înregistrării curent prelucrate dintr-o tabelă, sistemul foloseşte o variabilă specială, numită
indicatorul de înregistrări. Această variabilă indică totdeauna înregistrarea asupra căreia va acţiona
următoarea comandă.
Pentru prelucrarea datelor din bazele de date se pot folosi două metode:
• una interactivă, constând din utilizarea diferitelor instrumente ale mediului Visual FoxPro.
Operaţiile de prelucrare sunt realizate prin intermediul interfeţei sistemului, sarcina utilizatorului
constând în alegerea de opţiuni, întrebuinţarea unor ferestre de dialog etc.; metoda este folosită
atunci când operaţiile de prelucrare sunt executate de un cunoscător al mediului şi nu în mod
repetat (caz în care este mai avantajoasă realizarea unui program, a unei forme, care să
automatizeze operaţiile respective);
• una prin cod sau prin intermediul limbajului FoxPro: această metodă constă în folosirea
diferitelor comenzi, fie în diferite programe sau metode ale unor forme, rapoarte etc; realizarea
unui sistem informatic necesită folosirea acestei metode, prin construirea unor forme, rapoarte şi
a altor elemente, în ale căror câmpuri sunt introduse comenzi de prelucrare a datelor din bazele
de date.
Comanda USE deschide tabela <tabelă>, ce face parte din <bază_de_date>, în zona de lucru curentă
sau într-o altă zonă specificată în comandă. Modul de acces la conţinutul tabelei, adică modalitatea în care se
ajunge la înregistrările acesteia, poate fi:
• secvenţial, când pentru obţinerea unei informaţii anumite trebuie să parcurgem toate
înregistrările anterioare;
• direct, când putem stabili unde anume pe suport este înregistrarea vizată şi o utilizăm fără să ne
preocupăm de înregistrările anterioare; acest tip de acces este asigurat de o etichetă index
adecvată ataşată tabelei - <etichetă_index>; prin indexare, ordinea fizică a înregistrărilor din
tabelă nu se modifică, se schimbă însă modul în care utilizatorul are acces la datele acesteia;
“prima” înregistrare a tabelei indexate o constituie cea corespunzătoare celei mai mici
(ASCENDING)/mari (DESCENDING) valori a cheii de indexare, iar “ultima” înregistrare a
fişierului indexat este cea care conţine cea mai mare (ASCENDING)/mică (DESCENDING)
valoare a cheii de indexare.
Pentru tabela nou deschisă, se poate defini un pseudonume <alias>, ce va putea fi folosit în
comenzile de prelucrare a tabelei, în locul numelui acesteia.

2.2. Accesul la baza de date


1. Detectarea începutului respectiv a sfârşitului unei tabele
Funcţiile logice BOF (Bottom Of File) şi EOF (End Of File) permit detectarea, în parcurgerea
tabelei, a începutului respectiv a sfârşitului acesteia.

BOF ([<zonă_de_lucru> │<alias>])


EOF ([<zonă_de_lucru> │<alias>])

Tabela, al cărei început respectiv sfârşit se testează, poate fi cea curentă sau cea specificată prin
numărul zonei de lucru în care a fost deschisă sau prin pseudonumele <alias> al ei. Funcţiile returnează
valoarea logică .T., atunci când indicatorul de înregistrări este poziţionat la începutul, respectiv la sfârşitul
tabelei respective.

2. Comenzi de poziţionare a indicatorului de înregistrări


Pentru a ţine evidenţa înregistrării curent prelucrate dintr-o tabelă, sistemul foloseşte o variabilă
specială, numită indicatorul de înregistrări. Această variabilă indică totdeauna înregistrarea asupra căreia va
acţiona următoarea comandă.

GO TOP | BOTTOM [IN <zonă_de_lucru>]


GO [RECORD] <expr1> [IN <zonă_de_lucru>]
SKIP [<expr2>] [IN <zonă_de_lucru>]
Comenzile GO şi SKIP poziţionează indicatorul de înregistrări, al tabelei curente sau specificate prin
<zonă_de_lucru>, într-o anumită poziţie:
• TOP | BOTTOM : mută indicatorul de înregistrări pe prima, respectiv pe ultima înregistrare a
tabelei;
• RECORD <expr1> : mută indicatorul de înregistrări pe înregistrarea cu numărul de ordine dat de
valoarea lui <expr1>;
• comanda SKIP fără clauza <expr2> mută indicatorul de înregistrări la înregistrarea următoare
celei curente;
• dacă <expr2> este o valoare pozitivă, indicatorul de înregistrare va avansa spre sfârşitul tabelei
cu <expr2> poziţii, iar dacă <expr2> este negativă, deplasarea se va face spre începutul tabelei.
În cazul tabelelor indexate, “prima” înregistrare, “ultima” înregistrare, înregistrarea “următoare” şi
respectiv “precedentă” sunt date de către eticheta index activă.

3. Comenzi de localizare a înregistrărilor în acces secvenţial

LOCATE FOR <cond1>


<domeniu>] [WHILE <cond2>]

CONTINUE

Comanda LOCATE parcurge tabela curentă căutând prima înregistrare pentru care <cond1> este
îndeplinită. Dacă comanda are succes, funcţia RECNO ( ) va returna numărul înregistrării localizate, iar
funcţia FOUND ( ) va lua valoarea logică .T. Dacă LOCATE găseşte o înregistrare, se poate folosi comanda
CONTINUE pentru a căuta următoarele înregistrări din tabela curentă care îndeplinesc condiţia <cond1>. La
execuţia comenzii CONTINUE, procesul de căutare continuă de la înregistrarea imediat următoare celei
găsite anterior. Comanda CONTINUE poate fi executată repetat până la detectarea sfârşitului de fişier sau
până la epuizarea domeniului de căutare stabilit prin clauza <domeniu> a comenzii LOCATE sau până la
execuţia unei alte comenzi LOCATE.
Dacă LOCATE nu găseşte nici o înregistrare corespunzătoare, funcţia RECNO ( ) va returna o
valoare egală cu numărul total de înregistrări plus 1 (RECCOUNT ( ) + 1), iar FOUND ( ) valoarea .F. În
mod implicit, comanda LOCATE caută prima înregistrare care îndeplineşte condiţia <cond1> în întreaga
tabelă. Se poate reduce domeniul de căutare (ALL) incluzând clauza <domeniu> în sintaxa comenzii, în una
din următoarele forme: NEXT, RECORD sau REST. Totodată, se poate limita căutarea incluzând clauza
WHILE <cond2> în sintaxa comenzii LOCATE, aceasta având loc atâta timp cât condiţia <cond2> este
adevărată.

4. Comenzi de localizare a înregistrărilor în acces direct

SEEK <expr>
[ORDER [TAG] <etichetă_index>
[ASCENDING | DESCENING]]
[IN <zonă_de_lucru>]

Comanda SEEK caută prima înregistrare din tabela curentă sau din cea deschisă în zona de lucru
specificată prin <zonă_de_lucru>, pentru care valoarea cheii de indexare este egală cu <expr>. Căutarea se
poate face pe baza etichetei index curente sau prin intermediul celei specificate prin <etichetă_index>.
Dacă valoarea <expr> nu se regăseşte printre valorile cheii de indexare, dar setarea curentă SET
NEAR are valoarea ON, prin comanda SEEK se va localiza înregistrarea corespunzătoare celei mai
apropiate -mai mari- valori a cheii de indexare de valoarea expresiei <expr>.
Dacă expresia <expr> este alfanumerică şi nu se regăseşte exact printre valorile cheii de indexare, dar
setarea curentă SET EXACT are valoarea OFF, prin comanda SEEK se va localiza înregistrarea pentru care
<expr> constituie un subşir stâng al cheii de indexare.
Numărul înregistrării localizate prin intermediul comenzii SEEK este returnat de funcţia RECNO ( ),
iar funcţia logică FOUND ( ) va lua, în această situaţie, valoarea .T.
5. Implementarea mecanismului de căutare secvenţială şi directă

do form form2

do form form3

Fig. 2.1 Interfaţa aplicaţiei


Înregistrările localizate prin căutare secvenţială sunt vizualizate într-o fereastră Browse – figura 2.2.

LOCATE

CONTINUE

Fig. 2.2 Căutarea secvenţială. Comenzile LOCATE şi CONTINUE

procedura – cmdAfisare_Click( )
open database aplic
use magazin
locate for val(cod) = val(thisform.txtcod.value)
do while found( ) and not eof( )
browse noedit nodelete title "afisare"
continue
enddo
use
close databases

SEEK

Fig. 2.3 Căutarea directă. Comanda SEEK

Prima apariţie a valorii căutate se localizează cu ajutorul comenzii SEEK – figura 2.3, celelalte
apariţii urmậndu-i acesteia şi putậndu-se identifica cu ajutorul comenzii SKIP.

procedura – cmdAfisare_Click( )
open database aplic
use magazin order codr
seek thisform.cbocod.value
if found( )
do while cod = thisform.cbocod.value and not eof( )
browse
skip
enddo
endif
close databases

6. Tehnici de parcurgere a tabelelor

Există diferite modalităţi de acces la înregistrările unei tabele în vederea implementării algoritmilor de
prelucrare a datelor conţinute de către acestea.

 comanda SCAN … ENDSCAN


OPEN DATABASE <bază_de_date>
USE <tabelă> [ORDER <etichetă_index>]
SCAN [<domeniu>] [FOR <cond1>] WHILE <cond2>]
<prelucrare_înregistrare>
[LOOP]
[EXIT]
ENDSCAN
CLOSE TABLES
CLOSE DATABASES

Comanda SCAN…ENDSCAN realizează parcurgerea tabelei curente şi executarea operaţiilor


<prelucrare_înregistrare> pentru fiecare înregistrare a tabelei care aparţine domeniului specificat prin
<domeniu>, clauza FOR sau WHILE.
Se poate reduce domeniul de prelucrare (ALL) incluzând clauza <domeniu> în sintaxa comenzii, în
una din următoarele forme: NEXT, RECORD sau REST.
Totodată, se poate limita prelucrarea înregistrărilor incluzând clauza WHILE <cond2> în sintaxa
comenzii SCAN…ENDSCAN, aceasta având loc atâta timp cât condiţia <cond2> este adevărată. Clauza
FOR <cond1> limitează prelucrarea înregistrărilor la cele care îndeplinesc condiţia <cond1> din cadrul
domeniului luat în considerare.

 comenzile DO WHILE … ENDDO şi FOR … ENDFOR

OPEN DATABASE <bază_de_date>


USE <tabelă> [ORDER <etichetă_index>]

DO WHILE NOT EOF ( ) n = RECCOUNT ( )


<prelucrare_înregistrare_curentă> FOR i = 1 TO n
SKIP <prelucrare_înregistare_curentă>
ENDDO SKIP
ENDFOR
CLOSE TABLES
CLOSE DATABASES

7.Vizualizarea conţinutului tabelelor


♦ comenzile ? şi ??

? | ?? [<expr1> [ PICTURE <format1>] [FUNCTION <format2>]


[ AT <nr> ]
[, <expr2> ..]]
Comenzile ? şi ?? afişează rezultatul expresiilor <expr1>, <expr2> pe ecran, în cadrul formei sau
ferestrei curent active. Comanda ? face ca afişarea să aibă loc de la începutul liniei următoare celei curente,
iar ?? determină ca aceasta să aibă loc începând cu poziţia curentă a cursorului. Prin clauza AT se poate
impune şi o anumită coloană de început a afişării. <expr1>, <expr2> vor fi afişate conform formatului de
afişare specificat printr-o clauză PICTURE sau FUNCTION.

♦ comanda @ SAY

@<lin>,<col>SAY <expr> [PICTURE <expc1>]


[FUNCTION <coduri_functie>]
[SIZE <expn1>,<expn2>]
[FONT <expc2>[,<expn3>]][STYLE <expc3>]
[COLOR <culori>COLOR SCHEME <expn4>]

Comanda permite afişarea pe ecran, începând cu o anumită poziţie şi într-un anumit format, a valorii
expresiei <expr>.

♦ comenzile DISPLAY şi LIST

DISPLAY
[[FIELDS] <listă_cậmpuri>]
[<domeniu>] [FOR <cond1>] [WHILE <cond2>]
[OFF]
[TO PRINTER | TO FILE <fişier>]

Comanda permite vizualizarea conţinutului tabelei curente, în mod implicit fiind afişat conţinutul
înregistrării curente în cadrul formei sau ferestrei curent active. Se poate limita afişarea la anumite cậmpuri
prin introducerea clauzei [FIELDS] <listă_cậmpuri> în sintaxa comenzii. Afişarea conţinutul unui cậmp de
tip “memo” este posibilă numai prin includerea numelui acestuia în <listă_cậmpuri>.
Se poate extinde domeniul de afişare prin includerea clauzei <domeniu> în sintaxa comenzii, în una
din variantele: ALL, NEXT, RECORD sau REST.
Totodată, se poate limita afişarea înregistrărilor incluzând clauza WHILE <cond2> în sintaxa
comenzii DISPLAY, aceasta având loc atâta timp cât condiţia <cond2> este adevărată. Clauza FOR
<cond1> limitează afisarea înregistrărilor la acelea care îndeplinesc condiţia <cond1> din cadrul domeniului
luat în considerare.
Prin introducerea clauzei OFF în sintaxa comenzii DISPLAY se omite afişarea numerelor de
înregistrare în faţa înregistrărilor vizualizate. Inregistrarile vizate pot fi tipărite la imprimantă, prin
introducerea clauzei TO PRINTER în sintaxa comenzii sau pot fi memorate într-un fişier separat (clauza TO
FILE).

LIST
[[FIELDS] <listă_cậmpuri>]
[<domeniu>] [FOR <cond1>] [WHILE <cond2>]
[OFF]
[TO PRINTER | TO FILE <fişier>]

Comanda LIST este similară comenzii DISPLAY, în mod implicit fiind însă vizualizat întregul
conţinut al tabelei curente.

5. Introducerea datelor de la tastatură


♦ comenzile INPUT şi ACCEPT

INPUT ACCEPT [<expc>] TO <var>

Comenzile permit introducerea de la tastatură a unei valorii pentru variabila <var>, textul <expc>
precedând pe ecran poziţia de introducere a acesteia. Particularitatea comenzii ACCEPT constă în faptul că,
prin intermediul ei se pot introduce de la tastatură numai valori alfanumerice.
♦ comanda @ GET şi combinaţia @ SAY … GET

@<lin>,<col>GET<var><câmp> [PICTURE<expc1>]
[FUNCTION <expc2>]
[DEFAULT <expr1>] [MESSAGE <expc3>]
[WHEN <expl2>]
[RANGE [<expr2>][,<expr3>]] [VALID <expl1>]
[FONT <expc4>[,<expn1>]][STYLE <expc5>]
[SIZE <expn2>,<expn3>]
[COLOR <culori>COLOR SCHEME <expn4>]

Comanda permite editarea unei variabile sau a unui câmp al unei tabele fie la coordonatele specificate
prin <lin>,<col>, fie la poziţia următoare de pe ecran care urmează după SAY (atunci când comenzile SAY
şi GET sunt grupate împreună). O comandă @GET (sau grup de comenzi @GET) este urmată de o
comandă READ, care va declanşa citirea cîmpului GET şi atribuirea valorii citite lui <var><câmp>.

6. Adăugarea unor noi înregistrări în cadrul unei tabele

♦ comenzile de tip APPEND

APPEND [BLANK] [IN <zonă_de_lucru> │<alias>]


Comanda adaugă o înregistrare la sfậrşitul tabelei cuente sau celei specificate prin IN
<zonă_de_lucru><alias>. Clauza BLANK adaugă o înregistrare vidă. Comanda APPEND fără BLANK
este folosită pentru adăugarea de înregistrări prin intermediul unei ferestre de editare.

APPEND FROM <fişier> [FIELDS <listă_cậmpuri>]


[FOR <expl1>]
[DELIMITED WITH TABWITH<delimitator>WITH BLANK]
[TYPE]
[DIFFW2MODPDOXRPDSDFSYLKWK1WK3
WKSWR1WRKXLS]

Tabela curentă poată fi îmbogăţită cu noi înregistrări, al căror conţinut va fi preluat dintr-un alt fişier
specificat. <listă_cậmpuri> reprezintă cậmpurile ale căror valori vor fi adăugate în tabela curentă.
În mod implicit sunt preluate din <fişier> toate înregistrările şi adăugate la conţinutul tabelei curente.
Se poate limita setul de înregistrări din <fişier> care se vor adăuga la acelea care îndeplinesc o anumită
condiţie <expl1>. Dacă fişierul sursă nu este o tabelă de date (.DBF), atunci trebuie specificat tipul acestuia
în conformitate cu opţiunile oferite de către sintaxa generală a comenzii.

 comanda INSERT -SQL


INSERT INTO <tabela1> [(<cậmp1> [, <cậmp2>, ...] )]
VALUES (<expr1> [,<expr2>, ...])
Comanda INSERT – SQL adaugă o înregistrare în <tabela1>. Cậmpurile specificate ale noii
înregistrări se vor completa cu valorile precizate. Dacă tabela nu este deschisă, ea se va deschide într-o nouă
zonă de lucru, în mod exclusiv.

7. Modificarea conţinutului tabelelor

♦ comanda REPLACE

REPLACE <cậmp1> WITH <expr1> [ADDITIVE]


[, <cậmp2> WITH <expr2> [ADDITIVE] …]
[<domeniu>]
[FOR <cond1>] [WHILE <cond2>]

Comanda REPLACE actualizează înregistrările tabelei curente prin înlocuirea datelor din cậmpurile
<cậmp1>, <cậmp2>,… cu valorile expresiilor <expr1>, <expr2>…Prin ataşarea clauzei ADDITIVE
cậmpului <cậmpi> specificat în comanda REPLACE, la conţinutul curent al acestuia se va aduna valoarea
expresiei <expri>.
În mod implicit, sunt afectate cậmpurile înregistrării curente. Se poate extinde domeniul de acţiune al
comenzii prin includerea clauzei <domeniu> în sintaxa comenzii, în una din variantele: ALL, NEXT,
RECORD sau REST.
Înregistrările din <domeniu> afectate de comanda REPLACE pot fi numai acelea pentru care
condiţia <cond1> din clauza FOR este îndeplinită. Totodată, înlocuirea stabilită prin comanda REPLACE
poate avea loc atậta timp cật condiţia <cond2> este adevărată. Deoarece comanda REPLACE actualizează şi
index-urile active, este important ca operaţia de înlocuire să nu fie efectuată asupra cậmpului cheie atunci
cậnd există o clauză <domeniu>, FOR sau WHILE efectivă.
Pot fi actualizate prin REPLACE şi cậmpuri din tabele deschise în alte zone de lucru, dacă pentru
acestea se va utiliza sintaxa <alias>.<cậmpi>.

 comanda UPDATE - SQL

UPDATE [<bază_de_date>] <tabela1>


SET <cậmp1> = <expr1>
[, SET <cậmp2> = <expr2> ...]
[WHERE <expl1>]

Modificarea valorilor cậmpurilor unei tabele se poate realiza cu ajutorul comenzii UPDATE – SQL.
Modificările se vor efectua în <tabela1> şi vor afecta cậmpurile <cậmp1>, <cậmp2>, ... ale acesteia
conform setărilor stabilite. Prin includerea clauzei WHERE în sintaxa comenzii, se va limita domeniul
înregistrărilor luate în considerare la acelea care îndeplinesc condiţia <expl1>.

8. Ştergerea de înregistrări dintr-o tabelă

♦ comenzile DELETE şi PACK

DELETE [<domeniu>] [FOR <expl1>] [WHILE <expl2>]

Comanda marchează înregistrări pentru ştergere din tabela curent deschisă. Înregistrările marcate pot
fi recuperate prin comanda RECALL.

DELETE FROM [<bază_de_date>] <tabela1>


[WHERE <expl1> ]

Din tabela specificată se vor şterge logic toate înregistrările pentru care condiţia <expl1> este
îndeplinită.
PACK
Comanda PACK se foloseşte pentru ştergerea fizică a înregistrărilor care anterior au fost marcate
logic.

9. Operaţii de bază cu o tabelă. Exemplificare


Ne propunem să prezentăm o variantă nepretenţioasă de implementare a operaţiilor de bază asupra
datelor dintr-o tabelă – „tabel1” cu ajutorul comenzilor prezentate în cadrul acestui paragraf – figura 2.4. Pe
suprafaţa unei forme distincte se proiectează o machetă de introducere a datelor cu ajutorul combinaţiei
@SAY…GET. Intregul conţinut al tabelei va fi vizualizat cu ajutorul unui control de tip GRID respectiv cu
ajutorul comenzii @SAY. In cea de-a doua variantă este necesar să se implementeze algoritmul de
parcurgere al tabelei înregistrare cu înregistrare. Totodată este implementată şi operaţia de ştergere a
anumitor înregistrări specificate.

Fig. 2.4 Inregistrare date cu ajutorul comenzilor @SAY/GET

procedura – frminreg_Activate
open database aplic3
use tabel1
opt = "d"
do while upper (opt) = "D"
append blank
@ 2,2 say "Cod client:" get cod_cl picture "9999"
@ 3,2 say "Nume :" get nume function "!xxxxxxxxxxxxxxx"
@ 4,2 say "Prenume:" get prenume function "!xxxxxxxxxx"
@ 5,2 say "Factura:" get fact picture "99999999"
@ 6,2 say "Cantitate:" get cant picture "9999999999"
@ 7,2 say "Pret:" get pret picture "99999999"
@ 8,2 say "Data facturii:" get data_fact
@ 11,2 say "Dorit sa continuati introducerea de date?D/N" get opt
read
replace valoare with cant*pret
enddo
use
close databases

Fig. 2.5 Afişarea cu ajutorul unui control de tip GRID

procedura – frmAfisare_Activate
open database aplic3
use tabel1
go top
@ 2,1 say "Numele"
@ 2,17 say "Valoare"
@ 2,33 say "Data facturii"
linie = 3
do while not eof( )
@ linie,1 say nume
@ linie,17 say valoare
@ linie,33 say data_fact
linie = linie+1
skip
enddo
Fig. 2.6 Afişarea cu ajutorul comenzii @SAY use
close databases
(*)
( ** )

Fig. 2.7 Controlul de tip Grid şi operaţia de ştergere a înregistrărilor

Operaţia de ştergere a înregistrărilor este implementată cu ajutorul unui control de tip GRID, prin
intermediul căruia este vizualizat conţinutul tabelei „tabel1”. Înregistrarea ce urmează a fi ştersă se
selectează în cadrul controlului de tip GRID prin poziţionare pe o anumită linie a acestuia, iar operaţiile ce
urmează a fi executate vor fi incluse în corpul procedurii eveniment AfterRowColChange asociate grid-ului.

procedura – frmstergere_Activate
public poz
thisform.cmdstergere.visible = .F.
open database aplicatie

procedura – grid1_AfterRowColChange
poz = recno( )
if deleted( )
thisform.cmdstergere.visible = .F.
cls
else
thisform.cmdstergere.visible = .T. (*)
endif

Controlul de tip GRID afişează toate înregistrările tabelei asociate, cele şterse logic prin comenzi de
tip DELETE fiind marcate corespunzător. Distincţia între o înregistrare efectivă şi una ştearsă logic se poate
face cu ajutorul funcţiei logice DELETED( ).

procedura – cmdstergere_Click ( ** )
go poz
@ 7,60 say "Nume: " + nume
@ 8,60 say "Valoare: " + ltrim(str(valoare))
thisform.command1.visible = .F.
on key label del delete

Pe ecran sunt afişate informaţii extrase din înregistrarea selectată, pentru ştergere urmậnd a se apăsa
tasta <Del>. Conform specificării efectuate prin comanda ON KEY LABEL, comanda DELETE va fi
executată după apăsarea acestei taste.
procedura – cmdintoarcere_Click
close tables
use tabel1
pack
use
close databases
on key
thisform.release

2.3. Comenzile BROWSE,


BROWSE, CHANGE şi EDIT
BROWSE
[FIELDS <câmp1> [, <câmp2> ...]]
[FONT <font> [, <dimensiune>]] [STYLE <stil>]
[FOR <expr1> ]
[FREEZE <câmpi>]
[KEY <expr2 [, <expr3>]]
[NOAPPEND] [NODELETE][NOEDIT | NOMODIFY]
[PARTITION <coloană> [LEDIT] [REDIT]]
[TITLE <titlu>]
[WIDTH <lungime>]

Comanda BROWSE determină apariţia pe ecran, în cadrul formei sau ferestrei curente, a ferestrei
speciale Browse. Aceasta reprezintă principalul instrument pentru realizarea diferitelor operaţii de prelucrare
asupra datelor din tabela curentă. În cadrul ferestrei Browse este afişat conţinutul tabelei, în mod implicit
într-un format tabelar, coloanele reprezentând câmpuri, iar liniile înregistrări), conţinut care poate fi
modificat de utilizator în mod interactiv. Se pot adăuga noi înregistrări sau şterge înregistrări existente, se
poate interveni asupra conţinutului oricărui câmp şi al oricărei înregistrări, se pot căuta diferite date, etc. În
mod implicit sunt afişate toate câmpurile din structura tabelei. Se poate restrânge afişarea la anumite câmpuri
utilizând clauza FIELDS. Printre câmpurile specificate pot să apară şi câmpuri dintr-o tabelă referită,
deschisă într-o altă zonă de lucru. În acest caz se va utiliza sintaxa <pseudonume>.<câmp>.
Formatul de afişare al datelor vizualizate în cadrul ferestrei Browse, poate fi impus prin intermediul
clauzelor FONT şi STYLE. În absenţa acestor clauze, pentru afişarea datelor în cadrul ferestrei Browse se
va utiliza fontul MS Sans Serif, stilul de afişare va fi cel normal, iar caracterele vor fi de dimensiune 8.
În mod implicit sunt afişate toate înregistrările tabelei curente. Se poate restrânge afişarea la anumite
înregistrări care îndeplinesc o anumită condiţie impusă prin intermediul clauzei FOR.
În mod implicit, prin intermediul ferestrei Browse este posibilă modificarea conţinutului oricărui
câmp afişat în fereastră. Dacă se va dori modificarea conţinutului a numai unui singur câmp, atunci se va
specifica acest câmp şi prin clauza FREEZE <câmpi>.
În cazul tabelelor indexate, se poate limita afişarea la acele înregistrări care conţin o anumită valoare a
cheii de indexare <expr2> sau care conţin valori ale acesteia cuprinse în intervalul [<expr2>, <expr3>].
Operarea în cadrul ferestrei Browse poate fi limitată prin introducerea clauzelor NOAPPEND,
NODELETE, NOEDIT sau NOMODIFY.
Fereastra Browse poate fi împărţită în două subferestre (partiţii) de vizualizare, limita dintre cele două
ferestre fiind specificată printr-un număr de coloană – PARTITION <coloană>. Dacă se apelează la clauza
LEDIT/REDIT, atunci partiţia (subfereastra) stânga/dreaptă va corespunde modului EDIT de afişare,
partiţia dreaptă/stângă fiind în modul standard Browse de afişare.
În mod implicit, în bara de titlu a ferestrei Browse apare numele tabelei vizualizate. Se poate impune
un text oarecare prin intermediul clauzei TITLE.
Clauza WIDTH <lungime> impune lăţimea coloanelor de afişare din ferestra Browse, indiferent de
dimensiunea câmpurilor tabelei. În cazul câmpurilor de dimensiuni mai mari decât <lungime>, coloanele
aferente acestora vor fi prevăzute cu bare de difilare pe orizontală.
Comenzile CHANGE şi EDIT prezintă o sintaxă similară lui BROWSE, fereastra de vizualizare a
conţinutului tabelei fiind însă diferită.

CHANGE | EDIT
[FIELDS <câmp1> [, <câmp2> ...]]
[FONT <font> [, <dimensiune>]] [STYLE <stil>]
[<domeniu> [FOR <expr1> ] [WHILE <expr2>]
[FREEZE <câmpi>]
[KEY <expr3 [, <expr4>]]
[NOAPPEND][NODELETE][NOEDIT | NOMODIFY]
[PARTITION <coloană> [LEDIT] [REDIT]]
[WIDTH <lungime>]

Prin clauza <domeniu> se poate stabili mulţimea înregistrărilor din tabela curentă ce vor fi afişate
într-o fereastră de tip Change sau Edit. Clauzele FOR şi WHILE se vor aplica numai înregistrărilor din
<domeniu>. Acesta poate fi ALL, NEXT n, REST sau RECORD n.

2.4. Comenzile SCATTER şi GATHER


SCATTER
[FIELDS <câmp1> [,<câmp2> ...]
| FIELDS LIKE <cond1> | FIELDS EXCEPT <cond2>] [MEMO]
TO <tablou1> | TO <tablou2> BLANK | MEMVAR | MEMVAR BLANK
Comanda copiază conţinutul câmpurilor din înregistrarea curentă într-un set de variabile simple sau
într-un tablou. Dacă se utilizează clauza FIELDS, atunci se vor lua în considerare doar câmpurile specificate
explicit prin intermediul acesteia. Incluzând clauza FIELDS LIKE respectiv FIELDS EXCEPT în sintaxa
comenzii, se poate condiţiona transferul câmpurilor în memorie la acelea care îndeplinesc <cond1> respectiv
la acelea care nu îndeplinesc <cond2>. În mod implicit, câmpurile de tip memo sunt ignorate de către
comanda SCATTER. Pentru a transfera şi conţinutul acestor câmpuri, se va introduce clauza MEMO în
sintaxa comenzii.
Clauza TO <tablou1> specifică tabloul în care se copiază conţinutul câmpurilor înregistrării curente:
primul element al tabloutui va conţine valoarea primului câmp, al doilea element al tabloului valoarea celui
de-al doilea câmp, etc. Dacă tabloul are mai multe elemente disponibile decât câmpuri în înregistrare,
elementele nefolosite rămân neschimbate. Dacă tabloul nu există sau dimensiunea sa este prea mică, el este
creat automat, tipurile de date ale elementelor tabloului vor fi cele ale câmpurilor de unde se copiază
conţinutul. Clauza TO <tablou2> BLANK crează un tablou cu elemente “vide” de acelaşi tip şi de aceeaşi
dimensiune ca şi câmpurile tabelei curente. Clauza MEMVAR determină transferul conţinutului câmpurilor
tabelei curente într-un set de variabile, pentru fiecare câmp transferat generându-se o variabilă cu acelaşi
nume ca şi câmpul respectiv, de acelaşi tip şi de aceeaşi dimensiune ca acesta. O astfel de variabilă, obţinută
în urma execuţiei unei comenzi SCATTER, se referă utilizând notaţia
M.<nume_câmp>.
Clauza MEMVAR BLANK creează un set de variabile fără conţinut.

GATHER FROM <tablou> | MEMVAR


[FIELDS <câmp1> [, <câmp2>...]
| FIELDS LIKE <cond1> | FIELDS EXCEPT <cond2>] [MEMO]

Comanda mută conţinutul unui set de variabile sau al unor elemente de tablou în câmpurile
înregistrării curente, fiind opusă comenzii SCATTER.

2.5 Dezvoltarea aplicaţiilor. Interfeţe vizuale


O aplicaţie Visual FoxPro este o aplicaţie dirijată de evenimente, respectând regulile de comportare a
aplicaţiilor sub sistemul de operare Windows. Dezvoltarea unei aplicaţii începe cu proiectarea interfeţei
vizuale a acesteia şi continuă cu formularea procedurilor eveniment necesare implementării aplicaţiei.

10. O aplicaţie elementară cu forme de tip SDI


Interfaţa aplicaţiei este proiectată în stil SDI (Single Document Interface), constậnd din trei forme
independente. Forma principală conţine trei butoane de comanda – cmdcreare, cmdadaugare şi
cmdvizualizare. Acestea permit utilizatorului executarea unui click pe ele pentru a îndeplini anumite acţiuni.
Ori de cậte ori utilizatorul execută un click pe un buton de comandă, procedura corespunzătoare
evenimentului Click este invocată. Pentru a îndeplini acţiunea dorită, se va plasa codul respectiv în
procedura evenimshameless.us.s01e10.hdtv.xvid-fqmentului Click – figura 2.8.
Se propune utilizarea comenzii CALCULATE pentru numerotarea liniilor unei facturi şi în
determinarea valorii totale a acesteia . Comanda va efectua calculele indicate de utilizator prin intermediul
expresiilor <expr1>, <expr2>, …, în formularea cărora se pot utiliza funcţii statistice şi/sau financiare
standard, cum ar fi AVG, CNT, MAX, MIN, NVP, STD, SUM şi VAR.

CALCULATE <expr1> [ expr2 …]


[<domeniu>] [FOR <expl1>] [WHILE <expl2>]
[TO <var1> [<var2> …] TO ARRAY <tablou>]

Operaţiile specificate se vor aplica asupra valorilor unor cậmpuri ale tabelei curent deschise şi/sau
variabile. Prelucrarea se poate limita la înregistrările care aparţin unui anumit <domeniu> şi/sau îndeplinesc
o anumită condiţie <expl1> şi/sau prelucrarea poate avea loc atâta timp cât o anumită condiţie <expl2>
rămâne adevărată. Rezultatul poate fi memorat în variabile sau într-un tablou.

procedura - cmdcreare_Click
create table linie_factura (nr_factura N(8), nr_linie N(2), cod_material N(8),;
pret N(8),cantitate N(8))
use
7

Fig. 2.8 Evidenţa facturilor

procedura - cmdadaugare_Click
do form frmadaugare
thisform.release
procedura - cmdvizualizare_Click
do form frmvizualizare
thisform.release

Introducerea liniilor unei facturi are loc prin intermediul unei forme distincte, pe care sunt plasate
controale de tip casetă de text. Conţinutul curent al unei casete de text este dat de valoarea proprietăţii Value
a acesteia. Instrucţiunile de iniţializare a formei se vor ataşa procedurii eveniment Activate.

procedura - frmadaugare_Activate
thisform.txtnrfactura.value = ""
thisform.txtcod.value = ""
thisform.txtpret.value = ""
thisform.txtcantitate.value = ""
use linie_factura

Numărul noii linii se calculează în funcţie de poziţiile existente pe factură.


procedura - cmdinreg_Click
set talk off
linie = 0
calculate max(nr_linie) for nr_factura = val(thisform.txtnrfactura.value) to linie
append blank
8

replace nr_factura with val(thisform.txtnrfactura.value)


replace nr_linie with linie + 1
replace cod_material with val(thisform.txtcod.value)
replace pret with val(thisform.txtpret.value)
replace cantitate with val(thisform.txtcantitate.value)
thisform.txtnrfactura.value = ""
thisform.txtcod.value = ""
thisform.txtpret.value = ""
thisform.txtcantitate.value = ""

procedura cmdrev_Click
use
thisform.release
do form frmoptiuni

Vizualizarea facturii specificate prin nr are loc într-o fereastră Browse. Totodată se va calcula
valoarea totală a facturii şi se va afişa în bara de titlu a acesteia.

procedura frmvizualizare_Activate
set talk off
nr = 0
total = 0
use linie_factura
@2,10 say "NR. FACTURA: " get nr picture '99999999'
read
calculate sum(pret*cantitate) for nr = nr_factura to total
titlu = 'Factura: ' + ltrim(str(nr)) + ' Total: ' + ltrim(str(total))
browse fields nr_linie,cod_material,pret,cantitate nomodify noappend nodelete;
title titlu for nr = nr_factura
use
clear
set talk on

2. Combinarea formelor şi a ferestrelor WINDOW


Crearea unei ferestre utilizator, specificarea atributelor şi a caracteristicilor sale se realizează prin
comanda DEFINE WINDOW. Ferestrele utilizate sunt create într-o etapă distinctă activării şi afişării lor pe
ecran prin comenzile ACTIVATE WINDOW şi SHOW WINDOW. Ferestrele astfel definite şi activate vor
rămậne afişate pe ecran pậnă la executarea unei comenzi DEACTIVATE WINDOW sau HIDE WINDOW.

DEFINE WINDOW <nume> FROM <lin1,col1> TO <lin2,col2> 


AT <lin3,col3> SIZE <lin4,col4>
[IN [WINDOW] <fer> IN SCREEN]

ACTIVATE SHOWDEACTIVATEHIDE WINDOW <nume>

Este implementată atật operaţia de înregistrare a intrărilor/ieşirilor de material cật şi actualizarea


stocului curent. Materialul se specifică cu ajutorul unui control de tip listă derulantă ComboBox ce afişează
o listă cu codurile tuturor materialelor din gestiune. Cele trei controale cbocod, txtpret şi txtstoc sunt legate
de cậmpurile tabelei stocuri, astfel încật în casetele de text să fie preluată automat informaţia din
înregistrarea ce conţine codul materialului selectat.
9

Totodată, într-o fereastră Browse sunt afişate operaţiile de intrare/ieşire pentru materialul ales. Orice
modificare în cadrul ferestrei Browse se va reflecta şi în actualizarea stocului curent după închiderea acestei
ferestre. Fereastra Browse se va deschide în cadrul unei ferestre utilizator definite prin DEFINE WINDOW.
Legarea unui control de un cậmp dintr-o tabelă deschisă este posibilă prin setarea corespunzătoare a
valorii proprietăţii ControlSource (stocuri.cod) a controlului respectiv. În ceea ce priveşte controlul de tip
ComboBox, lista de elemente ataşată acestuia se generează conform specificărilor făcute prin proprietăţile
RowSource Type (6-Fields) şi RowSource (stocuri.cod ) ale controlului.

Fig. 2.9 Interfaţa aplicaţiei de gestiune a stocurilor

procedura - frmlocaliz_Load
set talk off
public p
open database magazie
use stocuri order cod alias stocuri in 1
use io order cod alias io in 2
p=0
procedura – frmlocaliz_Activate
if p = 1
wait ""
endif
thisform.txtpret.visible = .F.
thisform.txtstoc.visible = .F.
thisform.lblpret.visible = .F.
thisform.lblstoc.visible = .F.

În fereastra utilizator op se vor afişa toate întrările/ieşirile ale materialului cod_spec. Deoarece, în
fereastra Browse se afişează numai conţinutul cậmpurilor data_op şi cantitate, înregistrările noi adăugate
prin intermediul acestei ferestre au cậmpul cod necompletat. Acesta se va completa ulterior cu valoarea
cod_spec. Apoi se va recalcula, printr-o comanda CALCULATE, stocul curent al acestui material.

procedura – cbocod_Click
p=1
cod_spec = val(thisform.cbocod.text)
define window op from 3,28 to 16,55
select stocuri
seek cod_spec
thisform.txtpret.value = pret
10

thisform.txtstoc.value = stoc
thisform.txtpret.visible = .T.
thisform.txtstoc.visible = .T.
thisform.lblpret.visible = .T.
thisform.lblstoc.visible = .T.
activate window op
select io
browse fields data_op,cantitate for cod = cod_spec
replace cod with cod_spec for cod<1
calculate sum(cantitate) to stoc_curent for cod = cod_spec
frmlocaliz.txtstoc.value = stoc_curent
deactivate window op

procedura – frmlocaliz_Unload
close databases
set talk on

3. Forme generate cu ajutorulWizard-ului


11

Fig. 2.10 Înlănţuirea formelor în crearea interfeţei aplicaţiei

procedura – cmdCreare_Click
create database gest
create table produse (codp N(5) primary key check codp>10000;
error "Codul produsului trebuie sa fie de 5 cifre !",;
denp C(16) not null check substr(denp,1,1)=upper(substr(denp,1,1));
error "Prima litera din denumire este obligatoriu majuscula !",ump C(3))
create table beneficiari (codb N(5) primary key check codb>0;
error "Codul beneficiarului trebuie sa fie mai mare ca zero !",;
denb C(30) not null, str C(20),nr n(3),loc c(20), tel n(7),ctbc c(15))
create table contracte (codp N(5) check codp>10000 ;
error "Codul produsului trebuie sa fie de 5 cifre !",;
codb N(5) check codb>0 error "Codul trebuie sa fie mai mare ca zero !",;
prl N(8,2),cantl N(7),termenl D(8), cl M(4),;
foreign key codp tag codp references produse,;
foreign key codb tag codb references beneficiari)
close tables
close databases

procedura – cmdInreg_Click
thisform.release
do form frminreg

procedura – cmdcb_Click
thisform.release
do form frmcontrbenef

Forma de întegistrare frminreg conţine un grup de butoane de comandă, acţionarea unui buton
activând o formă specifică pentru introducerea datelor. În ceea ce priveşte operaţia de înregistrare a unui nou
contract s-a proiectat forma CONTRACTE (figura 2.10), pentru introducerea produselor şi a clienţilor
apelându-se însă la ajutorul FormWizard-ului – figura 2.11.

procedura - frmContracte_Activate
open database gest
use produse order codp alias pr in 1
use beneficiari order codb alias benef in 2
thisform.txtprl.value = ""
thisform.txtcantl.value = ""
thisform.txttermen.value = { / / }
thisform.edit1.value = ""

procedura – cmdInregistrare_Click
p = val (thisform.combo1.text)
b = val (thisform.combo2.text)
insert into contracte (codp,codb,prl,cantl,termenl,cl);
values p,b,val (thisform.txtprl.value),val (thisform.txtcantl.value),;
thisform.txttermen.value,thisform.edit1.value)
12

thisform.txtprl.value = ""
thisform.txtcantl.value = ""
thisform.txttermen.value = { / / }
thisform.edit1.value = ""
thisform.combo1.value = ""
thisform.combo2.value = ""

procedura – cmdQuit_Click
close tables
close databases
thisform.release
do form frminreg
13

Fig. 2.11 Forme generate cu ajutorul FormWizard-ului

Ne propunem să afisăm într-o fereastră Browse contractele destinate unui anumit beneficiar specificat
cu ajutorul unui control de tip ComboBox legat de cậmpul "codb" al tabelei "Beneficiari". Datorită relaţiilor
definite între cele trei tabele, în fereastra Browse vor fi afişate contractile solicitate.

procedura – frmcontrbenef_Activate
open database gest
use produse order codp alias pr in 1
use beneficiari order codb alias benef in 2
use contracte alias contr in 3

procedura – cmdOk_Click
b = val(thisform.combo1.text)
select 3
browse fields codp, pr.denp, prl, cantl for codb = b title benef.denb

4. Controale & elemente de programare avansată


A). În formularea primului exemplu am apelat la un control de tip ListBox ce va fi utilizat în
identificarea unui anumit element - figura 2.12. Procedura de căutare localizează un anumit cont, specificat
prin simbolul său, în planul de conturi – Planctb.dbf - vizualizat prin intermediul controlului de tip ListBox.

Fig. 2.12 Căutarea binară şi controlul de tip ListBox


14

procedura – cmdCauta_Click( )
if isblank (thisform.txtCautare.text)
messagebox("Completati text box-ul ''Cont cautat'' !",48,"Avertizare")
return
endif
cautat = thisform.txtCautare.value
inf = 1
sup = thisform.lstPlan_ctb.listcount
do while inf < sup
val_cautat = (inf+sup)/2
if cautat == thisform.lstPlan_ctb.list (val_cautat)
exit
endif
if cautat > thisform.lstPlan_ctb.list (val_cautat)
inf = val_cautat
else
sup = val_cautat
endif
enddo
if inf = sup
messagebox ("Contul "+cautat+" nu este un cont valid !",48,"Avertizare")
else
thisform.lstPlan_ctb.selected (val_cautat) = .T.
thisform.lstPlan_ctb.topindex = val_cautat
endif
thisform.txtCautare.value = ""

B). În următorul exemplu vom programa conţinutul unui control de tip ListBox cu ajutorul unei
interogări SQL. Proprietatea RowSourceType a unui control de tip listă specifică tipul sursei de date pentru
acesta. Optậndu-se pentru 3-SQL statement, în proprietatea RowSource se poate include comanda
SELECT – SQL pentru generarea listei de informaţii a controlului. Proprietatea RowSource se poate seta şi
prin program, caz în care comanda SELECT se va pune între ghilimele. În secvenţa de iniţializare a formei,
comanda SELECT afişează cậmpurile "nume" + "prenume", "nrbi", "nrpass" şi "idcli" într-un cursor.

Fig. 2.13 Programarea conţinutului unui ListBox

procedura – frmClienti_Init
select clients
15

set order to idcli


thisform.list1.rowsource = "select alltrim(nume)+' '+alltrim(prenume) as numcli,;
nrbi, nrpass, idcli from clients;
into cursor mylist order by nume,prenume"
if thisform.list1.listcount >= 1
thisform.list1.enabled = .T.
thisform.list1.listitemid = 1
thisform.cmddel.enabled = .T.
thisform.cmdmod.enabled = .T.
else
thisform.list1.enabled = .F.
thisform.cmddel.enabled = .F.
thisform.cmdmod.enabled = .F.
endif

C). Considerậnd baza de date evid – figura 1.13 – creată conform metodologiei prezentate în cadrul
paragrafului 1.6, vom prezenta un algoritm de înregistrare a unei note la o anumită disciplină dintr-un an de
studiu – figura 2.14.

(*)

Fig. 2.14 Interfaţa aplicaţiei

procedura – frmInreg_Activate
public zona
open database evid
* se populează primul ComboBox cu studentii luaţi în evidenţă
use date_pers order nr_leg in 5
thisform.cbostud.rowsource = "select date_pers.nr_leg from date_pers;
into cursor c"
* zona marcată cu ( * ) nu va apare pe ecran decật după ce s-a selectat un anumit student,
* dar ea se pregăteşte cu informaţiile adecvate apariţiei sale ulterioare; totodată se vor
* deschide, în zone diferite, cele patru fişiere de note;
< se ascunde zona ( * )>
for i = 1 to 4
a = "anul" + ltrim(str(i))
select (i)
use &a
endfor
thisform.optiongroup1.option1.value = 1
select 1
zona = 1
16

nr_campuri = afields(t)-1
for i = 1 to nr_campuri
nota = "n" + ltrim(str(i))
thisform.cbonote.additem((nota),i)
next i

procedura – cmdOkStud_Click( )
nr = val(thisform.cbostud.text)
seek nr in 5
* în cazul specificării unui student nou, acesta va fi luat în evidenţă şi i se va
* insera cậte o înregistrare în cele 4 fişiere de note
if not found(5)
insert into date_pers (nr_leg) values (nr)
for i = 1 to 4
a = "anul" + ltrim(str(i))
insert into &a (nr_leg) values (nr)
next i
endif
< se face vizibilă zona ( * )>

Disciplinele vizualizate de către cel de-al doilea ComboBox depind de anul selectat din cadrul
grupului de butoane de opţiune.

procedura – optiongroup1_Click( )
with thisform
.cbonote.clear
do case
case .optiongroup1.option1.value = 1
select 1
case .optiongroup1.option2.value = 1
select 2
case .optiongroup1.option3.value = 1
select 3
case .optiongroup1.option4.value = 1
select 4
endcase
zona = select(0)
nr_campuri = afields(t)-1
for i = 1 to nr_campuri
nota = "n" + ltrim(str(i))
.cbonote.additem((nota),i)
next i
endwith

În final, înregistrarea unei note efectuậndu-se după cum urmează, în variabila globală "zona"
fiind memorat anul selectat.

procedura - cmdOkNota_Click( )
select (zona)
camp_nota = thisform.combo1.value
replace &camp_nota with val(thisform.text1.value)

procedura – cmdQuit_Click( )
thisform.release
17

close databases

D). În exemplul următor – figura 2.15 este propusă o variantă de parcurgere a unor tabele legate,
fiind definite în acest scop două butoane de comandă speciale. Deplasarea în tabela părinte este
implementată cu ajutorul comenzii SKIP, fiind tratat explicit momentul atingerii sfậrşitului/începutului
fişierului. Controalele de tip TextBox utilizate sunt legate de cậmpurile al căror conţinut trebuie să-l
vizualizeze.

procedura – frmAfis_Activate( )
use salar order marca in 2
use personal order marca in 1
select 1
set relation to marca into 2

procedura – cmdUrmator_Click ( )
if not bof ( )
thisform.cmdPredecesor.Enabled = .T.
endif
if not eof ( )
skip
if eof ( )
skip -1
thisform.cmdUrmator.Enabled = .F.
endif
endif

procedura – cmdPredecesor_Click ( )
if not eof ( )
thisform.cmdUrmator.Enabled = .T.
endif
if not bof ( )
skip -1
if bof ( )
skip
thisform.cmdPredecesor.Enabled = .F.
endif
endif
18

Fig. 2.15 O variantă de parcurgere a tabelelor

2.6. Elemente de programare orientată


obiect
Un obiect 13 este un model informaţional al unei entităţi reale, care posedă o mulţime de proprietăţi
şi care are în acelaşi timp un anumit comportament. Având în vedere proprietăţile comune şi
comportamentul similar al entităţilor pe care le modelează, obiectele pot fi împărţite în mulţimi. O mulţime
de obiecte de acelaşi fel constituie o clasă, care poate fi descrisă prin modelul comun al obiectelor sale. Se
poate afirma că o clasă de obiecte se manifestă ca un tip de obiect, modelul comun al obiectelor constituind
modelul de definire a tipului obiect. În consecinţă, obiectele individuale constituie manifestări, realizări sau
instanţieri ale unei clase. O definiţie minimală a unei clase trebuie să cuprindă descrierea proprietăţilor şi
metodelor acesteia. Proprietăţile sunt acele trăsături ale obiectului care au fost reţinute ca importante în
procesul de abstractizare, iar cu ajutorul metodelor se defineşte comportamentul tuturor obiectelor din clasa
respectivă.

DEFINE CLASS <nume_clasă> AS <tip_bază1>

13
19

[<listă_proprietăţi1>]
[ADD OBJECT <nume_obiect> AS <tip_bază2>
[WITH <listă_proprietăţi2>]]...
[PROCEDURE <nume_procedură>
<comenzi>
ENDPROC]...
ENDDEFINE

Obiectele grafice aparţinậnd clasei <nume_clasă> sunt de tipul <tip_bază1> şi sunt înzestrate cu
proprietăţile specificate în <listă_proprietăţi1>. Ele pot conţine o serie de alte obiecte <nume_obiect> de
tipul <tip_bază2> şi avậnd proprietăţile specificate prin <listă_proprietăţi2>. Comportamentul tuturor
acestor obiecte este stabilit cu ajutorul procedurilor (metodelor) incluse în definiţia clasei. În exemplul de
mai jos fişierul de proceduri conţine definiţia unei clase de forme – frmutil, a unui grup de butoane de
opţiune – optutil, precum şi a unei liste - lstutil.

procedure def_clasa
define class frmutil as form
left = 300
top = 150
width = 375
height = 250
caption = 'Forma definita cu DEFINE CLASS'
add object cmdinreg as commandbutton;
with caption = 'Inregistrare',top = 40,left = 130,width = 115,;
height = 25, fontbold = .T.
add object cmdlist as commandbutton;
with caption = 'Listare',top = 80,left = 130,width = 115,height = 25,fontbold = .T.
add object cmdrev as commandbutton;
with caption = 'Revenire',top = 120,left = 130,width = 115,;
height = 25,fontbold = .T.
procedure cmdinreg.click
do form frminreg
endproc
procedure cmdlist.click
do form frmlist
endproc
procedure cmdrev.click
X.release
do form frmprinc
endproc
enddefine
define class optutil as optiongroup
buttoncount = 3
top = 60
left=75
height = 70
width = 110
procedure click
do case
case thisform.Y.value = 1
messagebox("S-a ales prima optiune !")
case thisform.Y.value = 2
20

messagebox("S-a ales a doua optiune !")


case thisform.Y.value = 3
messagebox("S-a ales a treia optiune !")
endcase
endproc
enddefine
define class lstutil as listbox
left = 80
top = 60
procedure click
for i = 1 to thisform.Z.listcount
if thisform.Z.selected(i)
messagebox("S-a selectat - " + thisform.Z.list(i))
endif
endfor
endproc
enddefine
return

obiectul X definit ca
instanţă a clasei frmutil

Fig. 2.16 Definirea şi utilizarea claselor de obiecte

procedura – frmprinc_click
public X
set procedure to fisproc

procedura – cmdcont_click
thisform.release
X = createobject("frmutil")
X.activate
X.cmdinreg.setfocus

procedura – cmdiesire_click
thisform.release
21

X, Y şi Z sunt definite ca variablile globale la nivelul aplicaţiei şi reprezintă obiecte componente ale
claselor definite – figura 2.16. Conform codului procedurii eveniment cmdinreg.click inclus în definiţia
clasei frmutil, acţionarea acestui buton aduce în prim plan forma frminreg. Apariţia ferestrei MessageBox
are loc în conformitate cu cele stabilite în codul procedurii eveniment Click incluse în definiţia clasei
optutil.

Fig. 2.17 Includerea obiectului Y în cadrul formei frminreg

procedura – frminreg_Activate
public Y
set procedure to fisproc
thisform.addobject('Y','optutil')
thisform.Y.option1.caption = 'Optiunea I'
thisform.Y.option2.caption = 'Optiunea a II-a'
thisform.Y.option3.caption = 'Optiunea a III-a'
thisform.Y.setall('width',110)
thisform.Y.visible = .T.
procedura – cmdrev_Click
release Y
thisform.release

În procedura eveniment Activate, asociată formei frmlist este inclusă secvenţa de creeare a obiectului
de tip listă Z, iar comportamentul său este descris în procedura eveniment Click specificată în cadrul
definiţiei clasei lstutil – figura 2.18.

procedura – frmlist_Activate
public Z
public tablou(3)
tablou(1) = "Unu"
tablou(2) = "Doi"
tablou(3) = "Trei"
set procedure to fisproc
thisform.addobject('Z','lstutil')
22

thisform.Z.rowsourcetype = 5
thisform.Z.rowsource = 'tablou'
thisform.Z.visible=.T.
procedura – cmdrev_Click
release Z
thisform.release

Fig. 2.18 Includerea obiectului Z în cadrul formei frmlist

Pe lângă un bogat sortiment de clase predefinite, Visual FoxPro dispune de facilităţi ce oferă
programatorului posibilitatea creării şi utilizării propriilor clase 14. Acesta va decide dacă va creea sau nu câte
o clasă pentru fiecare formă sau control pe care doreşte să-l utilizeze în cadrul aplicaţiei sale. Acest lucru
poate avea însă consecinţe nedorite în ceea ce priveşte fiabilitatea aplicaţiei, şi anume:
 o redundanţă nedorită, dată de clasele ale căror obiective se suprapun într-o
oarecare măsură, dar care trebuie întreţinute separat ;
 un grad redus de reutilizare a software-lui, dat de numai cele câteva utilizări ale
unei clase în gradul aplicaţiei şi/sau în cadrul altor aplicaţii.

14
23

INTEROGAREA
3
BAZELOR DE DATE

3.1. Elemente de SQL


Procesul prin care se pot obţine diverse informaţii dintr-o bază de date se numeşte interogare, iar
formularea acesteia se realizează cu ajutorul unei fraze SELECT.
1. Pentru definirea unei interogări de selecţie simple se poate utiliza următoarea sintaxă a instrucţiunii
SELECT:

SELECT [ALL | DISTINCT] [<alias1>.]<cậmp1> [,[<alias2>.] <cậmp2>...]


FROM <tabela1> [,<tabela2> ...]
[WHERE <expl >]
1

[ORDER BY [<aliasi>.]<cîmpi>]]
[INTO <destinaţie>]

Joncţiunea prezintă o importanţă fundamentală în elaborarea şi utilizarea bazelor de date relaţionale,


fiind operatorul ce permite "înlănţuirea" relaţiilor şi obţinerea de informaţii prin consultarea simultană a mai
multor tabele. Prin comanda SELECT de mai sus se efectuează joncţiunea naturală a tabelelor <tabela1>
şi <tabela2>. Cậmpul comun al acestora constituie cheia primară a primei tabele şi cheie străină în
<tabela2>, iar condiţia <expl1> este de forma <tabela1>.<cậmp_comun> = <tabela2>.<cậmp_comun>.
În plus, pentru realizarea joncţiunii naturale, atributele de legătură trebuie să aibă nume identic (şi domeniu
compatibil). Joncţiunea naturală presupune de asemenea eliminarea unuia dintre cele două atribute de
legătură din rezultatul interogării, constituind informaţie redundantă. In mod implicit, rezultatul interogării
este afişat într-o fereastră BROWSE, dar poate fi dirijat spre o tabelă distinctă (INTO TABLE <tabela3>) sau
poate fi o variabilă-tablou (INTO ARRAY <tablou1>). Pentru a elimina tuplurile identice din tabela rezultat
se va introduce clauza DISTINCT în sintaxa instrucţiunii SELECT. Ordinea de afişare a rezultatului cererii
de interogare poate fi impusă prin intermediul clauzei ORDER BY.
Pentru exemplificare, considerăm situaţia prezentată în figura 3.1, între tabelele "materiale" şi
"operaţii" fiind definită o relaţie permanentă de tipul 1:n pe baza valorilor cậmpului cod.
24

Fig. 3.1 Definirea unei interogări simple

O interogare de selecţie simplă (joncţiune naturală) se poate obţine cu ajutorul următoarei fraze
SELECT:

select materiale.cod, materiale.denumire, operatii.data, operatii.cantitate;


from materiale, operatii;
where materiale.cod = operatii.cod;
order by materiale.cod

2. În cadrul frazelor SELECT pot apare operatorii AND, OR, NOT, IN, IS NULL, BETWEEN şi
LIKE, cu ajutorul acestora fiind posibilă o construcţie mai amănunţită a interogărilor SQL precum şi o
creştere a complexităţii acestora. În SQL, pentru comparare, în afara clasicilor operatori ">", "≥", "<", "≤",
"=", "≠", mai pot fi definiţi şi alţi operatori – BETWEEN, LIKE, IN, IS NULL.
Considerăm cậteva exemple de utilizare a acestor operatori în definirea unor interogări simple ale
bazei de date din figura 3.1.

select materiale.cod, materiale.denumire, operatii.data_op, operatii.cantitate;


from materiale, operatii ;
where materiale.cod=operatii.cod and ;
operatii.data_op between {^2001-11-11} and {^2001-11-15};
order by materiale.denumire

În ceea ce priveşte operatorul LIKE, acesta se foloseşte pentru a compara un atribut de tip şir de
caractere cu un literal (constantă de tip şir de caractere). Rezultatul evaluării unui predicat ce conţine
operatorul IN este True, dacă valoarea lui <expr1> este egală cu una din valorile <expr2>, <expr3>, ...,
<exprn>:
<expr1> IN (<expr2>, <expr3>, ..., <exprn>)

select materiale.cod, materiale.denumire, operatii.data_op, operatii.cantitate;


from materiale, operatii ;
where materiale.cod=operatii.cod and ;
materiale.denumire like "d2" and operatii.cantitate in (100, 564, 700);
order by operatii.data_op
25

3. Pe lậngă interogările simple, SQL permite definirea unor cereri de interogare complexe
formulate cu ajutorul clauzelor GROUP BY şi HAVING, putậnd să implementeze şi diferitele tipuri de
joncţiuni JOIN precum şi combinaţiile UNION, INTERSECT, MINUS. Frazele SELECT aferente pot
să conţină şi funcţii predefinite de tip COUNT, SUM, AVG, MAX, MIN – figura 3.2.

SELECT [ALL | DISTINCT] <funcţie1> ([<alias1>.]<cậmp1>)


[,<funcţie2> ([<alias2>.] <cậmp2>)...]
FROM <tabela1> [,<tabela2> ...]
[WHERE <expl1>]
GROUP BY [<aliasi>.]<cậmpi> [,[<aliasj>.]<cậmpj>...]
[HAVING <expl2>]
[ORDER BY [<aliasi>.]<cîmpi>]]
[INTO <destinaţie>]

Clauza GROUP BY precizează cậmpul sau cậmpurile pe baza cărora se va efectua gruparea
înregistrărilor. Funcţiile <funcţie1>, <funcţie2>,... vor fi recalculate la nivelul fiecărei grupări. Clauza
HAVING permite introducerea unor restricţii care sunt aplicate grupurilor de tupluri, deci nu tuplurilor
"individuale" ca şi clauza WHERE.

select materiale.cod, materiale.denumire, sum(operatii.cantitate) ;


from materiale,operatii;
where materiale.cod=operatii.cod;
group by operatii.cod

Fig. 3.2 O cerere de interogare complexă

Grupurilor definite prin clauza GROUP BY de mai sus li se aplică o restricţie în ceea ce
priveşte data cậnd au fost efectuate operaţiile de intrare/ieşire de material. Un material va fi afişat,
doar dacă toate operaţiile sale de intrare/iesire au avut loc într-un anumit interval precizat (HAVING).
select materiale.cod, materiale.denumire, sum(operatii.cantitate) ;
from materiale,operatii;
where materiale.cod=operatii.cod;
group by operatii.cod ;
having operatii.data_op between {^2001-11-01} and {^2001-11-14}

4. În afara joncţiunii naturale, frazele SQL pot implementa atật echi-joncţiunea cật şi theta-
joncţiunea cu ajutorul clauzei INNER JOIN.
26

SELECT [ALL | DISTINCT] <funcţie1> ([<alias1>.]<cậmp1>)


[,<funcţie2> ([<alias2>.] <cậmp2>)...] | *
FROM <tabela1> INNER JOIN <tabela2> ON <expl1>
[ INNER JOIN <tabela3> ON <expl2> ...]
[WHERE <expl3>]
GROUP BY [<aliasi>.]<cậmpi> [,[<aliasj>.]<cậmpj>...]
[HAVING <expl4>]
[ORDER BY [<aliasi>.]<cîmpi>]]
[INTO <destinaţie>]

Joncţiunea tabelelor <tabela1> şi <tabela2> se realizează pe baza criteriului de asociere <expl1>.


Vor fi concatenate acele tupluri (înregistrări) din cele două tabele care satisfac condiţia de join <expl1>.
Condiţia de join se va formula pentru cậmpul de legătură al tabelelor. Echi-joncţiunea tabelelor presupune
utilizarea unui operator de tip egalitate "=" în criteriul de selecţie <expl1>, iar theta-joncţiunea a unui
operator relaţional oarecare.
Condiţii suplimentare de filtare a înregistrărilor tabelei rezultat pot fi specificate prin intermediul
clauzei WHERE <expl3>. În fraza SELECT se poate utiliza caracterul "*" pentru a referi toate cậmpurile
din structura tabelelor supuse operaţiei de joncţiune.
Pentru exemplificare, considerăm situaţia prezentată în figura 3.3, între tabelele "materiale" şi
"operaţii" fiind definită o relaţie permanentă de tipul 1:n pe baza valorilor cậmpului cod şi cod_material. O
interogare de selecţie complexă se poate obţine cu ajutorul următoarei fraze SELECT:

select materiale.cod, materiale.denumire,;


sum(operatii.cantitate), sum(operatii.cantitate)*materiale.pret;
from materiale inner join operatii on materiale.cod = operatii.cod_material;
group by materiale.cod
order by materiale.cod

Fig. 3.3 O cerere de interogare complexă ( INNER JOIN)

5. Cele trei tipuri de joncţiune prezentate (theta, echi, naturală) prezintă ca extensie, joncţiunea
externă. Ideea de bază a joncţiunii externe constă în a include în rezultat şi înregistrări dintr-o tabelă (sau din
ambele tabele), care prezintă valori ale cậmpului de legătură ce nu se regăsesc în cealaltă tabelă. Dacă
celelalte tipuri de joncţiune sunt comutative, în cazul joncţiunii externe trebuie specificat din care tabelă se
extrag liniile fără corespondent în celălalt tabel. De aceea, există joncţiune externă la stậnga şi joncţiune
externă la dreapta. La acestea se adaugă joncţiunea externă totală, care reprezintă reuniunea celor două.
27

SELECT [ALL | DISTINCT] <funcţie1> ([<alias1>.]<cậmp1>)


[,<funcţie2> ([<alias2>.] <cậmp2>)...] | *
FROM <tabela1>
LEFT | RIGHT | FULL [OUTER] JOIN <tabela2> ON <expl1>
[LEFT | RIGHT | FULL [OUTER] JOIN <tabela3> ON <expl2> ...]
[WHERE <expl3>]
GROUP BY [<aliasi>.]<cậmpi> [,[<aliasj>.]<cậmpj>...]
[HAVING <expl4>]
[ORDER BY [<aliasi>.]<cîmpi>]]
[INTO <destinaţie>]

Prin joncţiunea externă sunt adăugate în tabela rezultat şi înregistrările din cele două tabele, care nu au
participat la join. Acestea sunt completate cu valori nule pentru cậmpurile tabelei din stậnga join-ului (LEFT
JOIN), respectiv pentru cậmpurile tabelei din dreapta join-ului (RIGHT JOIN).

select materiale.cod, materiale.denumire, sum(operatii.cantitate);


from materiale left join operatii on materiale.cod = operatii.cod_material
group by materiale.cod
order by materiale.cod

În exemplul de mai sus este implementată o joncţiune externă la stậnga a tabelelor "materiale" şi
"operatii". În rezultatul de interogării astfel definite, va fi inclus şi ultimul material din tabela părinte, chiar
dacă nu există nici o înregistrare corespunzătoare în tabela fiu.
6. Operatorii asamblişti - reuniune, intersecţie şi diferenţă – prezintă operatori SQL dedicaţi şi pot
opera numai cu tabele unicompatibile.

<frază_SELECT1> UNION [ALL] <frază_SELECT2>

Pentru exemplificare, considerăm situaţia prezentată în figura 3.4, între tabelele "materiale",
"operaţii_11" şi "operaţii_12" fiind definite două relaţii permanente de tipul 1:n pe baza valorilor cậmpului
cod şi cod_material. Lista (se va memora într-o tabelă distinctă) cu toate operaţiile se poate obţine în felul
următor:

select materiale.cod,materiale.denumire,;
operatii_11.data_op,operatii_11.cantitate;
from materiale left join operatii_11 on materiale.cod = operatii_11.cod_material;
union all ;
select materiale.cod,materiale.denumire,;
operatii_12.data_op,operatii_12.cantitate;
from materiale left join operatii_12 on materiale.cod = operatii_12.cod_material;
into table lista
28

În tabela rezultat cậmpurile "data_op" şi "cantitate" pot avea valori nule. În această situaţie cậmpul
numeric "cantitate" va fi completat cu 0, iar "data_op" cu { }.

update lista;
set cantitate = 0, set data_op = {} where lista.cantitate is null

Fig. 3.4 Utilizarea operatorilor asamblişti în definirea unei interogări