Documente Academic
Documente Profesional
Documente Cultură
de masura I
Note de curs
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Cuprins
1 INTRODUCERE........................................................................................................................................................5
1.1 CE ESTE O BAZĂ DE DATE...................................................................................................................................5
1.2 AVANTAJELE UTILIZĂRII BAZELOR DE DATE.......................................................................................................6
1.3 INSTALAREA BAZEI DE DATE...............................................................................................................................7
1.3.1 Instalare Oracle 9i, 10g.............................................................................................................................8
1.3.2 Verificarea instalării bazei de date............................................................................................................9
1.3.3 Dezinstalarea bazei Oracle. Exportul informaţiilor din bază.................................................................10
1.3.3.1 Salvarea bazei de date.........................................................................................................................................10
1.4 CE ESTE LIMBAJUL SQL...................................................................................................................................11
2 NOŢIUNI DE BAZĂ DESPRE PROIECTAREA BAZELOR DE DATE........................................................12
2.1 DETERMINAREA SCOPULUI BAZEI DE DATE......................................................................................................12
2.2 MODELUL ENTITATE-ASOCIERE........................................................................................................................13
2.3 CONVERSIA ELEMENTELOR INFORMAŢIONALE ÎN COLOANE DE TABELE..........................................................15
2.4 SPECIFICAREA CHEILOR PRIMARE.....................................................................................................................17
2.5 CREAREA RELAŢIILOR DINTRE TABELE.............................................................................................................18
2.5.1 Crearea relaţiilor de tipul unu-la-mai-mulţi............................................................................................19
2.5.2 Crearea relaţiilor de tipul mai-mulţi-la-mai-mulţi..................................................................................19
2.5.3 Crearea relaţiilor de tipul unu-la-unu.....................................................................................................19
2.6 APLICAREA REGULILOR DE NORMALIZARE.......................................................................................................20
2.6.1 Prima formă normală...............................................................................................................................20
2.6.2 A doua formă normală.............................................................................................................................21
2.6.3 A treia formă normală..............................................................................................................................22
2.6.4 De ce trebuie adusă o bază de date la forma normală trei......................................................................23
2.7 RAFINAREA BAZEI DE DATE..............................................................................................................................24
3 CREAREA BAZEI DE LUCRU...........................................................................................................................26
3.1 CREAREA CONTULUI UTILIZATOR ÎN BAZA DE DATE........................................................................................26
3.2 CREAREA TABELELOR ÎN BAZA DE DATE..........................................................................................................26
3.3 TIPURI DE DATE DEFINITE ÎN BAZA DE DATE....................................................................................................27
4 CONSTRÂNGERI.................................................................................................................................................29
4.1 CONSTRÂNGERI DE TIP PRIMARY KEY (PK).....................................................................................................29
4.1.1 Completarea valorilor din cheile primare...............................................................................................29
4.1.2 Crearea și utilizarea indexilor.................................................................................................................30
4.1.3 Indexii accelerează căutarea, dar încetinesc modificările de date..........................................................30
4.2 CONSTRÂNGERI DE UNICITATE (UK)................................................................................................................30
4.2.1 Crearea constrângerilor de unicitate fără validarea datelor..................................................................31
4.2.2 Activarea și dezactivarea constrângerilor...............................................................................................31
4.3 CONSTRÂNGERI DE TIP "NOT NULL" (NN).......................................................................................................31
4.4 CONSTRÂNGERI DE TIP "FOREIGN KEY"...........................................................................................................32
4.5 CONSTRÂNGERI DE TIP "CHECK".......................................................................................................................32
4.6 DEFINIREA CONSTRÂNGERILOR ÎN BAZA DE DATE "STUD"...............................................................................32
5 INTRODUCEREA DATELOR ÎN TABELE......................................................................................................34
5.1 SINTAXA COMENZII INSERT...............................................................................................................................34
5.2 VALOAREA NULL............................................................................................................................................34
Page 2 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Page 3 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
11.3 TRIGGERI...........................................................................................................................................................76
11.3.1 Completarea automată a coloanelor de tip PK.......................................................................................77
11.3.2 Verificarea notelor obţinute de studenţi..................................................................................................78
12 BIBLIOGRAFIE................................................................................................................................................80
13 ANEXE................................................................................................................................................................81
13.1 INSTRUCTIUNILE SQL PENTRU CREAREA OBIECTELOR ÎN BAZA DE DATE........................................................81
13.1.1 Creare tabele............................................................................................................................................81
13.1.2 Introducerea date în bază........................................................................................................................85
13.2 FRAZE SELECT ÎN BAZA DE DATE......................................................................................................................89
13.2.1 Studenti-grupe-discipline-note.................................................................................................................89
13.3 Creare useri, import, export..............................................................................................................................90
Page 4 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
1 Introducere
Prezentul indrumar de curs se adreseaza studentilor de la Facultatea de Inginerie Electrica, Energetica si
Informatica Aplicata ce studiaza disciplina " Programarea sistemelor de masura I".
Acest curs se vrea un îndrumar pentru cei care vor să se iniţieze în domeniul bazelor de date, ca o prima
componenta necesara in dezvoltarea unei aplicatii de programare majora. Este un curs introductiv destinat
în principal utilizatorilor bazelor de date, adică celor ce accesează baza pentru obţinerea de informaţii utile,
fără să cunoască în mod obligatoriu cum a fost proiectată baza de date sau alte tehnici de administrare a
bazei. Se axează în principal pe instrucţiunile SQL (Structured Query Language) cu care utilizatorul obișnuit
trebuie să lucreze pentru a introduce informaţii în bază, să modifice aceste informaţii în timp și să scoată
apoi rapoarte și analize complexe specifice domeniului de lucru.
Ca metodă de lucru, pe parcursul cursului se va proiecta o bază de date minimală care să stocheze
informaţiile specifice activităţii dintr-o unitate de învăţământ superior: studenţii înscriși în facultate, grupele
de care aparţin, specializările, disciplinele studiate și notele obţinute. Fiecare cursant va trebui să treacă
prin toate fazele necesare realizării acestui sistem de stocare și administrare a datelor: crearea tabelelor,
introducerea datelor în tabele, modificarea datelor, selectarea și filtrarea datelor, realizarea de situaţii
specifice activităţii didactice.
Page 5 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Este mult mai simplu să utilizăm un sistem de gestionare a informaţiei care să primească toate datele
referitoare la activitatea didactică din facultate şi apoi să ne dea informaţia de care avem nevoie pentru un
student anume. Acest sistem este baza de date.
Alt exemplu ce justifică necesitatea bazelor de date este dat de gestionarea activităţii într-o întreprindere.
Suntem într-o întreprindere de câteva mii de angajaţi. Activitatea întreprinderii este împărţită pe diverse
servicii: aprovizionare, desfacere, întreţinere a echipamentelor, producţie, serviciul financiar, contabilitate,
magazia, salarizare, personal , etc.
Să luăm magazia: aici trebuie ţinută evidenţa tuturor articolelor necesare în procesul de producţie. Sunt
sute de mii de articole: de la articole mărunte de tip şurub, pix, electrod sudură, la subansambluri,
semifabricate şi produse finite destinate desfacerii. Fiecare articol poate proveni de la mai mulţi furnizori
având preţuri şi calităţi diferite. Când se face o intrare în magazie a unui articol, se completează un
document care merge la serviciul financiar ce ţine evidenţa facturilor şi a clienţilor, documentul se contează
apoi la serviciul contabilitate ce ţine evidenţa pe conturi de cheltuieli şi aşa mai departe. În orice moment
trebuie ştiută situaţia cantitativă şi valorică a magaziei. Cei de la producţie lansează în fabricaţie un nou lot;
există piesele necesare în magazie? La ce preţ va rezulta noul produs ţinând cont că s-au utilizat şuruburi
din loturi diferite şi la preţuri diferite? Care este valoarea articolelor din magazie? Care este situaţia contului
de TVA? Câte produse sunt în magazie pentru desfacere? Aceste situaţii trebuie date în condiţiile în care
sunt mai multe magazii care suportă tranzacţii de intrare-ieşire în mod permanent.
Soluţia acestei probleme complexe este baza de date. O singură bază de date ce rulează pe server, toţi
utilizatorii lucrează simultan pe bază astfel încât să avem în orice moment situaţia cumulată a tuturor
tranzacţiilor. În orice moment persoanele din conducere pot vedea ce clienţi au datorii mai mari de o limită
sau mai vechi de un anumit termen, situaţia băncilor, situaţia salariilor, cash-flow-ul, etc şi pe baza acestor
informaţii să ia decizii în cunostinţă de cauză.
Este clar ca astăzi nu mai poate fi construită o aplicaţie serioasă care să nu aibă în spate o bază de date.
De aceea, la orice interviu de angajare la o firma din domeniul informatic se cer cunoștinţe minime de SQL
şi baze de date.
Baza de date reprezintă un depozit unic al tuturor datelor dintr-o organizaţie. Spre deosebire de stocarea
datelor în fișiere proprii, dispersate în formate diferite prin departamente diferite și de multe ori redundante,
baza de date stochează o singură dată o informaţie care este vizibilă și poate fi accesată de mai mulţi
utilizatori simultan.
Acest mod unic de stocare a datelor oferă posibilitatea implementării unor funcţionalităţi foarte avantajoase
și utile în procesarea informaţiilor:
Page 6 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Reducerea redundanţelor: datele stocate în fișiere dispersate conţin în foarte multe cazuri dubluri
ale datelor. De exemplu, în cadrul unei facultăţi, numele si prenumele unui student se regăsește în
fișierele pe care le ţine profesorul, apoi trebuie să fie și la secretariat, la serviciul social, etc.
Aceeași informaţie stocată de mai multe ori. Este mult mai avantajos să fie stocată o singură dată
în baza de date unică și accesată de toate departamentele.
Evitarea inconsistenţelor: dacă un student își modifică adresa de exemplu, această modificare ar
trebui procesată în toate fișierele de la diverse departamente. Dacă un anumit departament nu
modifică adresa (din diverse motive: nu știe ca s-a schimbat adresa, sau uită pur și simplu, sau
introduce greșit noua adresă), atunci datele stocate în fișiere vor fi inconsistente: aceeași
informaţie va avea valori diferite la departamente diferite. In cazul utilizării bazei de date, acest
lucru nu este posibil deoarece acea informaţie este stocată o singură dată.
Aplicarea restricţiilor de securitate a datelor: se pot defini reguli de securitate prin care se permite
accesul utilizatorilor pe bază de parolă, iar fiecare utilizator are acces doar la un set predefinit de
date. De exemplu, un student nu trebuie să aibă posibilitatea de a modifica o notă, sau un profesor
să pună note la discipline la care nu este repartizat.
Aplicarea regulilor de validare a integrităţii datelor: se pot defini diverse constrângeri de validare a
datelor care să nu permită introducerea de date eronate în bază. De exemplu, baza de date nu
permite înscrierea unui student la grupa 6411, dacă această grupă nu există în structura
organizatorică a facultăţii.
Independenţa între date și programele de aplicaţii: pot fi construite o mulţime de aplicaţii care să
lucreze cu aceeași bază de date: o pagina WEB de afișare a listelor de studenţi, o aplicaţie
desktop pentru profesor care să gestioneze notele la o disciplină, etc. Dezvoltatorul de aplicaţii nu
trebuie să se preocupe de modul de stocare a datelor, ci doar de modul de regăsire și modificare a
acestora. In plus, se poate schimba structura bazei de date fără a necesita modificarea aplicaţiilor.
Independenţa include două componente:
o Independenţa fizică: stocarea efectivă a datelor în fișiere pe disc este problema
sistemul de gestiune a bazei de date (SGBD), programatorul de aplicaţii nu trebuie să
aibă in grijă acest aspect. Se pot schimba locaţiile acestor fișiere fără a afecta în niciun
fel programele de aplicaţii.
o Independenţa logică: se poate modifica structura logică a bazei de date (creare de noi
tabele, modificarea relaţiilor, a structurii de tabele) fără a afecta programele de aplicaţii.
In acest mod, programatorul și administratorul bazei de date pot să-și întreţină
aplicaţiile în mod independent, atât timp cât se respectă interfaţa stabilită între aceste
aplicaţii.
Page 7 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Observaţie: din comoditate, s-a încetăţenit în limbajul curent formularea "bază de date" atât pentru baza de
date efectivă (cea care conţine datele), cât și pentru Sistemele de Gestiune a Bazelor de Date (SGBD)
(sistemul prin care comunic cu baza de date). De exemplu, Oracle reprezintă un SGBD în care se pot crea
și dezvolta diverse baze de date: pentru studenţi, pentru instituţii, etc, dar în practica curentă spunem "o
bază de date Oracle". In documentul curent folosim acest termen pentru ambele noţiuni, în speranţa că se
deduce din context termenul la care se face referire.
Versiunea 9i a serverului Oracle Server include 3 CD-uri. Se introduce primul CD în unitate şi se parcurge
toţi paşii programului de instalare dând click pe butonul Next la toate ferestrele Wizard-ului de instalare.
Binenţeles, nu poţi scăpa peste tot cu Next, undeva te întreabă cum se va chema baza de date pe care vrei
s-o construieşti (noi punem "stud") , te mai întreabă parolele pentru utilizatorii "sys" şi "system". Aici daţi ce
parolă vreţi, dar e bine să nu intraţi cu aceste parole în bază. Vom crea un utilizator "stud" cu parola "stud"
în schema căruia vom construi toate tabelele de care avem nevoie.
Daca totul merge OK , la sfârşitul instalării vor apărea pe disc un folder "Oracle" şi mai multe servicii cu
denumirea Oracle (programul "Services" din Settings/Control Panel/Administrative Tools/Services pentru
sistemul de operare Win XP). După cum aţi presupus, baza de date lucrează ca un serviciu Windows.
Page 8 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Serviciile Windows sunt programe care se lansează automat la pornirea calculatorului. Dintre toate
serviciile instalate de Oracle, ne interesează două: "OracleServiceStud" dacă am denumit baza de date
"stud" şi "OracleOraHome92TNSListener". Primul este baza de date, al doilea este serviciul ce se ocupă de
comunicaţii (ascultă la port comenzile către bază şi le transmite mai departe la baza de date).
Baza Oracle ocupă o bună parte din memoria RAM a calculatorului. De aceea, dacă nu avem treabă cu
baza la un moment dat, e bine s-o oprim ca să eliberăm din resursele hardware ale calculatorului.
Cum oprim baza? Mergem în Services , click dreapta pe serviciul "OracleServiceStud" şi selectăm "Stop".
Problema e ca baza porneşte din nou la următoarea startare a calculatorului. Ca să scăpăm de acest lucru,
mergem la acelaşi serviciu , click dreapta, şi selectăm "Properties". Selectăm la opţiunea "Startup Type"
valoarea "Manual". Din acest moment baza nu va porni decât atunci când mergem noi în Services şi dăm
"Start" pe acel serviciu. Binenţeles, dacă baza nu-i pornită, nu o putem utiliza.
Primul lucru care-l facem după instalarea bazei este să verificăm funcţionarea acesteia. Există un program
în Start\Programs\Oracle-OraHome92\Enterprise Manager Console. Putem intra în acest program şi să
gestionăm baza de date. Ne întreabă un user şi o parolă unde introducem "system" cu parola
corespunzatoare.
Trebuie să ne obişnuim că orice acces la baza de date se face prin parolă. Baza conţine informaţii de
importanţă capitală pentru instituţia respectivă (întreprindere, bancă, ...). De aceea nu putem lăsa pe oricine
să acceseze şi să modifice informaţia.
Chiar daca Enterprise Manager Console este un program agreabil, ce ne poartă prin toată bucătăria bazei,
nu vom lucra cu acest program, tocmai că este prea puternic pentru noi şi putem strica ceva în mod
ireversibil. Acest program este pentru administratorii bazelor de date, cei care se asigură ca baza să
funcţioneze şi răspund pentru acest task.
Pentru un utilizator obișnuit al bazei de date, vom utiliza un editor de SQL prin care vom scrie comenzi
către baza de date. Un astfel de editor care vine cu baza este Start\Programs\Oracle-
OraHome92\Application development\SQL Plus. Ne întreabă iarăşi user, parola şi "Host String", adică
numele bazei de date pe care ne conectăm. Evident, aici tastăm "stud". Putem avea surpriza ca programul
să ne întoarcă o eroare de tipul "ERROR: TNS: could not resolve service name", adică nu a găsit baza pe
care noi am vrut să ne conectăm. Si asta din diverse motive: nu am tastat corect numele bazei sau, nu am
Page 9 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
realizat corect legătura la baza de date. Când lucrăm pe acelaşi calculator în care este instalată baza,
legătura se face automat şi este un fişier în "D:\oracle\ora92\network\admin\tnsnames.ora" care descrie
această legătură:
STUD =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = lucian)(PORT = 1521))
)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = stud)
)
)
Dar când lucrăm pe alt calculator şi ne conectăm la baza de pe calculatorul server prin Oracle Client trebuie
să avem grijă ca fişierul tnsnames.ora de pe calculatorul local să aibă definiţia legăturii corectă. Fraza din
tnsnames.ora definește protocolul de comunicaţie între client şi bază (se foloseste TCP), numele
calculatorului server (HOST='....'), (aici se poate înlocui cu IP-ul acestuia sub forma: 192.161.2.3. Pentru a
afla IP-ul calculatorului intraţi într-o fereastra cmd (start\run... : cmd) şi daţi comanda:"ipconfig").
SQL Plus nu reprezintă o interfaţă grafică cu baza de date, este doar un editor text unde utilizatorul poate
scrie și executa comenzi SQL. Pentru o interfaţă mai agreabilă putem utiliza programul SQL Navigator
produs de firma Quest Software. Acest program pune la dispoziţie pe lângă editorul de SQL şi un Object
Navigator prin care putem vedea toate obiectele din baza de date, putem edita obiecte sau crea altele noi.
Dacă ceva s-a blocat la instalare, sau baza nu mai merge, sau efectiv vrem să facem curaţenie pe
calculator, trebuie să dezinstalăm baza Oracle şi s-o instalăm din nou. Atenţie: la dezinstalare se pierde
toată informaţia din bază. Când nu vrem să pierdem toate tabelele cu datele incluse, mai întâi trebuie dat
un export la bază, să salvăm într-un fişier conţinutul acesteia.
La instalarea serverului Oracle s-a instalat şi un produs numit EXP în directorul " D:\oracle\ora92\bin" (dacă
baza s-a instalat pe D). Programul EXP este utilizat pentru salvarea bazei Oracle într-un fişier. Se deschide
o fereastră Command Dos (Start/Run... cmd) şi se introduce comanda:
exp stud/stud@stud file=c:\stud.dmp log=c:\stud.log grants=N recordlength=8192
buffer=30000000
sau: exp bd1/bd1@stud file=bd1.dmp
Page 10 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Deci se exportă toate obiectele care ţin de user-ul stud (se intra cu parola "stud" pe baza "stud"). Fişierul
rezultat "stud.dmp" va fi pe discul C şi fişierul de mesaje "stud.log" va fi salvat pe acelaşi disc.
După instalarea unei noi baze de date şi crearea user-ului "stud" se pot importa informaţiile din
baza veche cu comanda:
imp adm/adm@stud fromuser=adm touser=adm file=c:/adm.dmp log=c:/adm_imp.log grants=N
recordlength=8192 buffer=30000000 commit=y
Curaţenia totală se face după ce intrăm în regiştri şi stergem directoarele Oracle găsite acolo. Deci se intră
în regiştrii Windows (Start/Run... şi se tastează "regedit"). Intrăm în editorul de regiştri şi căutăm în folderul
"HKEY_LOCAL_MACHINE / SOFTWARE" directorul "ORACLE". Click dreapta pe acest director şi dăm
comanda delete. Mergem apoi în directorul "HKEY_LOCAL_MACHINE / SYSTEM / ControlSet001 /
Services" şi ştergem toate directoarele care încep cu secvenţa "Oracle". În sfârşit, mergem şi în
"HKEY_LOCAL_MACHINE / SYSTEM / ControlSet002 / Services" unde facem aceeaşi curăţenie şi putem
spune că am scăpat de serviciile Oracle.
Atenţie: nu modificaţi registrii Windows fără o bună înţelegere a celor ce doriţi sa schimbaţi, orice
modificare greşită aici poate duce la blocarea anumitor programe sau chiar a calculatorului. Singura
scăpare este reinstalarea sistemului de operare Windows cu o formatare prealabilă a discului, ceea ce
consumă timp, resurse și pierdere de informaţii.
După ştergerea serviciilor Oracle din Registry puteţi şterge în sfârşit directorul ORACLE de pe disc.
Dacă vă dă eroare "acces denied" daţi restart la calculator, să-şi recitească regiştrii şi să nu mai pornească
serviciile Oracle.
Page 11 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Se merge mai departe cu butonul „Next” si se accepta termenii si conditiile cerute de Oracle
Oracle propune un folder default pentru instalare pe discul C:
Intrucat acest folder este relativ ascuns, putem alege un alt folder: C:\\Oracle, sau pe orice alt disc din
calculator.
Implicit la instalare se creeaza trei conturi de administrare a bazei de date: SYS, SYSTEM,
PDBADMIN. Se cere parola pentru aceste conturi:
Page 12 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
ATENTIE: nu trebuie uitata aceasta parola pentru ca o vom utiliza pentru conectare la baza de date nou
creata.
Click pe butonul „Next” si incepe procesul de instalare a bazei de date:
Dureaza cateva zeci de minute, depinde de puterea calculatorului, dupa instalarea cu succes a bazei de
date, este afisata fereastra:
Page 13 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Procesul de dezinstalare face curat pe calculator, opreste si elimina serviciile Oracle. Folderul
„Oracle” nu este sters automat, dar se poate sterge din Windows fara probleme deoarece nu mai
este blocat de serviciile din background.
https://drive.google.com/drive/folders/1SW2ttn0VXNdUEkz2NfGeoYb6By9N78ua?usp=sharing
Aplicatia care trebuie rulata este „sqldeveloper.exe” si care deschide interfata pentru conectarea si lucrul cu
baza de date:
Page 14 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Primul pas este sa ne conectam la baza de date cu user-ul „sys” si sa cream un cont de lucru
pentru dezvoltarea bazei ce dorim s-o dezvoltam in cadrul cursului:
Click dreapta pe „Oracle Connection”, selectam „New connection” si definim conexiunea pentru
userul sys (ATENTIE: conectarea pe user-ul sys se poate face doar ca „sysdba”, de aceea
trebuie selectata aceasta optiune din lista „Role”):
By default in locatia server-ului (Hostname) este trecut „localhost”, adica computerul curent. Daca butonul
„Test” afiseaza erori de tipul „network adapter could not establish the connection”:
Page 15 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Nu se lucreaza in contul lui SYS, se creeaza un nou cont de lucru. Se ruleaza (butonul verde)
urmatoarele comenzi folosind sesiunea deschisa in SYS:
Page 16 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Prima instructiune ne permite sa cream useri fara restrictii referitoare la prefixul acestor nume. Apoi se
creeaza user-ului si i se acorda drepturi de tip DBA (Data Base Adminstrator), drepturi depline asupra bazei
de date. Ultima instructiune este de test, se verifica daca noul user se poate conecta la baza de date.
Din acest moment, oricand se poate deschide SQL Developer, selecta conexiunea de lucru si apoi
introduce comenzi SQL pe acest cont.
SQL (Structured Query Language) este un limbaj prin care comunicăm cu baza de date. Vrem să
introducem un student în tabela studenţi: scriem o comandă SQL care va fi înţeleasă şi executată de bază:
insert into studenti (nume) values ('Popescu Ionel').
Dacă dorim să vedem studenţii din baza de date, atunci trebuie să execut o altă instrucţiune SQL:
select nume_student from studenti;
Page 17 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Sunt câţiva pași ce trebuie parcurși in vederea proiectării corecte a unei baze de date:
Determinarea scopului bazei de date ce trebuie proiectată
Găsirea informaţiilor ce trebuie stocate în baza de date și împărţirea lor pe tabele
Specificarea cheilor primare și a relaţiilor între tabele
Aplicarea regulilor de normalizare
In continuare vom descrie acești pași prin proiectarea unei baze de date minimale ce gestionează
informaţiile specifice studenţilor dintr-o facultate: nume, prenume, grupă, discipline studiate, note obţinute,
etc.
Foarte multe probleme ce apar între clienţi și dezvoltatorii bazelor de date se datorează unei analize
defectuoase a scopului pentru care se creează o bază de date. In principiu, clientul este cel care știe ce
activitate se desfășoară în instituţia respectivă, dar nu știe cum s-o explice în termenii unei baze de date.
Abia după ce s-a implementat baza de date, încep să apară probleme în sensul că clientul cere situaţii și
rapoarte care nu pot fi realizate pentru că nu există informaţia necesară în bază.
De aceea trebuie acordată o atenţie corespunzătoare acestei etape, pentru a defini foarte clar ce informaţii
trebuie să conţină baza de date și ce situaţii se cer a fi realizate.
Page 18 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Nu toţi studenţii parcurg acelaşi domeniu, facultatea având mai multe specializări de studiu,
(Maşini Electrice, Metrologie, Informatică Aplicată, etc), fiecare specializare fiind formată
din una sau mai multe grupe de studenţi.
In fiecare semestru dintr-un an de studiu studenţii trebuie să studieze un număr de
discipline specifice specializării la care s-au înscris. Fiecare disciplină se predă la o
anumită specializare şi într-un anumit an de studiu.
Semestrele se încheie cu o sesiune de examinare unde studenţii primesc note la
disciplinele studiate.
Scopul bazei de date este de a stoca aceste informaţii astfel încât să fie capabilă să furnizeze următoarele
rapoarte:
Lista studentilor din facultate
Lista studentilor pe grupe şi ani de studiu dintr-un anumit an universitar
Lista disciplinelor grupate pe specializările din facultate
Lista disciplinelor ataşate unui student într-un an universitar împreună cu notele obţinute de acesta
Mediile generale ale studenţilor, media pe grupe şi discipline de studiu
Studenţii cu rezultate bune la învăţătură, având media superioară grupei din care fac parte
Studenţii care repetă un anumit an de studiu
Etc
Page 19 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Entităţile se reprezintă prin dreptunghiuri la care se atașează atributele reprezentate prin cercuri, iar
asocierile prin romburi sau poligoane regulate.
Figura următoare prezintă diagrama EA pentru baza de date pe care dorim s-o implementăm:
Page 20 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Pentru simplitate, nu s-au trecut și atributele entităţilor, dar acestea vor fi descrise în cadrul tabelelor.
Pot exista asocieri cu atribute: de exemplu asocierea dintre studenţi și grupe se face prin intermediul anilor
universitari. Un an universitar nu ţine nici de entitatea "Studenti", nici de entitatea "Grupe", deci va fi un
atribut al asocierii. Atributele se trec în dreptul asocierii și vor fi implementate prin entităţi independente în
pasul următor.
In baza de date, informaţia este stocată sub formă de tabele, pentru fiecare entitate din modelul EA se
creează un tabel având câte o coloană pentru fiecare atribut al entităţii.
Conform observaţiilor de mai sus, ar rezulta următoarea structură de tabele necesară pentru stocarea
informaţiilor specifice activităţii didactice :
1. Tabela "Specializari": este tabela ce conţine specializările din facultate. Este o tabelă
independentă, structura specializărilor nu depinde de studenţi sau grupe, ci este dată de profilul
facultăţii.
Include următoarele coloane:
Cod_specializare: (ex: "IAD", "IA", ...) un text ce descrie simplu specializarea
Descriere_specializare: (ex: "Instrumentatie si Achizitii de Date"), reprezintă denumirea
completă a specializarii
Cod_domeniu: codul domeniului de care aparţine specializarea (Inginerie Electrică,
Electromecanică, etc)
Nume_domeniu
Page 21 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
2. "AniStudiu": este tabela ce conţine anii de studiu pe care îi parcurge un student în decursul
facultăţii.
Include următoarele coloane:
Cod_an_studiu: (ex: "1", "2", ...) un text ce reprezintă anul de studiu
Descriere_an_studiu: (ex: "Anul I", "Anul II", ...), reprezintă descrierea anului de studiu
3. "Discipline": este tabela ce conţine disciplinele studiate în facultate.
Cod_disciplina: (ex: "BD", "ME", ...) un text ce descrie simplu disciplina
Descriere_disciplina: reprezintă denumirea completă a disciplinei
4. "Grupe": este tabela ce conţine grupele de studenţi care se formează în facultate. Grupele depind
de specializare şi de anul de studiu: la specializarea "IA", anul 3 este o singură grupă: "6309".
Include următoarele coloane:
Cod_grupa: (ex: "6102", "6301", ...)
Descriere_grupa: (ex: "Grupa 6301")
5. "Studenti": este tabela cu toti studenţii din facultate, conţine informaţiile statice ale unui student.
Include următoarele coloane:
Nume_student
Prenume_student
Adresa
6. Tabela "Note": ţine evidenţa notelor obţinute de studenţi în timpul examinărilor.
Nota: un număr cuprins între 1 şi 10 reprezentând nota luată de student
Data: data examinării, de tipul "date"
7. "AniUniversitari": reprezintă lista anilor universitari (2010/2011, 2011/2012, etc). Toate informaţiile
dinamice ale facultaţii trebuie legate de un anumit an universitar, astfel încât să se poată merge în
istoricul facultăţii pe un anumit an universitar și să se obţină informaţiile specifice pentru acel an. De
exemplu, legatura dintre studenţi și grupe se face prin intermediul anilor universitari, plecând de la
un an universitar, putem afla în ce grupă a fost înscris un anumit student.
Cod_AnUniversitar: (ex: "2011/2012", "2012/2013", ...)
Descriere_AnUniversitar: (ex: "Anul Universitar 2011/2012")
Page 22 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
o Adresa nu se pune într-o singură coloană: indicat ar fi să avem câte o coloană pentru
stradă, numărul străzii, bloc, scara, etaj. Chiar dacă momentan nu este evidentă această
separare, este posibil în viitor să se ceară o situaţie cu toţi studenţii de pe o anumită stradă,
sau studenţii care domiciliază la parter, etc. Cum baza noastră de date are un scop pur
demonstrativ, punem pentru simplitate informaţia "Adresa" într-o singură coloană.
Nu se pun coloane pentru valori calculate: de exemplu, nu este indicat să fie o coloană cu media
studentului. Aceasta se calculează printr-un apel de funcţie în momentul când este necesar, în caz
contrar, ar trebui recalculată la orice modificare a tabelului note, ceea ce poate constitui o sursă de
erori (dacă se uită de exemplu să se recalculeze la un moment dat, sau se termină calculul printr-o
eroare).
Orice tabel trebuie să conţină un identificator unic al liniilor cu informaţii. Numai în acest fel se poate regăsi
în mod unic și corect o anumită linie conform unor criterii de căutare. Acest identificator unic se numește
cheie primară și poate fi formată din una sau mai multe coloane a căror combinaţie este unică.
De exemplu, pentru tabela Studenţi am putea alege ca identificator unic numărul matricol al studentului.
Chiar dacă am fi tentaţi să folosim ca cheie primară numele studentului, această opţiune nu este bună,
deoarece oricând se pot găsi doi studenţi cu același nume sau mai mult, nume și prenume identice.
O altă opţiune simplă dar greșită ar fi să alegem ca identificator pentru tabela Grupe coloana "Cod_Grupa".
In fond, există o singură grupă cu acest cod în facultate, deci nu ar fi probleme de identificare. Totuși,
trebuie să privim lucrurile și pe axa timpului: este posibil ca peste doi ani grupa 6302 să nu mai aparţină de
specializarea "IAD", ci datorită numărului de studenţi mai mic din acea generaţie, grupa 6302 să trecă la
specializarea "Informatică Aplicată". Este același cod de grupă, dar vorbim de grupe diferite în decursul
anilor.
Soluţia cea mai sigură din acest punct de vedere este să adăugăm câte o coloană distinctă la fiecare tabel
care să conţină valori numerice unice pentru fiecare linie din tabel. Cunoscând acel număr , putem identifica
în mod unic linia din tabela de interes. Bazele de date pun la dispoziţie mecanisme de generare a
numerelor unice, astfel încât, aceste coloane să se completeze automat de către baza de date la inserarea
unei linii noi în tabelă (vom studia aceste mecanisme în cadrul capitolului "Constrangeri de tip
primary_key").
Ne conformăm acestor reguli de identificare a liniilor din tabele și adăugăm la toate tabelele câte o coloană
numită "pk_...", unde punctele din denumire vor fi înlocuite cu numele tabelului în cauză. In acest fel,
schema logică a tabelelor bazei de date evoluează spre următoarea configuraţie:
Page 23 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Dacă între tabelele bazei de date nu există asocieri, nu vom putea niciodată sa legăm informaţiile dintr-o
tabelă cu cele din altă tabelă. De exemplu, grupa '6403' de care specializare aparţine? Sau nota 7 din
tabela note, de care student și disciplină aparţine?
Toate asocierile definite în diagrama EA trebuie să se regăsească în structura tabelelor, deci următorul pas
în crearea bazei de date îl reprezintă definirea relaţiilor dintre tabele. Relaţiile de legătură între tabele se
creează prin folosirea de coloane comune, de joncţiune. Cele două tabele asociate au fiecare câte o
coloană ce conţin date identice. Plecând de la valoarea dată într-un tabel, se face joncţiunea cu cel de-al
doilea tabel prin selectarea liniilor care au aceeași valoare în coloana comună. De exemplu, în tabela
"Grupe" se introduce o coloană numită "pk_specializare" ca va conţine identificatorul specializării de care
aparţine acea grupă. Având o grupă dată, se regăsește valoarea "pk_specializare" pentru acea grupă și cu
acea valoare se merge în tabela "Specializări" unde vom găsi mai multe informaţii specifice specializării.
Page 24 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Este relaţia întâlnită cel mai des, exprimă în general apartenenţa unei entităţi la o clasificare mai largă. Din
analiza diagramei EA, observăm că trebuie create următoarele relaţii:
Grupe - AniStudiu: fiecare grupă aparţine de un an de studiu. Se introduce în tabela "Grupe" o
coloană numită "pk_AniStudiu" care va face legătura spre anul de studiu asociat grupei respective.
O grupă aparţine de un singur an de studiu, în timp ce un an include mai multe grupe (relaţie unu-
la-mai-mulţi".
Grupe - Specializări: o grupă ţine de o singură specializare, o specializare poate include mai multe
grupe
Discipline - AniStudiu, Discipline - Specializari: o disciplină se predă la un an de studiu și la o
anumită specializare
Note - Discipline, Note - Studenti: o notă se acordă unui student la o anumită disciplină. Studenţii
au mai multe note la diverse discipline, dar o singură notă la o disciplină dată.
Observaţie: în realitate, un student poate primi mai multe note la aceeași disciplină, conform cu cele trei
sesiuni de examinare permise de regulament. In acest caz, relaţia ar fi de tipul "mai-mulţi-la-mai-mulţi". Din
motive de simplitate, considerăm doar nota finală a studentului, deci ne limităm la relaţia "unu-la-mai-mulţi".
Relaţia între studenţi și grupe este mai complexă deoarece un student aparţine de o grupă într-un anumit
an universitar, dar în următorul an studentul îsi schimbă grupa. Vom introduce două coloane în tabela
Studenti, una care să identifice anul universitar (pk_AniUniversitari), și una pentru grupa (pk_Grupe) din
care face parte studentul în acel an universitar. In fiecare an universitar se va adăuga o linie nouă în tabela
Studenti prin care se va specifica grupa la care este înscris studentul în acel an.
Această relaţie se creează atunci când sunt coloane cu puţine linii completate în cazul unor tabele de mari
dimensiuni. Nu este indicat să se rezerve o coloană într-un astfel de tabel, coloană care să aibă informaţii
utile doar foarte rar, în rest având valoarea "null", adică nimic. Se face risipă de spaţiu în zona de
memorare a informaţiilor. Este mai util să se creeze o nouă tabelă cu acele coloane speciale și care să se
Page 25 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
lege la tabela principală prin relaţii de tip "unu-la-unu": la o linie din tabela principală corespunde (sau nu) o
singură linie din tabela copil.
De exemplu, să presupunem că în facultate sunt și studenţi străini, la care trebuie stocate informaţii
speciale legate de pașaport, viză, etc. Aceste informaţii sunt foarte rare și nu are sens să punem coloane în
tabela Studenti numai pentru câteva linii utile. Ele se pun într-o tabelă separată "DetaliiStudenti" care va
include doar o singură linie pentru fiecare student străin înscris în facultate. Relaţia dintre tabelele
"Studenti" și "DetaliiStudenti" va fi de tip unu-la-unu.
In urma etapei de implementare a relaţiilor dintre tabele, rezultă următoarea structură logică a bazei de
date:
Sunt câteva reguli numite "de normalizare" prin care se poate verifica dacă o bază de date este sau nu
structurată corect. Normalizarea bazei de date reprezintă procesul prin care se verifică dacă structura bazei
este conformă cu regulile de normalizare și ajustarea acesteia în cazul identificării unor neconcordanţe.
Sunt definite cinci reguli de normalizare, dar în majoritatea cazurilor ne oprim doar la verificarea primelor
trei forme normale.
Page 26 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Prima formă normală verifică ca la intersecţia dintre o linie și o coloană dintr-un tabel să fie o singură
informaţie, nu o listă. De exemplu, nu pot fi mai multe note într-o singură celulă a tabelului Note. Această
regulă este destul de evidentă și simplu de implementat. Toate tabelele din baza noastră verifică prima
formă normală.
A doua formă normală se aplică tabelelor care au cheia primară formată din mai multe coloane. Ea
specifică că în acest caz, toate coloanele din tabel trebuie să fie dependente de întreaga cheie primară, nu
numai de o coloană din această cheie.
Ce este cheia primară? Reprezintă coloana sau asocierile de coloane prin care se identifică în mod unic o
linie din tabel: pk_specializari, pk_grupe, pk_AniStudiu, etc.
Totuși, coloana pk_studenti nu este cheie unică în tabela Studenti. Chiar dacă această cheie identifică în
mod unic studentul, nu poate identifica și grupa din care face parte studentul la un moment dat. Analizând
tabela Studenti putem identifica cheia primară prin asocierea coloanelor pk_Studenti și pk_AniUniversitari.
Această combinaţie identifică în mod unic studentul și grupa din care face parte pentru un anumit an
universitar.
Verificăm a doua formă normală pentru tabela Studenti: sunt toate coloanele din această tabelă complet
dependente de combinaţia pk_studenti, pkAniUniversitari? Nu este respectată regula, numai coloana
pk_Grupe depinde de întreaga cheie primară, restul coloanelor (nume, prenume, adresă) fiind dependente
numai de coloana pk_Studenţi. Rezolvarea acestei situaţii se face prin crerea unui nou tabel de legătură
între cele trei tabele de lucru: Studenti, AniUniversitari, Grupe. In această tabelă se introduc acele coloane
dependente de cheia primară, restul rămânand în tabela Studenti. Noua tabelă se numește "StudentiGrupe"
și are următoarea structură:
Page 27 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Coloana Pk_StudentiGrupe reprezintă noul identificator al situaţiei didactice pentru un student. Toate
informaţiile dinamice din baza de date (grupele de studenţi, notele obţinute) trebuie legate de acest
identificator. Relaţia dintre tabela Note și tabela Studenti va fi înlocuită de relaţia între StudentiGrupe și
Note. In acest fel vom putea regăsi notele unui student pe fiecare an universitar. Chiar dacă un student
repetă anul, fiind înscris în aceeași grupă de două ori, vom regăsi două linii distincte în tabela
StudentiGrupe, având pk-uri diferite care să identifice această situaţie.
Observaţie: problema formei normale numărul doi a apărut prin implementarea incorectă a relaţiilor de tipul
mai-multi-la-mai-multi. Intotdeauna aceast tip de relaţie se rezolvă prin adăugarea unui tabel suplimentar
de legătură între cele două entităţi legate multiplu. Dacă aceste relaţii sunt corect implementate, atunci
forma normală doi se verifică implicit.
Forma normală trei verifică dependenţele tranzitive. Spunem că o coloană este dependentă tranzitiv de
cheia primară dacă în această dependenţă se interpune o alta coloană. De exemplu, în cazul tabelei
Specializări coloana "Nume_Domeniu" depinde de coloana "Cod_Domeniu" care la rândul ei este
identificabilă prin cheia primară "pk_specializari". Dependenţa "pk_specializari" -> "nume_domeniu" se
realizează prin intermediul coloanei "cod_domeniu".
Forma normală trei nu admite dependenţe tranzitive, adică o coloană dintr-o tabelă trebuie să fie
dependentă numai de cheia primară, nu și de alte coloane din tabelă. In cazul nostru, problema se rezolvă
prin crearea tabelei "Domenii" având coloanele: pk_domenii, cod_domeniu, descriere_domeniu. Legătura
cu tabela specializări se face prin coloana "pk_domenii" adăugată în structura tabelei Specializari.
După toate aceste corecţii, am ajuns la o structură logică a bazei de date care este relativ corectă și
respectă regulile de bază privind proiectarea bazelor de date.
Page 28 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Formele normale nu garantează ca datele din bază sunt corecte, în schimb ne ajută să minimizăm
redundanţa datelor și să eliminăm anomaliile de reactualizare.
Se observă în mod logic, că salvarea numelui de domeniu în tabela Specializari este redundantă. De ce
trebuie să scriem numele domeniului pentru fiecare specializare în parte, când îl putem scrie o singură dată
într-un tabel separat și doar accesat din tabela Specializari în caz de nevoie.
Page 29 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Anomalii de ștergere: dacă ștergem toate specializările din tabel, se pierd de asemenea toate
domeniile, ceea ce nu era în intenţiile noastre.
Anomalii la modificare: să presupunem că se modifică numele unui domeniu: din "Inginerie
Electrica" în categoria mai largă numită "Inginerie Electromecanică". Dacă baza de date nu s-ar afla
în forma normală trei, atunci ar trebui ca toate specializările din domeniul "Inginerie Electrica" să fie
modificate în coloana "Nume_Domeniu". Prin crearea tabelei suplimentare "Domenii", modificarea
s-ar face o singură dată, păstrând neschimbată valoarea "pk_domenii" prin care se face legătura
între tabele.
Page 30 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Page 31 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Page 32 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Ca să putem lucra pe baza de date trebuie mai intâi să introducem un utilizator de care se aparţină toate
obiectele necesare implementării programului. Acest utilizator se va denumi "stud" şi va avea parola "stud",
sau fiecare user îşi creează propriul cont folosind numele propriu.
Comanda SQL care creează un nou utilizator este următoarea:
create user stud identified by stud;
Dacă dorim să ştergem user-ul creat avem la dispoziţie comanda "drop":
drop user stud;
Atenţie: ştergerea unui user implică pierderea tuturor informaţiilor care aparţin acelui user: tabele, triggeri,
funcţii, proceduri, biblioteci etc, de aceea trebuie realizat un export mai întâi pe acel user, export ce
salvează informaţia într-un fişier pe disc (vezi comanda de export în capitolul "Dezinstalarea bazei Oracle").
Deci, intrăm in SQL Plus cu user-ul system şi dăm comanda de creare a userului "stud". Orice
instrucţiune SQL se termină cu semnul ";". Odată creat, user-ul trebuie să primească şi nişte drepturi care
să-i permită să întreprindă anumite acţiuni pe baza de date. Vom vorbi mai târziu despre drepturi, acum ne
vom mărgini să-i dăm user-ului "stud" un rol de "data base administrator" (dba), rol suficient de puternic ca
să realizăm ceea ce ne-am propus:
grant dba to stud;
Dupa crearea user-ului "stud" se iese din SQL Plus si se reintră cu noul user sau, se poate reconecta direct
cu noua identitate:
connect stud/stud@stud;
Din acest moment vom lucra numai sub user-ul "stud", deci la începutul fiecărei sesiuni se va da user-ul
stud şi parola corespunzătoare. Primul lucru ce-l avem de rezolvat este crearea tabelelor în care vom ţine
informaţia. Baza de date creată este minimală, acoperind doar câteva aspecte din activitatea şcolară a
studenţilor.
Având structura tabelelor definită, putem trece la crearea efectivă a tabelelor în baza de date. In acest
capitol vom învăţa sintaxa comenzii SQL de creare a tabelelor cu care vom dezvolta toate tabelele
proiectate.
Page 33 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Dacă lucrăm în programul SQL Navigator crearea unui tabel este simplă: click dreapta pe subdirectorul
"Tables" din "My Schema" şi comanda "Create" din submeniul apărut. Se creează tabela "ani_universitari"
având următoarele coloane:
- pk_an_universitar de tip number (în coloana se vor introduce numai numere care să identifice în
mod unic anul şcolar);
- cod_an_universitar: varchar2(50)
- descriere_an_universitar: varchar2(50)
După ce am completat cele două coloane se dă click pe butonul "Apply Changes to DB" ( ).
Cine nu are o asemenea interfaţă grafică, este nevoit să scrie comanda SQL pentru crearea unei tabele:
create table ani_universitari
(
pk_an_universitar number,
cod_an_universitar varchar2(15),
descriere_an_universitar varchar2(50)
);
Dacă am greşit ceva, putem şterge tabela (drop table ani_universitari;) şi apoi crea din nou, în forma
corectă. Sau, mai avem varianta de modificare a tabelei direct, prin comanda SQL "alter table" :
ALTER TABLE ani_universitari MODIFY ( DESCRIERE_AN_UNIVERSITAR VARCHAR2 (50));
Există comenzi pentru adăugare de coloane, ştergere, modificare tip, modificare nume coloane. Comanda
DESCRIBE face o descriere a coloanelor unui tabel cu tipurile aferente.
modificare coloană:
o alter table studenti modify (nume_stud not null);
o alter table studenti modify (nr_matricol number);
Adăugare coloană:
o alter table ani_universitari add (observatii varchar2(100));
Stergere coloană:
o alter table studenti drop column nr_matricol;
Redenumire coloană:
o Alter table student rename column nr_matricol to numar_matricol
Descriere arhitectură tabel "student":
o Describe studenti;
Page 34 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
In momentul când se defineşte o nouă coloană în tabel trebuie specificat şi tipul de date care vor fi stocate
în acea coloană. Intr-o coloană poate fi stocat doar un singur tip de date, nu pot fi amestecate tipuri diferite
(numere, texte, date calendaristice).
Numere:
Number(p,s): un număr cu p cifre din care s la partea zecimală.
o Exemplu: number (7,2) stochează numere de maxim 5 cifre la partea întreagă şi 2 la
partea zecimală
Date calendaristice:
Date: o dată calendaristică ce poate conţine orice dată în perioada 4712 î.e.n – 9999. Aceste
date se păstrează în bază sub forma unor numere cu virgulă, o unitate reprezentând o zi.
Pentru afişarea datelor calendaristice se folosesc diverse funcţii ce formatează acea data într-
un text corespunzător.
Timestamp(s): tot o dată calendaristică, dar memeorează şi fracţiuni de secundă. Numarul s
reprezintă câte zecimale se păstrează dintr-o secundă (implicit se pătrează 5 zecimale, maxim
sunt 9 zecimale). Acest timestamp e util când ne interesează intervale foarte scurte de timp ce
pot fi sub o secundă, deci pot fi calculate doar prin compararea unor date de tip timestamp.
Obiecte mari:
Blob (bynary large objects): pentru obiecte foarte mari de date (cum ar fi imagini, obiecte
binare) Oracle pune la dispoziţie tipul BLOB. In acest câmp se pot stoca până la 4 GB de date
binare, fără nici o formatare prealabilă (ele sunt tratate ca un şir de octeţi oarecare).
Page 35 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Page 36 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
3 Constrângeri
Am văzut ca putem afla grupa din care face parte un student ştiind numărul liniei din tabela "grupe"
care este asociat studentului. Dar, ce se întâmplă dacă s-a şters o grupă din baza de date? Toate liniile vor
fi decalate cu un pas, deci toti studenţii vor fi trecuţi automat în grupa anterioară. Este mult mai bine dacă
am defini o coloană în tabela "grupe" care să aibă ca valoare un număr întreg pentru fiecare linie și să fie
unic în tabelă, indiferent de câte linii are tabela, câte linii s-au şters sau modificat. Putem defini o asemenea
coloană prin constrângerea "primary key".
Constrângerile reprezintă un set de reguli pe care noi le definim şi apoi baza de date se asigură că sunt
respectate. De exemplu, din experienţa noastră de toate zilele, putem trage concluzia destul de utilă că nu
este bine să bagi mâna în foc. De aceea definim o regula : "nu este voie să se bage mâna în foc". Cu toate
că regula este clară, e greu să fie respectată. De multe ori omul uită că focul arde, sau din greşeală trece
cu mâna prin foc, sau introduce mâna într-o incintă în care nu ştie că arde focul. Deci, ne putem frige ori din
neatenţie, ori din neştiinţă. N-ar fi mai bine dacă cineva ar supraveghea tot timpul la respectarea acestei
reguli şi, la fiecare încalcare să ne blocheze să băgăm mâna în foc?
Exact asta face bază de date: noi definim o constrângere de tip "primary key" pe o coloană şi baza
are grijă ca niciodată să nu se repete o valoare pe acea coloană.
Comanda SQL de adăugare a unei constrângeri de tip "primary key" este următoarea:
alter table studenti add constraint studenti_pk primary key (pk_student);
Dar cum ştim să introducem valori unice în coloana de tip primary key? Cine stă să verifice ce valori s-au
introdus până acum şi ce valori putem introduce în continuare? Din fericire, baza de date iarăşi ne ajută.
Bazele de tip Oracle conţin un obiect numit "Sequence" care este un generator de numere crescătoare
unice. Odată definit, obiectul ne furnizează la apel un număr mai mare decât cel furnizat anterior. Nimeni nu
poate da secvenţa înapoi, deci nu este posibil să se repete un număr de mai multe ori. Prin urmare,
completarea unei coloane de tip "primary key" (pk) se face cu ajutorul secvenţelor.
Crearea unei secvenţe se face prin comanda:
create sequence seq_pk_stud increment by 1 start with 21 minvalue 10 maxvalue 9999999999
nocycle noorder ;
Page 37 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Indexul reprezintă un instrument de căutare rapidă a unei informaţii în baza de date. Acestea funcţionează
similar cu indexurile dintr-o carte: memorează o listă de valori (cuvinte cheie) și pagina la care se gasește
acea valoare în carte. Este mai rapid să căutăm un cuvânt cheie în lista din Index, decât să parcurgem
cartea pagină cu pagină.
Indexul în baza de date memorează valoarea cheii primare dintr-o tabelă și locaţia în care se regăsește
linia corespunzătoare acelei chei primare. Indexul are dimensiunea mult mai mică ca tabela, deci căutarea
în index este mult mai rapidă.
Indexii se definesc pe o coloană sau mai multe coloane asociate. In mod automat, Oracle definește un
index pentru fiecare cheie primară a unei tabele. In afară de cheile primare, indexii se definesc pe acele
coloane care sunt utilizate în mod frecvent pentru căutarea și filtrarea datelor.
Comanda SQL de creare index este următoarea:
CREATE INDEX cod_spec_ix ON specializari (cod_specializare);
S-a creat indexul "cod_spec_ix" care face căutarea după coloana "cod_specializare" mult mai rapidă.
Stergerea unui index se face prin comanda:
drop index cod_spec_ix ;
Indexii sunt creaţi pentru o căutare mai rapidă a informaţiilor din baza de date și sunt foarte utili în cazul
tabelelor cu un număr mare de linii. In același timp, scrierea sau modificarea informaţiilor din acea tabelă
este mai lentă deoarece, orice modificare în structura datelor din tabelă trebuie operată și-n structura index-
ului. Ca regulă, nu trebuie făcut abuz prin crearea unor mulţimi de indexi dacă acest lucru nu se impune.
Indexii sunt avantajoși acolo unde sunt tabele mari, cu citiri frecvente și modificări rare.
Sunt situaţii când mai multe coloane din tabelă trebuie să conţină valori unice, dar nu putem defini decât o
coloană ca fiind de tip PK. Pentru celelalte coloane avem la dispoziţie constrângerea de unicitate care
odată definită, nu permite repetarea valorilor pe acea coloană.
alter table grupe add constraint grupe_uk unique(cod_grupa, desc grupa);
alter table grupe drop constraint grupe_uk;
Page 38 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Dacă vreau să creez o constrângere de unicitate dar datele existente în tabelă nu respectă condiţia impusă
de constrângere, se foloseşte clauza NOVALIDATE (baza deja este pornită, avem date, dar s-a constatat la
un moment dat că unele date nu sunt corecte. Nu putem şterge datele, dar din acest moment definim
constrângerea care să nu mai permită introducerea de noi date greşite):
alter table studenti add constraint discipline_uk unique (cod_disciplina, nume_disciplina)
enable novalidate;
OBSERVATIE: novalidate merge numai daca s-a creat un index pe acele coloane:
create index discipline_ix_cod on discipline (cod_disciplina, nume_disciplina);
Constrângerile pot încetini la un moment dat activităţile din baza de date, de aceea există posibilitatea să le
dezactivăm temporar și apoi reactiva:
o alter table enable (novalidate) constraint studenti_uk;
o alter table disable constraint studenti_uk; (asta face automat novalidate)
Page 39 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Ce se întâmplă dacă introducem un student în bază, dar uităm să-i completăm numele? Programul
salvează linia şi apoi vine profesorul să pună note în catalog. Când ajunge la studentul cu pricina găseşte o
linie goală la care nu ştie ce notă să treacă.
De aceea, trebuie să ne asigurăm că, coloana "nume" nu are voie să conţină valori de tip null.
Această problemă se rezolvă prin constrângerea "Not Null" pe care o aplicăm coloanei "nume".
alter table studenti modify (nume_student varchar2(50) not null);
O altă sursă de erori este dată de legătura între tabele. De exemplu, în tabela "studenti_grupe"
ataşăm un student la grupa cu pk-ul 31 în loc de 3 cum ar fi corect. Grupa 31 nu există în bază, deci acel
student nu va apărea în catalog, deoarece catalogul afişează studenţii dintr-o grupă.
Soluţia este dată de constrângerea de tip "Foreign Key", adică stabilim regula ca toate valorile
introduse în coloana "pk_grupa" să facă parte din mulţimea valorilor din coloana "pk" a tabelei "grupe". În
acest fel, când se introduce studentul din grupa 31, baza imediat se sesizează şi întoarce eroare.
alter table StudentiGrupe add constraint stud_grupe_fk foreign key (pk_grupa) references
grupe(pk_grupa);
Observaţie: coloana pk_grupa din tabela grupe trebuie să fie declarată într-o constrângere de tip PK sau
UK, altfel obţinem eroarea: 'no matching unique or primary key for column-list';
Mai există o posibilitate de limitare a surselor de eroare: constrângerile generale de tip "check".
Acestea reprezintă expresii logice ce returnează adevărat sau fals. Dacă rezultatul este fals, linia respectivă
nu va fi modificată în baza de date și se va emite o eroare de validare.
De exemplu, nu putem introduce note în afara intervalului [1-10]:
Alter table note add constraint ck_nota CHECK (nota between 1 and 10);
Page 40 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
- pk_an_universitar (pk_an_universitar)
2. Ani_studiu:
- pk_an_studiu (cod_an_studiu)
3. Specializari:
- Pk_specializari (cod_specializare)
4. Discipline:
- Pk_discipline (cod_disciplina)
- Fk_disc_spec : discipline.cod_specializare -> specializari.cod_specializare
- Fk_disc_ani_studiu: discipline.cod_an_studiu - > ani_studiu.cod_an_studiu
- Unique: denumire_disciplina + cod_specializare (combinatia discipline, spec sa fie unica)
5. Grupe:
- Pk_grupa (cod_grupa)
6. Studenti:
- Pk_student (pk_student)
- Nn_nume_stud (nume_student) not null
7. Studenti_grupe:
- Pk_studenti_grupe
- Fk_studenti_grupe_grupe: student_grupe.pk_grupa -> grupe.pk_grupa
- Fk_studenti_grupe_studenti: student_grupe.pk_student -> studenti.pk_student
8. Note:
- Pk_nota(pk_nota)
- Fk_note_stud : note.pk_student -> studenti.pk_student
- Fk_note_discipline : note.pk_disciplina -> discipline.pk_disciplina
- Ck_nota : nota > 1 şi nota <= 10
Page 41 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Odată create tabelele, putem introduce date în ele. Comanda SQL de introducere a datelor în tabele este
INSERT ce are sintaxa dată în continuare:
insert into ani_universitari (pk_an_universitar, cod_an_universitar, descriere_an_universitar)
values (-1, '2010-2011', 'an universitar 2010-2011');
După comanda "insert into" se pune numele tabelei în care se introduc datele, între paranteze se trec
denumirile coloanelor care se completează cu informaţii, apoi după cuvântul "values" se pun valorile noilor
date ale tabelei. Observăm că valorile numerice se scriu ca atare pe când câmpurile de tip text se scriu cu
apostrof ('an universitar 2010-2011' ).
Aceeaşi comandă poate fi dată şi prin sintaxa:
insert into ani_universitari values (-1, '2010-2011', 'an universitar 2010-2011');
când se renunţă la specificarea coloanelor completate. In acest caz paranteza "values" trebuie să conţină
valori pentru toate coloanele tabelei. Prima variantă este de preferat deoarece se vede foarte clar ce
coloane se completează (sunt tabele care au zeci de coloane şi doar câteva primesc valori la o inserare) şi
în al doilea rând nu se ştie niciodată dacă nu cumva mai trebuie adăugată o coloană la o tabelă după
conceperea bazei de date. O coloană în plus necesită modificarea tuturor insert-urilor scrise sub a doua
variantă.
Notă: trebuie verificat dacă studentul primeşte notă doar la o disciplină pe care el o studiază.
Constrângerea fk_note_discipline verifică că disciplina la care se pune nota există în baza de date, dar nu
poate verifica dacă studentul ce primeşte nota studiază sau nu acea disciplină. Această verificare trebuie
făcuta printr-un trigger, concept ce se va studia în alt capitol.
Page 42 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
ecuaţie dă peste cap toată ecuaţia care va returna întotdeauna NULL, indiferent de celelalţi membri ai
ecuaţiei (nimic adunat, împartit, testat, etc., cu ceva dă întotdeauna nimic).
Este foarte greu să ţinem evidenţa valorilor care sunt NULL în baza de date, de aceea, pentru a nu bloca
procesarea informaţiei în cazul întâlnirii unei valori NULL se foloseşte funcţia NVL care să furnizeze o
valoarea implicită pentru coloanele ce nu au valori:
select nvl(prenume_student,'prenume student') from studenti;
Dacă coloana "prenume_student" are valori diferite de NULL atunci funcţia "nvl" returnează acea valoare, în
caz contrar returnează şirul 'prenume student'.
Folosind comanda insert prezentată mai sus, se introduc date de lucru în tabele astfel încât să obţinem o
bază de date funcţională (grupe, studenţi, specializări, discipline, note, etc).
Prezentăm în continuare câteva tabele:
select * from specializari;
Pentru o introducere mai rapidă a datelor se pot utiliza frazele insert listate în anexa "Introducerea date în
bază".
Page 43 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Orice informaţie din bază trebuie la un moment dat extrasă şi vizualizată. In acest scop folosim comanda
SQL SELECT, comandă cu care ne confruntăm cel mai des în lucrul cu baza de date.
Dar nu vreau să văd toate coloanele, nu mă interesează pk-ul studentului, sau alt pk, vreau să ştiu numele
şi prenumele studentului:
select nume_student, prenume_student from studenti;
Sunt prea mulţi, nu vreau să văd toţi studenţii care au trecut prin facultate, vreau să văd numai studenţii
care sunt la litera 'B':
select nume_student, prenume_student from studenti
where nume_student like 'B%';
Din cele prezentate până acum putem reţine sintaxa de bază a instrucţiunii SELECT:
select nume_coloane from nume_tabel where condiţii de selecţie.
După cuvântul "select" se introduc numele coloanelor din tabela care vrem sa fie afişate. Dacă sunt mai
multe coloane, se despart prin virgulă, după ultima coloană nu se pune virgulă. Dorim toate coloanele din
tabelă? punem direct * (select *). Urmează clauza "from" după care punem numele tabelei din care
Page 44 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
extragem acele informaţii. Nu dorim toate liniile din tabel? atunci trebuie să adăugăm clauza "where" unde
introducem condiţiile pe care trebuie să le îndeplinească liniile din tabel ce se doresc afişate.
In acest scop se creează o legătură (database link) între baza curentă şi altă bază din sistem:
CREATE DATABASE LINK adina_link CONNECT TO adina IDENTIFIED BY adina USING 'stud'
Selectul dintr-un tabel aparţinând unei baze de date vecine se face prin intermediul legăturii create:
select * from studenti@adina_link;
In felul acesta pot trece date dintr-o bază în alta, sau pot face un select care să afişeze datele cumulate din
cele două baze (folosim clauza Union):
select studenti.nume_student || ' '|| studenti.prenume_student "Nume prenume student",
'baza db3' "baza de origine"
from studenti
union
select studenti.nume_student || ' '|| studenti.prenume_student "Nume prenume student",
'baza db1' "baza de origine"
from studenti@db1_link
order by "baza de origine" desc ;
De cele mai multe ori informaţiile necesare nu sunt stocate într-un singur tabel, ci sunt dispersate în mai
multe tabele. De exemplu, un caz tipic apare când vreau să văd studenţii pe fiecare grupă dintr-un anumit
an universitar. In acest caz datele se regăsesc în tabele diferite: tabela studenti conţine numele studenţilor,
iar tabela studenti_grupe conţine legătura între studenţi și grupe, iar tabela ani_universitari păstrează anii
universitari:
select st.nume_student, st.prenume_student, g.cod_grupa,
au.cod_an_universitar
from studenti st, grupe g, studenti_grupe stg, ani_universitari au
where st.pk_student = stg.pk_student
Page 45 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Instrucţiunea select permite extragerea informaţiilor din mai multe tabele simultan. Pot vizualiza atât numele
studentului, cât şi specializarea acestuia, chiar dacă cele două informaţii sunt în tabele diferite. Important
este să existe o legătură (join) între cele două tabele astfel încât unei linii dintr-o tabelă să-i corespundă una
sau mai multe linii din cealaltă tabelă. Legătura între tabele se face prin coloane ce au aceleași valori (equi-
join). Când fraza select lucrează cu două tabele (sau mai multe), întotdeauna trebuie să existe clauza
where în care se specifică legătura între tabele. In selectul de mai sus legătura dintre tabela student si
tabela student_grupe se realizează prin coloana "pk_student" comună celor două tabele, în timp ce mai
departe se merge pe legătura "pk_an_universitar" pentru a lega anul universitar de selectul nostru.
Cum lucrează selectul în acest caz? Se aduce numele studentului şi pk-ul din tabela "studenti" şi apoi se
caută toate liniile din tabela "student_grupe" ce corespund acelui pk. Pentru fiecare din aceste linii se
testează dacă respectă cealaltă condiţie dată de pk_grupă (stg.pk_grupa = g.pk_grupa), cele care
nu respectă condiţia sunt eliminate din select. Se merge în continuare pe condiţia "pk_an_universitar" până
sunt eliminate toate liniile nedorite.
Ce se întâmplă dacă uit pur şi simplu să pun în clauza "where" legătura dintre tabele? Atunci e grav, se
umple ecranul cu zeci de rânduri fără nici o noimă. Fraza select face produsul cartezian între cele două
tabele şi afişează rezultatul (adică pentru fiecare linie din tabela "studenti" parcurge toate liniile din tabela
"studenti_grupe").
Concluzie: să nu uităm niciodată să punem în clauza "where" condiţia de legătură dintre tabele atunci când
selectăm coloane din mai multe tabele simultan.
Tema: să se construiască un select care să afişeze toţi studentii din facultate, împreună cu grupa,
disciplinele şi notele obţinute la examene:
Page 46 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Să presupunem că este un student care nu este ataşat la nicio grupă, deci nu are nici o linie inserată în
tabela "studenti_grupe".
insert into studenti values(null,'Chirila', 'C-tin',null);
Nu atașez nicio grupă la acest student şi facem un select care să-mi afişeze studenţii şi grupele ataşate:
select st.nume_student, st.prenume_student, stg.pk_grupa
from studenti st, studenti_grupe stg
where st.pk_student = stg.pk_student
order by nume_student asc ;
Studentul Chirila nu apare în selectul precedent, chiar dacă el există în baza de date, deoarece linia
respectivă cade în clauza st.pk_student = stg.pk_student.
Totuşi, dacă vreau să vad toţi studenţii, indiferent dacă au grupe asociate sau nu, cei care au grupe
asociate să li se afişeze grupa, cei care nu au, să apară fără grupă, dar să apară, atunci trebuie folosit
operatorul Outer-join (sau operator '+' pentru bazele Oracle) de unire a tabelelor:
select st.nume_student, st.prenume_student, stg.pk_grupa
from studenti st, studenti_grupe stg
where st.pk_student = stg.pk_student (+)
order by nume_student asc ;
Page 47 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Operatorul (+) pus în dreapta tabelului ce nu are corespondent pentru fiecare linie din primul tabel, are ca
efect afişarea tuturor liniilor selectate din primul tabel, indiferent dacă au sau nu legătură cu cel de-al doilea.
Afişez şi codul grupei din care face parte studentul. In acest caz trebuie sa folosesc de două ori operatorul
"outer join" (+):
select st.nume_student, st.prenume_student, stg.pk_grupa, g.cod_grupa
from studenti st, studenti_grupe stg, grupe g
where st.pk_student = stg.pk_student (+)
and stg.pk_grupa = g.pk_grupa (+)
order by nume_student asc
;
Alt exemplu este dat de afișarea studenţilor și notelor obţinute: dacă un student nu are notă, atunci prin
folosirea joncţiunii de tip "equi-join", studentul respectiv nu apare în rezultatul selectului:
Page 48 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Dar dacă se folosește joinul extern, atunci toţi studenţii din acel an universitar sunt afișaţi:
Tabela "studenti_grupe" a fost unită cu tabela "note" prin intermediul unui join extern ce afișează liniile din
"student_grupe" care nu au în mod obligatoriu correspondent în tabela "note".
5.4.1Operatori de comparaţie
In general, tabelele au mii de linii, milioane chiar, clauza where este cea care ne ajută să sortăm aceste linii
şi să scoatem numai informaţia de care avem nevoie. In această clauză sunt scrise expresii logice ce
returnează doar două valori: valoarea "adevărat" (true) sau valoarea "fals" (false). Expresiile logice sunt
construite cu ajutorul operatorilor de comparaţie:
Operator Explicaţie
= Egal cu
> Mai mare decât
>= Mai mare sau egal
< Mai mic decât
<= Mai mic sau egal
<> != Diferit de
Exemple:
1. Să se listeze toţi studenţii care au note mai mari de 8:
select st.nume_student, st.prenume_student, g.cod_grupa,
d.cod_disciplina, n.nota, au.cod_an_universitar
from studenti st, grupe g, studenti_grupe stg, ani_universitari au, note
n, discipline d
where st.pk_student = stg.pk_student
Page 49 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Clauza "where" poate conţine mai multe fraze care împreună să construiască valoarea de adevăr finală.
Frazele sunt legate prin trei termeni cheie:
"and" : returnează "true" numai dacă toate frazele din compoziţie sunt "true"
"or" : returneaza "true" dacă cel puţin o frază este "true".
"not" : inversează valoarea de adevăr a frazei la care se aplică
Exemple:
1. Afişează studenţii care au obţinut la disciplina ME nota 7 şi nota 8:
select st.nume_student, st.prenume_student, g.cod_grupa,
d.cod_disciplina, n.nota, au.cod_an_universitar
Page 50 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Dacă uităm de paranteze, atunci obţinem sute de mii de linii cu informaţii eronate, deoarece pentru fiecare
notă de 8 din tabela note se repetă selectul iniţial.
Mai sunt disponibili şi alţi operatori de relaţie care ne ajută la construirea clauzei "where":
Page 51 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
5.4.3Operatorul "between"
5.4.4Operatorul "in"
5.4.5Operatorul "like"
Este operatorul care ne scoate din încurcătură când nu ştim ce căutăm, ci doar bănuim. Dacă nu ştiu
numele complet al unui student, ci doar câteva caractere, atunci folosesc clauza like pentru filtrarea liniilor:
1. Ex: caut studenţii de la litera A:
select * from studenti
where studenti.nume_student like 'A%';
Page 52 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Deci scriem în clauza where caracterele pe care le ştim sigur ("A") şi pentru restul caracterelor punem
simbolul "%". Fraza select anterioară se traduce: adu-mi toţi studenţii al căror nume începe cu litera "A".
2. Toate fetele din facultate (prenumele se termina cu litera 'a' sau 'A')
select * from studenti
where (prenume_student like '%a' or prenume_student like '%A');
3. Mă mai pot juca cu operatorul "like": vreau toţi studentii care au în componenţa numelui şirul de
caractere "ut":
select st.nume_student, st.prenume_student, upper(st.prenume_student)
from studenti st
where upper(prenume_student) like '%UT%';
5.4.6Clauza distinct
In rezultat Iacob Adelina apare de două ori pentru ca a repetat anul şcolar, deci a fost de două ori în
aceeaşi grupă (chiar dacă în ani universitari diferiţi).
Daca vreau să afişez doar liniile distincte, ce nu repetă informaţia afişată de alte linii, folosesc clauza
"distinct":
select distinct st.nume_student, st.prenume_student, g.cod_grupa
from studenti st, grupe g, studenti_grupe stg
where st.pk_student = stg.pk_student
Page 53 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Directiva "distinct" înlătură informaţia care se repetă în rezultatul frazei select. In acest fel, studenţii vor fi
afişaţi o singură dată, indiferent de numărul de înregistrări găsite în tabela studenti.
Dar dacă se afişează şi pk_an_universitar, atunci Iacob Adelina apare iarăşi de două ori, pentru că, în acest
caz, liniile nu mai sunt identice.
select distinct st.nume_student, st.prenume_student, g.cod_grupa,
stg.pk_an_universitar
from studenti st, grupe g, studenti_grupe stg
where st.pk_student = stg.pk_student
and stg.pk_grupa = g.pk_grupa
order by nume_student, g.cod_grupa
;
O valoare nulă e o valoare care nu e disponibilă, neatribuită, necunoscută şi neaplicabilă. Adică, oriunde
folosim null într-o expresie, toată expresia devine null (false). Nu pot folosi expresii de tipul =null, sau
<>null.
De exemplu, mă interesează studenţii care nu au număr matricol; încercăm fraza următoare:
select * from studenti where nr_matricol = null;
Evident, nu aduce nici o linie, am făcut o greşeală evidentă, am pus null într-o expresie logică.
Corect ar fi în felul următor:
select * from studenti where nr_matricol is null;
Clauza "order by" se foloseşte pentru a ordona liniile aduse de fraza select. Intotdeauna această clauză se
pune ultima într-o frază select. Liniile aduse de select nu sunt ordonate, de aceea e posibil ca acelaşi select
Page 54 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
rulat de mai multe ori să aducă liniile în ordine diferite. Order by rezolvă această problemă, prin ordonare,
aceeaşi linie va fi tot timpul la locul ei.
Să vedem studenţii în ordinea alfabetică:
select st.nume_student, st.prenume_student
from studenti st
where st.nr_matricol is null
order by st.nume_student, st.prenume_student;
Implicit "order by" lucrează ascendent, adică de la valoare mică spre valoare mare. Dar dacă vreau să-i
afişez în ordine inversă, folosesc clauza "desc":
select st.nume_student, st.prenume_student
from studenti st
where st.nr_matricol is null
order by st.nume_student desc, st.prenume_student desc;
Deci folosesc clauza "desc" (de la descendent) şi schimb ordinea de listare. Pentru ordinea ascendentă
folosesc cuvântul "asc" (dar acesta este implicit, nu trebuie scris neapărat).
Pot face ordonarea după mai multe coloane, îi pun în ordinea notelor, dar la aceeaşi notă, să fie în ordinea
alfabetică:
select st.nume_student, st.prenume_student, g.cod_grupa,
d.cod_disciplina, n.nota, au.cod_an_universitar
from studenti_grupe stg, studenti st, ani_universitari au, grupe g,
specializari s,
discipline d, note n
where stg.pk_student = st.pk_student
and stg.pk_an_universitar = au.pk_an_universitar
and stg.pk_grupa = g.pk_grupa
and g.pk_specializare = s.pk_specializare
and s.pk_specializare = d.pk_specializare
and g.pk_an_studiu = d.pk_an_studiu
and n.pk_student_grupa = stg.pk_student_grupa
and n.pk_disciplina = d.pk_disciplina
order by n.nota desc, st.nume_student
;
La prima vedere pare ilogic să pun order by după nume_stud, deoarece această coloană nu este de tip
number, ca s-o poţi ordona. Dar să ne amintim că, toată informaţia din calculator este sub formă de
numere, deci are sens ordonarea. Literele din cadrul numelui studentului sunt memorate sub forma unor
numere ce corespund codurilor ASCII ale acelor litere. Nu întâmplător, codurile ASCII ale literelor
corespund ordinii din alfabet (litera "a" are codul ASCII mai mic decât litera "b").
Exerciţii:
1. Să se afişeze disciplinele studiate de grupele 6403 şi 6404:
Page 55 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Page 56 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
6 Funcţii SQL
Adesea avem nevoie ca informaţia adusă de fraza select să fie prelucrată înainte de afişare. Vreau să mă
asigur că toţi studenţii vor fi afişaţi cu litere mari. In acest caz folosesc funcţia "upper" ce prelucrează
informaţia de pe fiecare rând al rezultatului adus de fraza select:
select upper(nume_student) from studenti;
Nu-mi place titlul coloanei adusă de fraza select, aş dori să se numească "nume student":
select upper(nume_student) "nume student" from studenti ;
SQL permite definirea de alias-uri pentru nume de coloane astfel încât acestea să fie cât mai explicite.
Aceste alias-uri trebuie să urmeze imediat după numele coloanei în fraza select şi trebuie scrise folosind
ghilimele.
Funcţiile SQL se împart în două tipuri: funcţii de un singur rând , care întorc câte un rezultat pentru fiecare
linie adusă de fraza select, şi funcţii de grup ce prelucrează informaţia de pe mai multe linii şi returnează o
valoare pentru întreg grupul de linii.
In continuare sunt listate câteva funcţii de rând care se întâlnesc în mod frecvent în practica SQL:
1. Funcţie 2. Rezultat
LOWER Face conversia caracterelor alfabetice în litere mici
UPPER Face conversia caracterelor alfabetice in litere mari
INITCAP Face conversia pentru primul caracter din fiecare cuvânt în litera
mare iar pentru restul caracterelor conversia se face în litere mici
CONCAT(exp1,exp2) Concatenează cele două expresii
SUBSTR(expresie, Intoarce n caractere din cadrul expresiei începînd cu pozitia m.
m/,n/) Daca m este negativ atunci poziţia de început a numărării este
ultimul caracter din şir. Dacă n este omis atunci funcţia întoarce
toate caracterele de la poziţia m pîna la sfârşitul şirului.
LENGTH(expresie ) Intoarce numarul de caractere din expresie
INSTR(expresie ,"sir") Caută şirul de caractere "sir" în expresie şi întoarce poziţia unde a
fost găsit.
Exemple:
Page 57 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
vrem sa facem un raport simplu in care fiecare student sa apară numai cu primele 3 litere
din nume (facem o prescurtare):
select substr(nume_student,1,3) from studenti where rownum < 4;
Am pus condiţia rownum < 4 ca să aducă numai 3 linii din rezultatul selectului. Rownum este o coloană
implicită construită de motorul SQL în care se păstrează numărul liniei curente.
funcţia round (numar, n): rotunjeşte numărul la n zecimale. Dacă n lipseşte, se rotunjeşte
la partea întreagă. Rotunjirea se face prin adaos (dacă numărul zecimal se apropie mai
mult de întregul superior, sau prin lipsă).
funcţia trunc (numar, n): similar cu round numai că se face trunchiere, adică rotunjire prin
lipsă întotdeauna
funcţia mod(n,m): întoarce restul împărţirii lui n la m
Tabela "dual" este o tabelă creată automat sub user-ul "sys" şi este accesibilă tuturor utilizatorilor. Are o
singură coloană "dummy" cu o singură linie "X":
select * from dual;
Se utilizează când vrem să afişăm diverse informaţii care nu sunt în tabele din baza de date. Vreau să
afişez rezultatul împărţirii lui 3 la 2:
select 3/2 from dual;
Page 58 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Nu există altă posibilitate de a afişa informaţie decât prin fraza select, care are o sintaxă fixată: select ...
from nume_tabel, de aceea am nevoie de tabela "dual".
Datele calendaristice ocupă un capitol important în cadrul SQL deoarece foarte multe informaţii sunt legate
de date calendaristice. Oracle memorează data calendaristică sub forma unui număr ce reţine următoarele
valori: secol, an, luna, zi, ora, minute, secunde. Coloanele din tabele care conţin date calendaristice sunt
definite cu tipul "date", similar cu "number" pentru numere sau "varchar2" pentru caractere.
funcţia sysdate: returnează data curentă (citită din memoria CMOS a calculatorului):
select sysdate "data curenta" from dual;
Un întreg adunat (sau scăzut) la o dată calendaristică creşte acea dată cu 1 zi. Vreau să cresc cu 7 ore de
exemplu, adun la data respectivă 7/24:
select sysdate + 7/24 from dual;
funcţia months_between(data1, data2): returnează numărul de luni între cele două date,
număr ce poate fi negativ dacă data2 e mai mica decât data1.
add_month(data,n): adună n luni la data, n poate fi şi negativ
next_day(data,'nume_zi'): găseşte data când cade prima dată ziua 'nume_zi' ce urmează
după data:
select next_day(sysdate,'monday') from dual;
Page 59 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Exerciţii:
1. Să se găsească prima zi din luna următoare lunii curente:
select last_day(sysdate) + 1 from dual;
Sunt funcţii ce permit conversia unui şir de caractere într-un număr sau într-o dată calendaristică sau
invers: din număr în şir de caractere.
Sunt permise o mulţime de formate pentru afişarea unei date calendaristice. Prezentăm în continuare doar
câteva mai semnificative:
YYYY sau YY sau Y Ultimele 4,2 sau 1 cifră din an
MM Luna scrisă cu două cifre
MONTH Numele întreg al lunii scris pe 9 caractere
MON Luna scrisă pe 3 caractere
WW sau W Saptamâna din an sau luna
DDD sau DD sau D Ziua din an ,luna sau saptamâna.
DAY Denumirea completă a zilei completată eventual
cu spaţii până la 9 caractere.
DY O abreviaţie a denumirii unei zile formată din trei
Page 60 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
litere
AM sau PM indicator de meridian
A.M. sau P.M. indicator de meridian cu puncte
HH sau HH12 sau HH24 ora
MI minute (0-59)
SS secunde (0-59)
SSSSS Numarul de secunde începînd cu miezul noptii
Exemplu:
select to_char(sysdate,'ww') nr_saptamana_an, to_char(sysdate,'day-mon-yy') zi_luna_an
from dual;
Dacă nu specificăm un format funcţia to_char afişează numărul aşa cum este, fără separatoare sau
rotunjiri. Prin câmpul de formatare scris între apostrofuri sunt definite numărul de cifre pentru afişare, poziţia
virgulei de separare, numărul de zecimale, afişare cu monedă, etc. Observăm că dacă numărul de cifre
specificat în câmpul de formatare este prea mic, atunci to_char afişează semnul (#) în locul valorii
numerice.
Page 61 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Prin funcţia to_date transformăm un şir de caractere într-o dată. Intotdeauna avem probleme cu
interpretarea unui şir de caractere ce reprezintă o dată calendaristică. Să luăm de exemplu textul
"01/02/05"; poate fi interpretat ca fiind 1 februarie 2005 dacă lucrăm după modelul românesc, sau foarte
bine, poate fi 2 ianuarie 2005 interpretat după modelul american: mm/dd/yy (ei pun luna în faţa zilei). De
aceea, când transformăm un şir de caractere în dată trebuie să specificăm formatul. Mai rău este pentru cel
care utilizează programul, vede data 01/02/2005 şi nu ştie cum s-o interpreteze. In acest caz, data se
afişează prin funcţia to_char în care putem specifica exact cum trebuie să apară data la utilizator. Indicat
este să se utilizeze formatul cu litere pentru afişarea lunii, astfel încât să dispară orice dubii în citirea datei
calendaristice.
In coloana 3 din exemplul precedent, chiar dacă am utilizat formatul de citire a date sub forma 'dd-mon-yy',
ea a fost afişată tot cu cifre : 1/02/2005. Această afişare depinde de modul cum a fost setat programul SQL
Navigator, sau, dacă se utilizează SQL Plus de setările din regiştrii Windows: run ->regedit
->hkey_local_machine -> software ->oracle -> nls_date_format. Ca să nu mai depind de aceste setări ce
pot diferi de la un calculator la altul, am folosit funcţie to_char (coloana 4 din exemplul de mai sus).
Page 62 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Prin to_number se returnează un număr pornind de la un şir de caractere. Acest şir este interpretat conform
formatului specificat şi tradus într-un număr. Dacă cele două câmpuri nu corespund, funcţia to_number dă
eroare.
select TO_NUMBER('100.00', '9G999D99'), TO_NUMBER('5,342', '9G999D99'),
TO_NUMBER('5,342.14', '9G999D99') from dual;
Decode este o funcţie foarte puternică ce permite implementarea unei structuri de programare în codul
SQL. Funcţia nu este standard SQL, ea fiind specifică motorului Oracle. Prin funcţia decode putem simula
structura if - else specifică oricărui limbaj de programare în sensul că putem selecta informaţie diferită în
funcţie de anumite rezultate obţinute în procesul rulării.
Formatul funcţiei este:
decode (expresie, prima_varianta , rezultat1,
a_doua_varianta, rezultat2,
.............. ,
rezultat_default
);
Se evaluează expresia şi se compară valoarea acesteia cu variantele scrise în partea a doua a funcţiei. In
caz că valoarea expresiei coincide cu o variantă, se întoarce rezultatul corespunzător acelei variante, în caz
contrar se întoarce "rezultat_default".
Exemplu: să zicem că profesorul de la disciplina IB (Instrumentatie de Bord) vrea să mărească la
toţi studenţii nota cu 1 punct, iar profesorul de ME vrea să micşoreze notele cu 1 punct. Deci trebuie să
facem un select din tabela note, dar acest select să se comporte diferit, în funcţie de valoarea rândului
"cod_disciplina".
select stg.pk_student_grupa, st.nume_student, st.prenume_student, g.cod_grupa,
d.cod_disciplina, n.nota,
decode(d.cod_disciplina, 'IB', decode(n.nota, 10, 10, n.nota +1),
'ME',n.nota - 1,
n.nota) Nota_Modificata,
ast.cod_an_studiu, au.cod_an_universitar
from studenti_grupe stg, studenti st, ani_universitari au, grupe g,
specializari s,
discipline d, ani_studiu ast, note n
where stg.pk_student = st.pk_student
Page 63 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Aceste funcţii nu se aplică unei singure linii, ele calculează informaţii caracteristice unui grup de rânduri:
max, min, avg, count(*), sum.
select max(nota),round(avg(nota),2), min(nota), sum(nota),
count(*), round(sum(nota)/count(*),2) from note;
In exemplul de mai sus am extras câteva informaţii globale ale tabelei "note":
max(nota_finala) : calculează cea mai mare valoare din coloana "nota_finala". Chiar dacă
sunt mai multe note de 9, funcţia max returnează o singură valoare
min(nota_finala) : returnează cea mai mica valoare din coloana "nota_finala";
avg (nota_finala): calculează media aritmetică a notelor obţinute de studenţi;
Page 64 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Suma a rămas aceeaşi, count(*) s-a schimbat şi are dreptate, count(*) returnează numărul de linii ale
tabelei "note". Din această cauză nu a mai ieşit corect nici media aritmetică calculată de noi sub forma
sum(nota)/count(*). Să observăm totuşi că media calculată cu avg(nota_finala) este corectă, deci funcţia
ştie să trateze valoarea null în mod corect.
Putem şi noi să calculăm corect media dacă numărăm numai liniile care au nota_finala diferită de
null: count(nota):
select max(nota),round(avg(nota),2), min(nota), sum(nota),
count(*), round(sum(nota)/count(nota),2) from note;
Funcţiile min, max pot fi folosite şi pentru coloane care nu sunt în mod necesar de tip numeric. Aceste
funcţii au sens şi pentru şiruri de caractere sau date calendaristice:
select min(nume_student), max(nume_student) from studenti ;
Evident, min(nume_stud) nu aduce studentul cu cel mai mic nume (adică format din cel mai mic număr de
caractere), ci primul student în ordinea alfabetică din grupul selectat.
Dacă vreau să văd câte caractere are cel mai mic nume (fără prenume) scriu următorul select:
select min(length(substr(nume_student,1,instr(nume_student,' ')-1)))
from studenti;
Page 65 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Până acum am calculat max,min, medie pentru tot tabelul "note". Dar aceste valori nu-mi spun mare lucru,
degeaba calculez eu media notelor pentru toţi anii de studii, la toate materiile şi-n toţi anii şcolari.
Mă interesează de exemplu să ştiu care a fost media pentru disciplina ME, media pentru AE, min, max pe
aceste discipline să pot face comparaţii, să trag eventual semnale de alarmă în caz că apar nişte situaţii
catastrofale.
Să afişăm notele obţinute pe discipline:
select d.cod_disciplina, n.nota
from discipline d, note n
where n.pk_disciplina = d.pk_disciplina
order by d.cod_disciplina
;
Să calculăm media notelor la fiecare disciplină:
select d.cod_disciplina, round(avg(n.nota),2)
from discipline d, note n
where n.pk_disciplina = d.pk_disciplina
order by d.cod_disciplina
;
Evident că Oracle a dat eroare, ca să calculeze media, trebuie sa-i spui cum să grupeze liniile; media
înseamnă că există un grup, deci trebuie precizat cum se formează grupul. Când se afişează doar rezultatul
funcţiei pe un întreg tabel, nu avem nevoie de clauza "group by", funcţia face media pentru tot tabelul. In
cazul că dorim să vedem şi codul disciplinei, atunci trebuie să grupez liniile după acest cod. Grupurile se
formează cu clauza "group_by":
Deci, prima regulă: dacă vrem să afişăm o coloană într-un select ce foloseşte o funcţie de grup, acea
coloană trebuie să fie inclusă în clauza "group by", altfel Oracle dă eroare.
Să încercăm o altă situaţie: care sunt disciplionele de la care s-au obţinut medii sub 8?
Page 66 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Nu-i bun, nu pot folosi funcţiile de grup în clauza where. Pentru a face filtrări cu ajutorul funcţiilor de grup
folosim clauza "having":
select d.cod_disciplina, round(avg(n.nota),2)
from discipline d, note n
where n.pk_disciplina = d.pk_disciplina
group by d.cod_disciplina
having round(avg(n.nota),2) < 8
order by d.cod_disciplina
;
Funcţiile de grup sunt foarte utile când sunt necesare statistici şi rapoarte finale. Cu ajutorul lor putem
sintetiza informaţia care să reflecte aspecte globale ale tabelelor.
Exerciţii:
- Care este cel mai bun student din grupa sa?
- Studenţii care repetă anul şcolar (se regăsesc de două ori în acelaşi an de studiu pe ani
universitari diferiţi)
- Studenţii care sunt peste media din grupa lor
Page 67 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
7 Subinterogări
De multe ori se întâmplă să nu putem scrie exact valorile care ne interesează în clauza where. De exemplu,
trebuie să dau un raport cu studenţii care au obţinut cea mai mare notă la o sesiune de examene. Problema
e că eu nu ştiu care este acea notă, poate fi 10, sau 9, sau 8 sau..... Ar trebui să aflu mai întâi care este cea
mai mare notă în sesiunea respectivă, iar această valoare se află în mod dinamic printr-un select interior:
Dacă uneori se întâmplă ca subinterogarea să aducă mai mult de o linie, ceea ce duce la eroare Oracle,
este foarte posibil ca subinterogarea să nu aducă nici o linie, ceea ce propagă null-ul şi la interogarea
principală şi nu se afişează nimic.
De exemplu, dacă în seelctul inferior greşim codul disciplinei, atunci tot selectul principal dă greş:
select st.nume_student, st.prenume_student, d.cod_disciplina, n.nota
from studenti st, studenti_grupe stg, note n, discipline d, ani_universitari
ani
where st.pk_student = stg.pk_student
and stg.pk_an_universitar = ani.pk_an_universitar
and stg.pk_student_grupa = n.pk_student_grupa
and n.pk_disciplina = d.pk_disciplina
and d.cod_disciplina = 'PSM1'
and ani.cod_an_universitar = '2011-2012'
and n.nota = (
select max(n.nota)
from studenti_grupe stg, ani_universitari ani, note n, discipline d
where n.pk_disciplina = d.pk_disciplina
Page 68 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Să mai rezolvăm o problemă: toţi studenţii a căror medie pe un an universitar este mai mare decât
media notelor pe acel an universitar:
De exemplu, aș dori să afișez intr-un select numele studenţilor, disciplinele studiate, notele obţinute, dar și
media notelor obţinute la acea disciplină de ceilalţi studenţi. In felul acesta, aș putea face o evaluare mai
corectă a notei obţinute de student, dacă pot s-o compar cu media celorlalţi (nota 9 nu reprezintă o
realizare de exemplu, când media pe disciplină este 9.50).
Page 69 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
In selectul de mai sus, s-a calculat media pe disciplină printr-un subselect care se leagă la valoarea
"pk_disciplina" din selectul principal.
Complicăm puţin lucrurile: care sunt studenţii ce au media notelor peste media grupei din care face parte
fiecare (cei mai buni studenţi din grupa lor) ?
Deja problema s-a complicat: trebuie să găsim media fiecărui student şi să comparăm acea medie cu
media grupei din care face parte studentul. Selectul ar fi mai simplu dacă aş avea în bază două tabele:
- Un tabel care să dea studentul, grupa din care face parte şi media obţinută de student
- Un tabel ce conţine grupa şi media pe acea grupă
Dacă aş avea acele tabele, atunci selectul se construieşte mult mai simplu. Oracle permite definirea unor
tabele temporare, bazate pe un select şi care pot fi utilizate în acel select specific:
Page 70 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
In clauza "from" am adăugat cele două selecturi, le-am dat denumire de tabele şi am putut să le utilizăm în
selectul superior.
Am văzut în acest capitol cât de puternică poate deveni instrucţiunea SELECT. In afara selectării clasice a
informaţiilor din tabelele bazei de date, selectul permite utilizarea subselecturilor în clauza where (care în
plus, pot face referire la tabelele din selectul superior) şi, de asemenea, permite construirea de tabele
temporare în clauza from (select din select).
7.4 Vizualizări
Vizualizările sunt tabele virtuale create printr-un select din alte tabele. Am văzut în subcapitolul anterior ce
util a fost să creez tabele intermediare cu mediile studenţilor, sau mediile grupelor. Alt exemplu, mi-ar fi
foarte util să am un tabel în care să găsesc numele studentului, specializarea, codul grupei din care face
parte, notele obţinute şi disciplinele la care a obţinut note. Cu un asemenea tabel aş putea lucra mult mai
uşor când fac selecturile.
De ce nu se creează asemenea tabele? Una din regulile proiectării bazei de date spune că un tabel nu
trebuie să conţină informaţii amestecate, cu identităţi şi funcţii diferite. Ce facem dacă se modifică o
denumire de specializare? Modificăm toate liniile tabelului?
Oracle îmi vine în ajutor şi mă lasă să-mi fac un tabel virtual care să conţină aceste câmpuri. Dar nu este un
tabel propriu-zis, ci o imagine ce oglindeşte informaţia din tabelele de bază. In felul acesta, orice modificare
într-un tabel de bază apare în mod automat şi în imaginea sa din tabela virtuală. Aceste tabele se numesc
vizualizări ( VIEW ) şi se construiesc cu ajutorul instrucţiunii select:
Creez un view numit "medie_grupe" care să conţină toate grupele și media notelor pe acea grupă
în fiecare an universitar:
create or replace view medie_grupe as
select g.pk_grupa , g.cod_grupa, round(avg(nota),2) medie_grupa,
ani.pk_an_universitar
from grupe g, studenti_grupe stg, note n, ani_universitari ani
where g.pk_grupa = stg.pk_grupa
and stg.pk_student_grupa = n.pk_student_grupa
and stg.pk_an_universitar = ani.pk_an_universitar
group by g.pk_grupa, g.cod_grupa, ani.pk_an_universitar
;
Alt view numit "student_grupe_note" ce conţine studenţii cu grupele din care fac parte, disciplinele
studiate și notele obţinute:
Page 71 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
In acest moment am un nou tabel în bază (chiar dacă este virtual) din care pot să fac select :
select * from studenti_grupe_note;
Numai că acesta nu este un tabel obişnuit, este un tabel logic în care nu pot face modificări, inserări, etc.
Este o oglindă unde am grupat în mod avantajos informaţia din tabelele de bază astfel încât să am acces
mai uşor asupra datelor de interes.
Recapitulare:
In acest capitol am văzut cât de performantă poate fi o frază select, ce facilităţi extraordinare ne oferă
pentru căutarea și filtrarea datelor. Dacă facem acum o comparaţie cu modul de stocare a datelor în fișiere,
unde în afară de un suport de stocare aceste fișiere nu-mi oferă nimic, observăm că stocarea datelor în
baza de date este de departe mult mai avantajoasă.
Binenţeles, informaţia introdusă și analizată în tabelele de lucru din acest curs este ultraminimală, ca să nu
ocupăm spaţiul inutil afişând rezultatele selecturilor şi să putem urmări firul rezultatelor. In practică însă, pe
acelaşi principiu se pot stoca informaţii mult mai ample, cu zeci sau sute de mii de studenţi. Un întreg
centru universitar se introduce în aceeaşi bază de date ce rulează pe un singur calculator şi fiecare
profesor, student, secretară de la diverse facultăţi sau universităţi intră cu parola sa prin intermediul
internetului şi introduce sau vizualizează datele la care are dreptul. Putem gândi mai departe să creăm o
bază unică cu toţi studenţii din ţară ș.a.m.d. Să ne gândim ce bază de date uriașă reprezintă Google în
momentul de faţă și totuși ce repede se deschide o pagină Google care în ultimă instanţă, se bazează pe
un select dintr-o bază cu miliarde de linii.
Page 72 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
8 Instrucţiuni DML
Până acum ne-am ocupat de modul de extragere a informaţiilor din baza de date. Acum trebuie să acordăm
un timp special pentru tehnicile de introducere, modificare şi ştergere a datelor. Aceste instrucţiuni, care
modifică baza de date, se numesc instrucţiuni DML (Data Manipulation Language).
Introducerea datelor în bază se face cu ajutorul comenzii INSERT prezentată în prima parte a cursului, fiind
necesară pentru completarea tabelelor iniţiale. Acum, când ştim mai multe despre baze de date, să facem
câteva precizări suplimentare.
Am văzut că formatul acestei comenzi este de forma:
INSERT INTO nume_tabel (nume_coloana_1, nume_coloana_2,....,nume_coloana_n) VALUES
(val_1, val_2, ....., val_n);
Ca să introducem un student în tabela studenti, avem mai multe variante:
insert into studenti(nume_student, prenume_student, nr_matricol)
values('Ionescu','Pavel', 1275);
In al doilea caz nu am mai specificat numele coloanelor, ceea ce înseamnă că le completăm pe toate. Dar
dacă uităm o coloană?
insert into studenti values('Ionescu','Pavel', 1275);
Apare eroare: valori insuficiente. Nu știu ce valoare va primi prin trigger coloana pk_student, dar ea trebuie
pusă în câmpul values. In acest caz pot utiliza valoarea NULL.
Binenţeles, nu pot pune NULL peste tot unde nu ştiu valoarea coloanei.
insert into studenti values(null, null,'Pavel', 1275);
deoarece se sesizează constrângerile definite pe tabela "studenti".
Aceste constrângeri trebuie definite chiar la început, înainte de introducerea datelor, tocmai ca să mă
protejeze împotriva datelor incoerente. Dacă am introdus câteva linii ce nu respectă condiţia de unicitate şi
de "not null", şi încercăm apoi să definim constrângerea, aceasta va eşua. Deoarece, la definire,
Page 73 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
constrângerea verifică dacă sunt date în bază ce nu respectă condiţiile impuse de ea. In caz că găseşte
date incorecte, dă eroare şi anulează definiţia.
Putem avea surpriza ca insert-ul să dea eroare, chiar dacă nu folosim NULL:
insert into studenti_grupe(pk_student) values(100);
In acest caz a fost violată altă constrângere, cea de tip "foreign key" între tabela "studenti_grupe" şi
"studenti". Această constrângere specifică faptul că orice linie introdusă în tabela "studenti_grupe" trebuie
să aibă corespondent în mulţimea valorilor "pk_student" din tabela "studenti".
In momentul când introducem nota în catalog, trebuie să punem şi data examinării. De obicei, punem nota
în catalog în ziua examenului, de aceea, e bine ca sistemul să-mi propună automat ziua curentă pentru
coloana "data" din tabela "note". Pentru aceasta folosim funcţia "sysdate":
insert into note(pk_student_grupa, pk_disciplina, nota, data)
values(26,2,9,sysdate);
Anumite valori introduse de insert pot fi aduse în momentul execuţiei prin comanda select dintr-un alt tabel.
De exemplu, vreau să-i pun notă studentului Ionescu, dar nu ştiu ce pk are (îmi trebuie pk_student la
introducerea datelor în tabela note). Eu ştiu doar că-l cheamă Ionescu şi vreau să-i pun nota 8 la disciplina
'ME':
insert into note(pk_student_grupa, pk_disciplina, nota, data) values(
(select stg.pk_student_grupa from studenti st, studenti_grupe stg,
ani_universitari ani
where st.pk_student = stg.pk_student
and stg.pk_an_universitar = ani.pk_an_universitar
and st.nume_student = 'Ionescu'
and ani.cod_an_universitar = '2011-2012')
,(select pk_disciplina from discipline where cod_disciplina = 'ME')
,8,sysdate);
In acest caz, am adus pk-ul printr-un select din tabela "studenti". Trebuie respectate câteva condiţii ca să
meargă subinterogarea în insert: comanda select trebuie pusă în paranteze, să fie tratată ca un bloc unitar
şi ea trebuie să aducă numărul şi tipul de valori specificate în comanda insert.
Page 74 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Ionescu nu este mulţumit cu nota 8, a venit a doua zi la mărire și a obţinut 10. Dar nu pot să mai pun o notă
în catalog deoarece el primește doar o notă pe sesiune, de aceea ar trebui să modific nota obţinută în ziua
precedentă:
update note set nota=10 where pk_nota =
(select n.pk_nota
from studenti st, studenti_grupe stg, note n, discipline d
where st.pk_student = stg.pk_student
and stg.pk_student_grupa = n.pk_student_grupa
and n.pk_disciplina = d.pk_disciplina
and d.cod_disciplina = 'ME'
and st.nume_student = 'Ionescu');
De ce este importantă clauza where la update? Dacă la select am uitat sau am greşit această clauză, nu-i
nimic, eventual aduce prea multe date sau deloc. Ne uităm prin ele şi ne dăm seama imediat că ceva nu-i
în regulă. Dar dacă uităm să punem clauza "where" la "update"? Atunci e dezastru, modificăm notele la toţi
studenţii din tabela "note". Lipsind clauza "where", comanda "update" nu se mai opreşte la o singură linie
cum am fi dorit, ci parcurge toată tabela de date modificând nota. Iar ca dezastrul să fie complet, Oracle nu
se sesizează ca noi am făcut o greșeală imensă, el nu are de unde să știe că de fapt noi am dorit să
modificăm nota la un singur student.
noile valori pe care le setăm în bază pot veni şi dintr-un alt tabel, aduse prin comanda
"select". Aceste subinterogări trebuie să respecte aceleaşi condiţii de la comanda "insert".
Vreau să șterg nota pusă studentului Ionescu, el zice că preferă să aibă absent în loc de o notă mică.
delete from note where pk_nota in
(select n.pk_nota
from studenti st, studenti_grupe stg, note n, discipline d
Page 75 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
ATENTIE: comanda delete nu şterge doar o valoare dintr-o coloană pe o anumită linie, această comandă
lucrează pe linii, ea şterge toată linia. Rămâne valabilă discuţia pentru clauza "where" de la comanda
"update": dacă clauza "where" este uitată sau greşit scrisă, atunci dezastrul este mai mare decât la
comanda "update". In acest caz se şterg toate liniile din tabel, sau, într-un caz mai fericit, mult mai multe
decât am fi dorit.
Comanda delete şterge liniile din tabel, dar nu şi tabelul ca obiect al bazei de date. Obiectul se șterge prin
comanda DDL (Data Definition Language) "drop":
drop table note;
Comanda DDL "drop" șterge complet tabela și nu se mai poate recupera nimic. Dacă de la delete se mai
pot recupera datele (în anumite condiţii ), după drop nu se mai poate face nimic.
8.3.1Stergerea în cascadă
Am definit o constrângere de tip "foreign key" între tabelele studenti și studenti_grupe bazată pe coloana
"pk_student":
ALTER TABLE studenti_grupe
ADD CONSTRAINT fk_student_grupe_st FOREIGN KEY (pk_student)
REFERENCES studenti (pk_student)
Page 76 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Această constângere nu permite ștergerea unei linii din tabela "studentI dacă există linii aferente în tabela
"studenti_grupe" (nu pot șterge studentul dacă el este înscris în diverse grupe, are note, etc. Toate aceste
informaţii ar rămâne suspendate în aer dacă s-ar șterge studentul).
Modificăm definiţia constrângerii de tip "foreign key" dintre tabele adăugând sufixul "on
delete cascade":
ALTER TABLE studenti_grupe drop CONSTRAINT fk_student_grupe_st;
ALTER TABLE studenti_grupe
ADD CONSTRAINT fk_student_grupe_st FOREIGN KEY (pk_student)
REFERENCES studenti (pk_student) on delete cascade;
Foarte simplă a doua variantă , dar nu indicată. Pentru exemplul nostru a fost simplu, am avut numai aceste
trei tabele, dar ce ne facem dacă de tabela "studenti" se mai leagă şi alte tabele copil, care la rândul lor
sunt părinţi pentru alte tabele şi aşa mai departe? Punem peste tot "ON DELETE CASCADE" ? Nu-i bine,
putem scăpa de sub control aceste ştergeri şi la un moment dat, nu mai ştii de ce a dispărut disciplina de
matematică din bază când tu nu ai făcut decât să ştergi un student.
Page 77 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
Revenim la problema când am pus din greşeală la toată lumea nota 10:
update note set nota = 10 ;
select * from note;
Constat o eroare de proporţii, toată lumea are nota 10, la toate obiectele şi în toate sesiunile.
Din fericire încă mai pot corecta eroarea prin utilizarea comenzii ROLLBACK:
rollback;
Pur şi simplu rulăm rollback şi totul s-a rezolvat, am revenit la situaţia iniţială. Problema e, cât de iniţială
este situaţia? Până la ce punct m-am întors cu baza de date?
Comanda ROLLBACK anulează ultima tranzacţie, dar când a început această tranzacţie?
Orice tranzacţie începe după comanda commit și se termină la comanda commit. Tranzacţia reprezintă un
şir de comenzi de tipul select, update, insert, etc, care este privit de Oracle ca un tot unitar: ori toate
comenzile se termină cu succes, ori sunt anulate toate.
De exemplu, ne hotărâm să ştergem un student din baza de date. Pentru aceasta începem să-i ştergem
notele, apoi prezenţa la curs, laborator, apoi din tabela de examene, apoi de la bibliotecă, etc.După ce
ştergem informaţiile din toate tabelele copil, la sfârşit ștergem din tabela "studenti". Dar ce se întâmplă
dacă, undeva pe parcurs, o instrucţiune dă eroare? De exemplu, când ștergem studentul din registrul
bibliotecii, constatăm că acest tabel are o altă tabelă copil cu cărţile împrumutate de la bibliotecă. Iar acel
student nu a returnat toate cărţile luate împrumut de la bibliotecă. Evident ca procesul de ștergere se
oprește aici și nu poate continua până când studentul nu returnează împrumuturile.
Pe de altă parte, avem deja efectuate câteva ștergeri din baza de date. Acestea trebuie refăcute, pentru că
nu putem lăsa unele tabele sterse și altele nu. Intreg procesul de ștergere trebuie terminat cu succes, caz
Page 78 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
în care încheiem tranzacţia prin comanda "Commit", altfel situaţia în bază trebuie să rămână aceeași.
Refacerea situaţiei se face prin comanda ROLLBACK , caz în care ne întoarcem la situaţia de la ultimul
commit executat.
Comanda commit încheie tranzacţia curentă astfel încât, toate modificările realizate până în acel moment
trec în stare definitivă. Prima comandă SQL după commit începe o nouă tranzacţie care se termină la
următorul commit. Să reţinem deci că prin ROLLBACK putem reface doar modificările din tranzacţia
curentă, nimic nu ne mai salvează după execuţia comenzii COMMIT.
9.2.1Commit implicit
Se întamplă ca uneori comanda Rollback să nu mai restaureze situaţia dată de ultimul commit. Aceasta
pentru că, uneori baza de date realizează un commit implicit, adică execută comanda commit fara ca noi să
comandăm explicit acest lucru.
Commit-ul implicit se execută atunci când utilizatorul rulează o comandă specială de tip DDL sau DCL:
comandă DDL (Data Definition Language): de exemplu comanzile "create table..", "alter
table...",etc.
comandă DCL (Data Control Language): "create user...", "grant user...",etc.
Este util câteodată să împărţim o tranzacţie în mai multe subtranzacţii şi să anulăm prin rollback numai o
anumită subtranzacţie. De exemplu, vindem un produs la un client, proces ce presupune mai multe etape:
- scoatem produsul din magazie (modificăm stocul din magazia respectivă)
- facem factură pentru client (adăugăm un document în tabela de documente emise)
- contăm acel document (pentru evidenţa contabilă)
Ce se întâmplă dacă nu merge contarea ? (nu ştiu exact care sunt conturile pe care trebuie să contez). Nu
mai vând produsul clientului? Dar el este la poartă şi aşteaptă să plece cu produsul.
In acest caz salvăm câteva puncte intermediare în tranzacţia noastră. După scoaterea produsului din
magazie definim un punct intermediar de succes :
SAVEPOINT iesire_magazie_succes;
Mergem mai departe şi scoatem şi factura:
SAVEPOINT factura;
Apoi contăm factura. Dacă totul e OK dăm un COMMIT, altfel mă intorc la ultima situaţie de succes:
ROLLBACK to factura;
Page 79 of 98
Product name: Programarea sistemelor de masura I – note de Date: Sep - 2020
curs
In acest fel am reuşit să să scoatem factura clientului, rămânând pentru mai târziu să rezolvăm problema
contării.
Page 80 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
10 PL/SQL
SQL este OK în majoritatea taskurilor pe care le avem de îndeplinit când lucrăm cu o bază de date. Dar de
multe ori, există necesitatea de a putea rula câteva linii de cod direct pe bază, să putem extrage informaţia
într-un mod mai flexibil.
De exemplu, m-ar interesa să afişez pentru fiecare student media sa generală calculată prin media
aritmetică a tuturor notelor obţinute de student. Dar doresc ca media generală să nu fie influenţată de
notele obţinute la sport sau alte materii opţionale. Mi-ar fi foarte uşor să aflu aceste informaţii dacă aş putea
scrie o funcţie care să primească ca parametru pk_student (identificatorul unic al studentului) şi să-mi
returneze acele valori de care am nevoie. E clar că într-o funcţie pot folosi acele bucle FOR, LOOP, WHILE,
condiţii IF de care avem atâta nevoie când este necesar să alegem din mai multe variante, să facem tot
felul de sume, să extragem informaţia în funcţie de anumite condiţii.
Oricât de puternic ar fi SQL-ul, tot mai avem nevoie de linii de cod, de tehnicile specifice limbajelor de
programare. Un alt avantaj al acestor programe (pe care le denumim PL/SQL) este dat de faptul că ele
rulează pe server, deci nu încărcăm în mod excesiv reţeaua de calculatoare cu un trafic imens între staţia
noastră şi server (de cât să cer de zeci de ori cu câte un select o porţiune de informaţie, scriu o funcţie
PL/SQL în bază care returnează o singură dată tot blocul de date cerut).
Ce înseamnă PL/SQL? înseamnă SQL introdus într-o structură de programare. Prin SQL obţinem o
informaţie care se afişează şi atât. Dar dacă vreau ca această informaţie să o salvez într-o variabilă şi apoi
să o prelucrez conform unui algoritm? In SQL clasic nu putem, de aceea folosim PL/SQL.
Programele PL/SQL pot fi funcţii, proceduri, sau blocuri anonime de cod. Diferenţa între funcţii şi proceduri?
Funcţia întoarce o valoare, procedura nu întoarce nimic, prin codul ei modifică un obiect din baza de date
sau valoarea unui parametru.
Page 81 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
exception
WHEN exception_name THEN
instructiuni tratare exceptii ;
END;
Prima parte din program este rezervată declaraţiilor de variabile. Urmează blocul de instrucţiuni care se
termină cu instrucţiunea "end". Intre "begin" si "end" se poate include eventual clauza "exception" unde se
introduc instrucţiuni de tratare a erorilor apărute în blocul de instrucţiuni. Structura respectă arhitectura
oricărui limbaj de programare: declaraţie variabile, instrucţiuni, tratare erori (construcţia begin .... exception
... end poate fi privită ca un bloc try ... catch din limbajele de programare moderne).
Lucrând pe o bază de date care ne poate surprinde oricând cu date invalide sau lipsă, este obligatorie
tratarea erorilor printr-o clauză de tip exception, altfel eroarea se transmite mai departe şi apare la interfaţa
utilizator cu tot felul de mesaje Oracle neinteligibile pentru utilizatorul final.
Există o medie ponderată ce ia în calcul creditele obţinute de un student când promovează un examen.
Această medie are formula:
nr _ total _ discipline
nr _ credite * nota i i
Medie _ ponderata i 0
nr _ total _ discipline
nr _ credite
i 0
i
Se face produsul dintre numărul de credite de la fiecare disciplină studiată de student într-un an de studiu și
nota obţinută de student. Suma produselor se împarte la numărul total de credite. In caz că studentul nu are
notă, atunci se consideră nota zero.
Este destul de clar că această problemă nu prea poate fi rezolvată prin SQL, deci se va construi o funcţie
ce primește ca parametru un numar dat de "pk_student_grupa" și va returna un număr ce reprezintă media
ponderată.
cursor c_discipline_studiate is
select d.pk_disciplina, d.cod_disciplina, d.credite
from studenti_grupe stg, grupe g, discipline d
Page 82 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
v_medie_ponderata number := 0;
sum_credite number := 0;
sum_medie_credite number := 0;
nota_curenta number := 0;
begin
for rec in c_discipline_studiate loop
open c_note(rec.pk_disciplina);
fetch c_note into nota_curenta;
if c_note%notfound then
nota_curenta := 0;
end if;
close c_note;
sum_medie_credite := sum_medie_credite + nvl(rec.credite,0) *
nvl(nota_curenta,0);
sum_credite := sum_credite + nvl(rec.credite,0);
end loop;
if sum_credite != 0 then
v_medie_ponderata := sum_medie_credite/sum_credite;
else
v_medie_ponderata := 0;
end if;
return round(v_medie_ponderata,2) ;
Page 83 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
Am declarat două cursoare, unul ce aduce toate disciplinele studiate de studentul dat, celălalt aduce nota
obţinută de student la o disciplină dată.
Am baleiat întreg cursorul "c_discipline_studiate" (for rec in
c_discipline_studiate loop) şi am calculat suma produselor dintre credite și note:
sum_medie_credite:=sum_medie_credite+nvl(rec.credite,0)*nvl(nota_curenta,0);
Dacă studentul nu are notă, sau disciplina nu are credite, atunci se pune implicit zero
(nvl(nota_curenta,0).
La sfârșit se calculează media prin împărţire:
if sum_credite != 0 then
v_medie_ponderata := sum_medie_credite/sum_credite;
else
v_medie_ponderata := 0;
end if;
Nu lipsește clauza EXCEPTION unde se intră în caz de o eroare în codul nostru ("when others"
adică oricare ar fi eroarea , ea va fi tratată în acest segment). Aici nu facem nimic altceva decât
returnăm zero.
Nu explicăm aici cum lucrează instrucţiunea IF sau altele (FOR, WHILE, EXIT, ... ) ele au aceeaşi
funcţiune ca-n orice limbaj de programare. Trebuie doar să fim atenţi la sintaxa lor care se
aseamănă cu cea din DELPHI.
Testăm funcţia printr-un select obișnuit din bază:
select st.nume_student, st.prenume_student,
calcul_medie_ponderata(stg.pk_student_grupa) medie_ponderata
from studenti st, studenti_grupe stg
where st.pk_student = stg.pk_student
;
10.3 Triggeri
Page 84 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
TRIGER-ul este o secţiune de cod ce se execută automat la un anumit eveniment. Odată definit acel
eveniment, baza de date are grijă să lanseze în execuţie trigger-ul corespunzător ori de câte ori are loc
evenimentul. Este foarte util atunci când trebuie executate anumite sarcini în mod automat când sunt
întrunite anumite condiţiile impuse.
La inserarea datelor în baza de date am folosit în mod extensiv triggerii de completare a valorilor din
coloanele de tip "primary key". Am discutat în acel capitol problema completării valorilor unice și modul de
rezolvare prin utilizarea secvenţelor și trigerilor:
In acest mod, indiferent de câţi utilizatori introduc simultan date în bază, se asigură în mod unic o valoare
de PK pentru fiecare.
Sintaxa unui trigger este aceeași cu a unui bloc PL/SQL cu diferenţa că, în cazul triggerului avem acces la
valorile vechi și noi din linia de tabel care tocmai se modifică.
In header-ul triggerului se definesc condiţiile de rulare care fac să se execute triggerul. In exemplul de mai
sus, dorim ca triggerul să se execute înaintea unui insert în tabela studenti_grupe:
BEFORE INSERT ON studenti_grupe
REFERENCING NEW AS NEW OLD AS OLD FOR EACH ROW
Ceea ce se traduce prin următoarele:
- Triggerul se execută înainte de a introduce datele (before insert)
- Se aplica la tabela "studenti_grupe" (ON studenti_grupe)
- Informaţiile care sunt înlocuite sunt accesibile prin construcţia "OLD", iar cele noi prin
constructia "NEW". Exemplu: ":new.pk_student_grupa" este valoarea care tocmai se
dorește a se introduce în linia curentă, coloana "pk_student_grupa". Avand acces la ambele
valori, putem face comparaţii și analize care să decidă dacă operaţia de modificare este
corectă sau nu.
- Triggerul se va executa pentru fiecare linie afecată de operaţia în desfașurare din tabelă (FOR
EACH ROW).
Page 85 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
Un exemplu pentru demonstrarea utilităţii triggeri-lor este dat de necesitatea verificării notelor inserate în
tabela note. Din diverse cauze, poate interveni eroarea ca un student să primească notă la o disciplină pe
care nu o studiază (sunt mai multe surse de program care pot insera note: modulul de profesori, secretara
și pot interveni erori de acest tip). Binenţeles că se pot face verificări pe parcursul inserării notelor, dar toate
aceste verificări pot da eroare la fel de bine. De aceea, baza de date este ultima și cea mai sigură barieră
împotriva erorilor de acest fel. Se definește un trigger de verificare a notelor și în mod sigur acesta se va
executa întotdeauna când se introduce o nouă notă în tabel.
dummy number;
begin
open c_discipline_studiate;
fetch c_discipline_studiate into dummy;
if c_discipline_studiate%notfound then
raise_application_error(-20101,'studentul nu poate primi nota
pentru ca nu studiaza disciplina respectiva');
end if;
close c_discipline_studiate;
exception when others then
raise_application_error(-20101,'Eroare ' || sqlerrm);
end;
end;
Page 86 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
Recapitulare:
Acest ultim capitol din curs introduce câteva noţiuni elementare despre programarea PL/SQL.
Nu toate bazele de date suportă limbajul PL/SQL ci doar cele mai performante.
Executarea sarcinilor direct pe server, prin programare PL/SQL are multiple avantaje (trafic redus
în reţea, viteză mare de execuţie, eliminarea conflictelor de date între sesiuni, etc).
Se pot define funcţii, proceduri, biblioteci de asemenea programe, triggeri ce se execută automat
pe evenimente prestabilite.
Ușurează foarte mult din munca programaoriloe de aplicaţii complexe.
Page 87 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
11 Bibliografie
http://office.microsoft.com/ro-ro/access-help/notiuni-de-baza-despre-proiectarea-bazelor-de-date-
HA001224247.aspx
http://www.w3schools.com/sql/default.asp
http://st-curriculum.oracle.com/tutorial/SQLDeveloper/index.htm
http://www.java2s.com/Tutorial/Oracle/CatalogOracle.htm
Page 88 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
12 Anexe
12.1 Instructiunile SQL pentru crearea obiectelor în baza de date
In continuare sunt date frazele SQL ce creează tabelele de lucru împreună cu legăturile între ele.
--ani studiu
drop table ani_studiu;
create table ani_studiu
(
pk_an_studiu number,
cod_an_studiu varchar2(2),
descriere_an_studiu varchar2(50)
);
alter table ani_studiu add constraint pk_an_studiu primary key (pk_an_studiu);
alter table ani_studiu add constraint uk_cod_ani_studiu unique (cod_an_studiu);
-- specializari
drop table specializari;
create table specializari
(
pk_specializare number,
cod_specializare varchar2(15),
descriere_specializare varchar2(50)
);
alter table specializari add constraint pk_specializare primary key
(pk_specializare);
alter table specializari add constraint uk_specializare unique
(cod_specializare);
-- grupe
drop table grupe;
Page 89 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
--studenti
drop table studenti;
create table studenti
(
pk_student number,
nume_student varchar2(50),
prenume_student varchar2(50),
nr_matricol number
);
--studenti_grupe
drop table studenti_grupe;
create table studenti_grupe
(
pk_student_grupa number,
pk_student number,
pk_grupa number,
pk_an_universitar number
);
alter table studenti_grupe add constraint pk_student_grupa primary key
(pk_student_grupa);
alter table studenti_grupe add constraint fk_student_grupe_st foreign key
(pk_student) references studenti(pk_student);
alter table studenti_grupe add constraint fk_student_grupe_gr foreign key
(pk_grupa) references grupe(pk_grupa);
alter table studenti_grupe add constraint fk_student_grupe_au foreign key
(pk_an_universitar) references ani_universitari(pk_an_universitar);
--discipline;
drop table discipline;
create table discipline
(
pk_disciplina number,
cod_disciplina varchar2(50),
Page 90 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
descriere_disciplina varchar2(100),
pk_specializare number,
pk_an_studiu number,
semestru number,
credite number
);
--note
drop table note;
create table note
(
pk_nota number,
pk_student_grupa number,
pk_disciplina number,
nota number,
data date
);
alter table note add constraint pk_nota primary key (pk_nota);
alter table note add constraint fk_nota_student
foreign key (pk_student_grupa) references studenti_grupe(pk_student_grupa);
alter table note add constraint fk_not_disciplina foreign key
(pk_disciplina) references discipline(pk_disciplina);
ALTER TABLE note ADD CONSTRAINT ck_note CHECK (nota <= 10);
/* -------------------------------------------------------------------------
Secvente
--------------------------------------------------------------------------
*/
CREATE SEQUENCE pk_an_universitar
INCREMENT BY 1
START WITH 1
MINVALUE 1
MAXVALUE 9999999999999;
CREATE SEQUENCE pk_an_studiu;
CREATE SEQUENCE pk_specializare;
CREATE SEQUENCE pk_grupa;
CREATE SEQUENCE pk_student;
CREATE SEQUENCE pk_student_grupa;
CREATE SEQUENCE pk_disciplina;
CREATE SEQUENCE pk_nota;
/* ---------------------------------------------------------------------------
Page 91 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
----------------------------------------------------------------------
*/
Page 92 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
--2. ani_studiu
insert into ani_studiu values(null,1,'an 1');
insert into ani_studiu values(null,2,'an 2');
insert into ani_studiu values(null,3,'an 3');
insert into ani_studiu values(null,4,'an 4');
insert into ani_studiu values(null,5,'an 5');
insert into ani_studiu values(null,6,'an 6');
--3. specializari
insert into specializari(cod_specializare,descriere_specializare)
values('IAD', 'Instrumentatie si Achizitii de Date');
insert into specializari(cod_specializare,descriere_specializare)
values('IA', 'Informatica Aplicata');
insert into specializari(cod_specializare,descriere_specializare)
values('EM', 'Electromecanica');
insert into specializari(cod_specializare,descriere_specializare)
values('EPAE', 'Actionari');
--4. discipline
insert into discipline values(null,'PSM1','Progr.Sist. de Masurare1',1,4,1);
insert into discipline values(null,'IB','Instrumentatie de bord',1,4,1);
insert into discipline values(null,'ME','Masurari Electrice',1,3,1);
insert into discipline values(null,'BD','Baze Date',2,3,1);
insert into discipline values(null,'MEP','Masini Electrice de putere',3,4,1);
insert into discipline values(null,'AE','Actionari Electrice',4,3,2);
--5. grupe
insert into grupe values(null,'6403','Grupa 6403',1,4);
Page 93 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
Page 94 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
-- 7. Note
--grupa 6404
delete from note;
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,26,2,5,sysdate-401);
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,26,1,4,sysdate-402);
Page 95 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
values(-1,7,2,10,sysdate-1);
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,7,1,10,sysdate);
--grupa 6401
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,15,5,7,sysdate);
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,16,5,9,sysdate);
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,17,5,4,sysdate);
--grupa 6303
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,25,3,5,sysdate);
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,21,3,9,sysdate);
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,24,3,8,sysdate);
insert into note(pk_nota,pk_student_grupa,pk_disciplina,nota,data)
values(-1,22,3,6,sysdate);
commit;
Page 96 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
12.2.1 Studenti-grupe-discipline-note
select stg.pk_student_grupa, st.nume_student, st.prenume_student, g.cod_grupa,
d.cod_disciplina, n.nota, n.data, d.pk_disciplina, ast.cod_an_studiu,
au.cod_an_universitar
from studenti_grupe stg, studenti st, ani_universitari au, grupe g,
specializari s,
discipline d, ani_studiu ast, note n
where stg.pk_student = st.pk_student
and stg.pk_an_universitar = au.pk_an_universitar
and stg.pk_grupa = g.pk_grupa
and g.pk_specializare = s.pk_specializare
and s.pk_specializare = d.pk_specializare
and g.cod_grupa = '6404'
and g.pk_an_studiu = d.pk_an_studiu
and g.pk_an_studiu = ast.pk_an_studiu
and stg.pk_student_grupa = n.pk_student_grupa
and d.pk_disciplina = n.pk_disciplina
order by au.cod_an_universitar, g.cod_grupa,st.nume_student
;
Page 97 of 98
Baze de date
Project no. BD 2010 Product name: Baze de date – note de curs Date: Feb - 2010
Page 98 of 98