Sunteți pe pagina 1din 43

Baze de Date pentru Telecomunicaţii

Lucrarea 2

Crearea şi administrarea tabelelor


Cuprins

Generalităţi.......................................................................................................................................3
Crearea bazelor de date...................................................................................................................3
Tabelele............................................................................................................................................3
Tipuri de date suportate...................................................................................................................3
Denumirea tabelelor şi coloanelor....................................................................................................5
Crearea unei tabele...........................................................................................................................5
Modificarea structurii tabelelor........................................................................................................8
Ştergerea tabelelor..........................................................................................................................10
Popularea tabelelor..........................................................................................................................10
Constrângerile.................................................................................................................................12
Constrângerile de domeniu.............................................................................................................12
Cheile..............................................................................................................................................13
Chei primare....................................................................................................................................14
Chei străine......................................................................................................................................17
Exerciţiu (cerinţă şi rezolvare).........................................................................................................24
1) Modelarea informaţiilor despre clienţi.................................................................................24
2) Modelarea informaţiilor despre tipurile de abonament.......................................................27
3) Modelarea abonamentelor....................................................................................................29
4) Modelarea facturilor............................................................................................................32
5) Modelarea tipului de serviciu..............................................................................................33
6) Modelarea convorbirilor şi serviciilor folosite...................................................................34
7) Popularea bazei de date......................................................................................................36
8) Extragerea de informaţii pentru facturare...........................................................................40
9) Extragerea de informaţii legate de serviciile folosite.........................................................41
Bibliografie.....................................................................................................................................43

2
Generalităţi

Scopul bazelor de date relaţionale este acela de a memora informaţii care modelează entităţi din
realitate, dar şi relaţiile matematice care apar între mulţimile de entităţi.
O bază de date relaţională are la bază mai multe “relaţii”. O relaţie reprezintă o mulţime de entităţi sau o
mulţime de asocieri între entităţi. Relaţia este definită de atribute şi domeniile de definiţie ale acestora.
Înainte de a decide care este structura unei baze de date (adică a se stabili concret ce tabele vor fi
necesare şi ce coloane vor avea acestea), este necesar să se analizeze problema pentru care se doreşte crearea
unui model. Trebuie ca structura folosită să modeleze suficient de detaliat entităţile implicate şi, de asemenea,
să asigure faptul că baza de date nu va memora informaţii dublate din motive de eficienţă a utilizării spaţiului
de stocare şi nu numai. Deşi spaţiul disponibil în sine poate nu este o problemă, o bază de date de dimensiuni
foarte mari se va comporta cu siguranţă suboptimal. Operaţiile efectuate asupra acesteia vor dura mai mult
timp, în special dacă nu există niciun fel de optimizări de tipul indecşilor.

Crearea bazelor de date


Pentru a crea o bază de date, se foloseşte instrucţiunea CREATE DATABASE. Baza de date este
necesară pentru a putea crea tabele.
Pentru această temă, se va folosi baza de date creată la instalarea Oracle, RaluDB. Dacă baza de date nu
ar fi fost creată în timpul procesului de instalare, pentru crearea ei s-ar fi folosit o instrucţiune de forma:

CREATE DATABASE RaluDB;

Aceasta este o formă simplistă a instrucţiunii, însă se pot folosi parametri pentru a specifica spaţiul
alocat pe disc. Condiţia esenţială este ca utilizatorul care crează baza de date să aibă drepturi de acces care să
îi permită acest lucru.
Aspectul cu adevărat important este designul bazei de date. Se doreşte normalizarea relaţiilor pentru a
evita existenţa informaţiilor dublate în baza de date. De asemenea, trebuie impuse constrângeri asupra
valorilor pe care atributele relaţiilor le pot lua pentru a asigura funcţionarea corectă a aplicaţiei.
Pentru fiecare structură sau funcţie/procedură de pe baza de date este nevoie să se documenteze codul
pentru a permite lucrul în echipă şi pentru a uşura efortul depus la modificări ulterioare. Documentaţia este
cunoscută ca “data dictionary” şi are ca scop descrierea bazei de date şi a celor care o vor utiliza, informaţii
legate de dimensiunea bazei de date sau a logului, scripturi pentru instalare/import/export, descrierea detaliată
a structurii fiecărei tabele, codul sursă pentru procedurile stocate/triggeri, constrângeri de unicitate sau de
domeniu de definiţie etc.

Tabelele
Un tabel este reprezentarea practică a noţiunii teoretice de relaţie (relaţia este o noţiune abstractă, cu
sensul de mulţime matematică). Se poate spune că un tabel este format din :
 Numele său (ajută la identificarea entităţilor reale pe care le modelează);
 Coloane (corespund valorilor atributelor relaţiilor);
 Capul tabelului (header-ul tabelului, cuprinzând numele atributelor relaţiei; se poate spune că
acesta corespunde schemei relaţiei);
 Tupluri (reprezintă înregistrări sau linii din tabelă, adică valori particulare pentru atributele
relaţiei).

Tipuri de date suportate


Sistemul de gestiune de baze de date Oracle are un număr considerabil de tipuri de date ”built-in” care
se pot folosi pentru definirea tipurilor de date asociate coloanelor din tabele.
3
În tabelul de mai jos sunt prezentate pe scurt tipurile de date suportate de Oracle.

Tipul de dată Descriere


Varchar2(n[byte|char]) Şir de caractere de lungime variabilă cu lungimea maximă de n bytes sau
caractere. Dimensiunea maximă este de 4000 de caractere, iar cea minimă de un
caracter. Trebuie specificată dimensiunea când se foloseşte. Se poate specifica
dacă lungimea va fi în bytes sau în caractere.
Nvarchar2(n) Şir de caractere de lungime variabilă, cu lungimea maximă determinată de setul
de caractere naţional setat pe sistemul respectiv. Limita maximă este de 4000 de
bytes. Trebuie să se specifice lungimea.
Number(p, s) Număr cu precizia p şi scară s. Precizia ia valori între 1 şi 38, iar scara între -84 şi
127.
Long Date de tip caracter cu lungime variabilă. Lungimea maximă este de până la 2GB
sau 231-1 bytes.
Date Modelează data calendaristică. Valorile valide se regăsesc între 1 ianuarie 4712
î.H. şi 31 decembrie 9999.
Binary_float Număr în virgulă mobilă, reprezentat pe 32 biţi. Sunt necesari 5 bytes pentru
reprezentare, incluzând byte-ul de lungime.
Binary_double Număr în virgulă mobilă, reprezentat pe 64 de biţi. Sunt necesati 9 bytes pentru
reprezentare, incluzând unul pentru lungime.
Timestamp(p) Specifică valori pentru zi, lună, an, dar şi oră, minut şi secundă ale timpului. ”p”
reprezintă precizia de reprezentare a părţii fracţionale ale secundelor. Valoarea
implicită este 6, dar sunt acceptate valori între 0 şi 9.
Timstamp(p) with time În plus faţă de timestamp(p), include informaţii despre poziţionarea pe Glob.
zone
Timestamp(p) with local Valori ca şi pentru timstamp(p) with time zone, cu următoarele excepţii:
time zone - Data este normalizată la zona de timp a bazei de date în momentul în care
este inserată;
- Când data este returnată din baza de date, utilizatorii o primesc în time
zone-ul specific sesiunii.
Interval year(p) to month Memorează o perioadă de timp în ani şi luni, unde p este numărul de cifre din
câmpul corespunzător anului. Se acceptă valori între 0 şi 9, iar valoarea implicită
este 2.
Interval day(d) to Memorează o perioadă de timp în zile, ore, minute. ”d” este numărul maxim de
second(s) cifre din câmpul zi (se acceptă valori între 0 şi 9, iar cea implicită este 2). ”s” este
numărul de cifre din partea fracţională a câmpului secundă (se acceptă valori între
0 şi 9, cu valoarea implicită de 6).
Raw(n) Date binare brute de lungime n. Dimensiunea maximă este de 2000 de bytes. Este
necesar să se specifice dimensiunea unui astfel de câmp.
Long raw Date binare brute de lungime variabilă, cu lungimea maximă de 2GB.
Rowid Şir de caractere codat base 64, reprezentând adresa unică a unui tuplu într-o
tabelă. Acest tip este returnat în pseudo-coloana rowid.
Urowid[(n)] Şir de caractere codat base 64, reprezentând adresa logică a unui tuplu într-o
tabelă indexată. ”n” este lungimea câmpului care poate fi de maxim 4000 de
bytes.
Char(n[byte|char]) Date de tip caracter, cu lungime fixă. Dimensiunea maximă este de 2000 de bytes
sau caractere. Dimensiunea implicită şi minimă este de 1 byte.
4
Nchar(n) Date de tip caracter de lungime n. Lungimea maximă este determinată de tipul de
limbă din care se ia setul de caractere, dar are o lungime maximă de 2000 de
bytes.
Clob Un obiect mare de caractere conţinând caractere single-byte sau multi-byte. Sunt
suportate seturi de caractere cu lungime fixă şi variabilă. Dimensiunea maximă
este de (4GB-1)*dimensiunea blocului din baza de date.
Nclob Diferenţa faţă de clob este aceea că este format din caractere unicode şi că
foloseşte setul de caractere specific limbii.
Blob Un obiect binar mare. Dimensiunea maximă este de (4GB-1)*dimensiunea
blocului din baza de date.
Bfile Conţine informaţii despre locaţia unui fişier binar mare aflat în afara bazei de
date. Permite accesul la obiecte mari binare externe bazei de date, dar aflate pe
serverul de bază de date. Dimensiunea maximă este de 4 GB.

Denumirea tabelelor şi coloanelor


Numele tabelelor se dă în conformitate cu datele pe care le vor conţine. Trebuie ca tabelele să aibă nume
diferite dacă sunt în aceeaşi schemă. În caz contrar, baza de date va genera o eroare, specificând că numele
este deja asociat unui obiect existent în baza de date. Crearea noii tabele va eşua.

create table abonati(id int not null);

Acelaşi lucru se întâmplă şi în cazul în care se doreşte adăugarea unei coloane la o tabelă dacă numele
noii coloane este identic cu numele unei coloane deja existente în tabelă, cu diferenţa că mesajul de eroare
specifică o altă eroare.

alter table abonati add id number;

5
Crearea unei tabele
Pentru crearea tabelei din exemplul de mai jos s-a folosit sintaxa:
create table abonati(id int not null,
nume varchar(50) not null,
prenume varchar(50) not null,
telefon varchar(20) not null,
cnp number(13) not null
)
Această metodă creează o tabelă nouă (”de la 0”) care va fi iniţial nepopulată.
S-a creat o tabelă cu numele ”abonati” care are următoarele coloane :
 id, reprezentând un identificator unic al tuplurilor şi, prin urmare, al fiecărui abonat;
 nume, prenume, telefon, cnp reprezintă datele personale ale abonatului.
Se observă că pentru toate coloanele s-a definit constrângerea not null pentru a nu permite introducerea
unor date personale incomplete. Coloana ”id” va fi folosită pentru a identifica în mod unic tuplurile din tabelă,
dar deocamdată i s-a impus doar constrângerea NOT NULL.

Am folosit tipul varchar pentru a memora numele, prenumele şi numărul de telefon deoarece acestea nu
au o dimensiune standard pentru toţi abonaţii (nu putem spune că numele tuturor abonaţilor are acelaţi număr
de litere; de asemenea, numărul de cifre ale numărului de telefon poate diferi în funcţie de operator). Folosind
varchar(n), se limitează numărul maxim de caractere la n, însă, dacă un abonat are numele mai mic de n
caractere, spaţiul ocupat efectiv de câmpul corespunzător din tuplul respectiv este exact numărul de caractere
al numelui.
O altă metodă de a crea o tabelă este pornind de la o tabelă deja existentă. Astfel, se creează o tabelă cu
o structură identică celei a tabelei deja existentă. Mai mult, noua tabelă va conţine şi datele care existau în
tabela veche la momentul creării sale şi care corespund unei eventuale condiţii specificate.
Pentru început, se listează conţinutul tabelei deja existente pentru a putea compara, la final, conţinutul
tabelei originale cu al celei care urmează să se creeze.

select * from abonati;

6
Apoi, se creează o nouă tabelă în care se vor selecta doar abonaţii cu abonament de tip 2.

create table tabela_noua(id, nume, prenume, telefon, cnp, id_tip_abonament)


as (select id, nume, prenume, telefon, cnp, id_tip_abonament from ABONATI
where id_tip_abonament=2);

Se observă că nu au fost obţinute erori, deci tabela a fost creată. Pentru a vedea ce date conţine aceasta,
se va executa

select * from tabela_noua;

Se observă că această tabelă conţine tuplurile din tabela ”abonati” care corespundeau condiţiei din
instrucţiunea de creare a tabelei (doar tuplurile pentru care id_tip_abonament=2) şi are structura specificată. O
observaţie este că tabela nou creată nu va moşteni şi constrângerile de chei primare sau străine ale tabelei
originale.
7
Modificarea structurii tabelelor
De multe ori, este nevoie să se adauge una sau mai multe coloane la o tabelă deja existentă. Adăugarea
nu afectează în niciun fel coloanele deja existente ale tabelei respective. În cazul în care se specifică o valoare
implicită pentru coloana nou adăugată, toate tuplurile existente în tabelă se vor completa cu valoarea implicită
pentru coloana nou adăugată.
Se presupune că la tabela ”abonati” se doreşte introducerea unor informaţii suplimentare legate de
adresa de corespondenţă la care să se trimită facturile, de exemplu. Pentru aceasta, se va adăuga o coloană,
”adresa” la tabela ”abonati”. Pentru a exemplifica, se va seta ca valoare implicită ”Bucuresti” şi se va adăuga
şi constrângerea not null.

alter table abonati add adresa varchar(200) default 'Bucuresti' not null;

Pentru a observa ce s-a întâmplat cu tuplurile deja existente, se execută select * from abonati.

Se vede că toate tuplurile existente s-au completat cu valoarea ”Bucuresti” pentru câmpul corespunzător
coloanei nou adăugate.
Există şi posibilitatea de a modifica o coloană dintr-o tabelă (se schimbă, de exemplu, dimensiunea
coloanei respective). Trebuie ţinut cont de faptul că micşorarea dimensiunii unei coloane nu este permisă în
cazul în care tabela respectivă conţine un tuplu în care valoarea corespunzătoare coloanei respective are o
dimensiune mai mare decât noua dimensiune care se doreşte.
Se va încerca reducerea numărului de caractere al coloanei ”adresa” la maxim 6 caractere. În acest caz,
baza de date va genera o eroare deoarece toate tuplurile din tabelă conţin valoarea implicită, ”Bucuresti”, a
coloanei ”adresa” (lungimea este de 9 caractere, adică mai mare decât noua dimensiune care se dorea setată).

alter table ABONATI modify adresa varchar(6);


8
Pentru exemplificarea unei redimensionări reuşite, se presupune că se doreşte micşorarea dimensiunii
coloanei ”adresa” la 100 de caractere.

alter table ABONATI modify adresa varchar(100) default 'Bucuresti';

Există şi cazuri în care se doreşte eliminarea uneia sau mai multor coloane dintr-o tabelă. Se exemplifică
prin ştergerea coloanei ”adresa” din tabela ”abonati”.

alter table abonati drop column adresa;

Se observă că instrucţiunea de mai sus s-a executat fără erori. Pentru a vedea efectul asupra tabelei, se
va executa din nou :

select * from abonati;

9
Ştergerea tabelelor
Pentru ştergerea unei tabele se execută o instrucţiune de tip drop. Pentru exemplificare, se va şterge
tabela ”tabela_noua” creată într-unul din exemplele anterioare.
drop table tabela_noua;

Popularea tabelelor
Pentru a popula o tabelă, se execută instrucţiuni de tip insert, specificându-se tabela, coloanele din tabelă
care vor fi completate şi valorile pentru coloanele respective.
Pentru a insera mai multe rânduri în tabela creată anterior, am folosit instrucţiunile de mai jos.

insert all
into abonati(id, nume, prenume, telefon, cnp) values (1, 'Ion', 'Ion',
'0700123456', 1901231256314)
into abonati(id, nume, prenume, telefon, cnp) values (2, 'Ion', 'Maria',
'0700654321', 2901231256395)
into abonati(id, nume, prenume, telefon, cnp) values (3, 'Georgescu',
'Nicolae', '0213512647', 1901231783314)
select 1 from dual

10
După inserarea celor trei tupluri, se poate executa o instrucţiune select pentru a vedea că cele trei tupluri
se regăsesc în tabelă.
select * from abonati

Rezultatele pot fi afişate şi sub formă de tabel, pentru o mai bună organizare.

Se observă că inserarea a fost făcută cu succes. Instrucţiunea insert all aşteaptă o instrucţiune select la
final, dar, întrucât datele care au fost introduse în tabela abonati nu existau într-o altă tabelă, s-a folosit ”select
1 from dual”. Obiectul ”dual” este o tabelă fictivă(”dummy-table”) creată de Oracle odată cu crearea bazei de
date şi a dicţionarului. Aceasta are o singură coloană, ”DUMMY”, şi conţine un singur tuplu, ”X”. La
execuţia unui select din dual, se va returna un singur tuplu. Această tabelă este folosită pentru cazurile în care
se doreşte execuţia unui cod SQL care nu are asociată logic o tabelă (în exemplul de mai sus, am folosit-o
doar pentru că ”insert all” aştepta o instrucţiune select). Tabela ”dual” se află în schema SYS, dar poate fi
apelată din orice altă schemă.

select * from dual

11
Constrângerile
Deoarece structura bazelor de date trebuie să modeleze realitatea, datele conţinute de acestea trebuie să
se încadreze în anumite mulţimi de valori. Aceste constrângeri sunt definite ca nişte reguli la proiectarea bazei
de date şi trebuie respectate întotdeauna.
Se pot diferenţia două tipuri de constrângeri din punctul de vedere al locului unde sunt definite : intra-
relaţie şi inter-relaţie. Cele intra-relaţie se referă la o singură relaţie (sau tabelă, în practică) şi se împart în
constrângeri de domeniu, constrângeri de tuplu etc. Cele inter-relaţie se referă la una sau mai multe tabele,
fiind modelate prin cheile străine şi asigurând asocierea corectă a relaţiilor.
Constrângerile de domeniu sunt impuse valorilor atributelor pentru ca acestea să corespundă realităţii.
Acestea afectează coloanele şi, din acest motiv, se mai numesc şi constrângeri de coloană. Există constrângeri
care se pot aplica pe orice tip de coloană (de exemplu, DEFAULT care setează valoarea implicită a atributelor
relaţiei sau NOT NULL care constrânge coloana să aibă valori valide).
Constrângerile de tuplu (cheie primară şi chei secundare) afectează tabela în sensul că aceasta nu trebuie
să conţină rânduri duplicate. De asemenea, se pot impune constrângeri asupra combinaţiilor de valori ale
atributelor tuplurilor. Nu trebuie să existe tupluri distincte în care să existe aceleaşi combinaţii de valori ale
anumitor atribute.

Constrângerile de domeniu
La crearea tabelei ”abonati” s-au specificat constrângeri de domeniu prin specificarea ”not null” după
declararea tuturor coloanelor. ”Null” este o valoare generală, asociată tuturor tipurilor de date dintr-o bază de
date care modelează lipsa unei valori a câmpului respectiv.
Mai pot fi impuse constrângeri care să asigure că modelarea realităţii este cât mai fidelă. De exemplu,
pentru tabela abonaţi, se observă că există un câmp care modelează cnp-ul. În realitate, cnp-ul este format din
13 caractere. Se poate impune o constrângere de tip ”check” care să se asigure că orice valoare introdusă
pentru coloana ”cnp” este formată din strict 13 caractere.

alter table abonati add constraint ck_cnp check(length(cnp)=13)

12
Se va încerca, apoi, introducerea unui tuplu care are lungimea valorii atributului ”cnp” diferită de 13
caractere.

insert into abonati( nume, prenume, telefon, cnp) values ( 'Ilie', 'Ioana',
'0700123456', 265012125634)

Se observă că inserarea eşuează cu eroarea afişată mai sus. Se specifică încălcarea constrângerii impuse
asupra coloanei ”cnp”.

Cheile
O supercheie este o submulţime de atribute ale unei relaţii care are proprietatea că orice combinaţie a
valorilor atributelor este unică în relaţia respectivă. Cu alte cuvinte, o combinaţie a valorilor atributelor
respective identifică în mod unic un tuplu.
În exemplul considerat mai sus, se poate spune că o combinaţie a atributelor ”nume”, ”prenume”, ”cnp”
identifică unic un abonat. Se poate defini în acest sens o constrângere de unicitate pe tabelă.

alter table abonati add constraint unique_nume_prenume_cnp unique(nume,


prenume, cnp)

O cheie candidată este o supercheie ireductibilă (nu există o submulţime a atributelor supercheii care să
aibă proprietatea că identifică în mod unic un tuplu). Această cheie poate fi formată din unul sau mai multe
atribute şi condiţia de unicitate reprezintă o constrângere de integritate impusă asupra modelului respectiv.
În acelaţi exemplu, se constată că ”cnp” constituie o supercheie ireductibilă (cnp-ul este unic pentru
orice persoană şi, întrucât supercheia este formată dintr-un singur atribut, aceasta este ireductibilă).

alter table abonati add constraint unique_cnp unique(cnp)

13
În cazul când se identifică mai multe chei candidate, una dintre acestea este desemnată cheie primară,
celelalte fiind chei secundare (unice). Cheii primare i se atribuie rolul de a identifica în mod unic un tuplu şi i
se impune ca niciunul dintre atributele sale să nu accepte valori NULL. Această cheie se poate alege din toate
cheile candidate, dar se preferă cea cu cele mai puţine atribute, din raţiuni de eficienţă.

Chei primare
Cheile primare pot fi naturale (formate din unul sau mai multe atribute existente în relaţie) sau artificiale
(construite special pentru identificarea unică a tuplurilor; de obicei, cheile artificiale sunt formate dintr-un
singur atribut al relaţiei).
În relaţia ”abonati”, se observă că atributul ”cnp” poate fi o cheie primară naturală deoarece atributul
există deja în relaţie. Pe de altă parte, atributul ”id” poate fi o cheie primară artificială.
Se alege coloana ”id” şi se defineşte constrângerea de cheie primară pe tabelă.

alter table abonati add constraint pk_id_abonat primary key(id)

Dacă s-ar dori să se insereze încă un tuplu cu valoarea ”1” a atributului ”id”, se observă că acest lucru a
devenit imposibil prin specificarea constrângerii de cheie primară. În acest caz, baza de date nu va permite
inserarea tuplului şi va genera o eroare, specificând numele constrângerii care a fost încălcate.

insert into abonati(id, nume, prenume, telefon, cnp) values (1, 'Vasile',
'Vasile', '0700123656', 1900331256314)

14
Este eficient ca utilizatorul să nu fie nevoit să ştie de existenţa câmpului id şi, mai ales, să nu fie nevoit
să ştie ce valoare trebuie să specifice în instrucţiunile de tip insert. De aceea, se poate implementa un sistem în
care valorile cheii primare, în acest caz este vorba despre valorile câmpului id, să se incrementeze automat.
Pentru aceasta, este necesar să se definească o secvenţă de numere pentru care se specifică valoarea de
început, incrementul şi, eventual, valoarea maximă. După definirea secvenţei, se creează un trigger pe tabelă
înainte de inserare. Acesta selectează din secvenţa creată următoarea valoare pe care o va folosi pentru
valoarea cheii primare a noului tuplu introdus prin insert. Astfel, înainte de inserarea oricărei linii noi în
tabelă, se execută triggerul. Acesta selectează următoarea valoare a secvenţei şi o foloseşte pentru completarea
câmpului id a tuplului care se introduce.
O secvenţă este un obiect al bazei de date care memorează o secvenţă de numere. Acest lucru este util
atunci când se doreşte definirea unei chei primare şi setarea proprietăţii de autoincrement a coloanei
respective.
Un trigger este o procedură stocată specială în sensul că este declanşată (executată) automat în urma
unei alte acţiuni ce a fost făcută asupra tabelei (de exemplu, după inserarea unui tuplu, înainte de inserarea
unui tuplu sau înainte/după executarea unui update, se poate dori executarea automată a unei instrucţiuni
suplimentares).
În urma definirii secvenţei şi a trigger-ului, nu mai este necesar să se specifice valori pentru id atunci
când se face o operaţie de insert în tabela respectivă.

Crearea secvenţei se face cu :


create sequence seq_id_abonat start with 4 increment by 1 nomaxvalue;

Crearea triggerului se face cu :


create or replace trigger trig_bi_abonat
before insert on ABONATI
for each row
begin

15
select seq_id_abonat.nextval into :new.id from dual;
end;

Inserarea unui tuplu fără a specifica o valoare pentru coloana ”id” :


insert into abonati( nume, prenume, telefon, cnp) values ( 'Ilie', 'Ioana',
'0700123456', 2650121256314);

Pentru a verifica faptul că triggerul s-a executat şi tuplul a fost completat automat cu o valoare pentru
”id”, se execută din nou select * from abonati.

Sistemele de gestiune a bazelor de date nu permit inserarea unor tupluri care au valori identice ale cheii
primare şi cheilor secundare.

16
Chei străine
O cheie străină reprezintă o constrângere referenţială care asigură modelarea corectă a asocierilor 1:N
între două sau mai multe relaţii. Considerând două relaţii, R1 şi R2, se numeşte cheie străină o submulţime de
atribute ale lui R2 care referă R1 respectând două condiţii :
 Atributele cheii străine sunt definite pe domenii compatibile cu cele ale atributelor unei chei
candidate din R1;
 Valorile atributelor cheii străine într-un tuplu din relaţia R2 sunt identice cu valorile cheii
atributelor cheii candidate din R1 ale unui tuplu din R1 sau iau valoarea NULL (aceasta este
proprietatea de integritate referenţială).
Pentru a putea exemplifica crearea unei chei străine, este necesar să se mai creeze cel puţin încă o tabelă.
Se va crea o tabelă care să modeleze tipul de abonament (se va folosi un model simplificat,
specificându-se doar o coloană pentru preţul în euro, una pentru descrierea abonamentului şi una pentru a
defini o cheie primară artificială).

create table tip_abonament


(id int,
pret_euro number(10),
servicii_incluse varchar(500),
constraint pk_id_abonament primary key(id)
)

La fel ca şi pentru tabela ”abonati”, se vor defini o secvenţă şi un trigger înainte de inserare şi pentru
tabela ”tip_abonament”, pentru a simula autoincrementarea cheii primare.

create sequence seq_id_tip_abonament start with 1 increment by 1 nomaxvalue;

create or replace trigger trig_bi_tip_abonament


before insert on tip_abonament
for each row
begin
select seq_id_tip_abonament.nextval into :new.id from dual;
end;

17
Se populează şi această tabelă cu câteva tupluri, pentru a putea ilustra mai târziu utilizarea cheilor
străine.
insert all
into tip_abonament(pret_euro, servicii_incluse) values(5, '500 sms, 500 min
retea')
into tip_abonament(pret_euro, servicii_incluse) values(7, '700 sms, 700 min
retea')
into tip_abonament(pret_euro, servicii_incluse) values(11, '1000 sms, 1000
min retea')
select 1 from dual;

Se execută o instrucţiune select * from tip_abonament pentru a verifica inserarea corectă a


tuplurilor în tabelă.

18
Se va adăuga o coloană în tabela ”abonati” care să refere coloana ”id” din tabela ”tip_abonament”. În
acest fel, nu este necesar să se ţină toate informaţiile despre tipul de abonament al fiecărui abonat în tabela
”abonat”, ci doar un număr care va duce la tipul de abonament din tabela ”tip_abonament”. Această abordare
va elimina informaţia redundantă din baza de date (un operator are un număr relativ restrâns de tipuri de
abonamente, deci aceleaşi informaţii legate de tipul de abonament s-ar regăsi la mai mulţi clienţi şi ar genera
informaţii repetate în coloanele dedicate tipului de abonament, dacă aceasta s-ar modela în tabela ”abonati”).
Pentru adăugarea coloanei ”id_tip_abonament” s-a folosit codul SQL de mai jos:

alter table abonati add id_tip_abonament int;

Se doreşte definirea cheii străine în tabela ”abonati” care să refere coloana ”id” din tabela
”tip_abonament”. Pentru aceasta este necesar ca toate valorile din coloana ”id_tip_abonament” din tabela
”abonati” să conţină strict valori regăsite în coloana ”id” din tabela ”tip_abonament” la care se va face
referinţă sau valori NULL.
alter table abonati add constraint fk_id_tip_abonament foreign
key(id_tip_abonament)
references tip_abonament(id);

19
La inserarea unui nou tuplu (sau la execuţia unui update asupra valorii coloanei ”id_tip_abonament” a
unui tuplu) în tabela ”abonati”, se va verifica dacă valoarea furnizată pentru ”id_tip_abonament” este NULL
sau dacă se regăseşte printre valorile coloanei ”id” din tabela ”tip_abonament”. În cazul în care valoarea
furnizată nu este NULL şi nici nu este regăsită în valorile existente în coloana ”id” din tabela
”tip_abonament”, se va genera o eroare şi operaţia de inserare va eşua.

insert into abonati( nume, prenume, telefon, cnp, id_tip_abonament) values (


'Scooby', 'Doo', '0705623456', 1650113256314, 5);

În cazul în care valoarea corespunzătoare coloanei ”id_tip_abonament” nu este specificată explicit în


instrucţiunea insert, se va completa automat cu NULL.

insert into abonati( nume, prenume, telefon, cnp) values ( 'Scooby', 'Doo',
'0705623456', 1650113256314);

Pentru a verifica acest lucru, se execută o nouă instrucţiune de tip select * from abonati.

20
Se poate modifica tipul abonamentului unui abonat prin specificarea unei valori pentru coloana
”id_tip_abonament” din tuplul corespunzător. Această operaţie se poate face cu o instrucţiune de tip update ca
mai jos.

update abonati set id_tip_abonament=3 where id=8;

Această operaţie setează tipul de abonament cu id-ul 3 pentru abonatul cu id 8. Operaţia reuşeşte pentru
că 3 este o valoare care se regăseşte în coloana ”id” din tabela ”tip_abonament”. Se poate executa un nou
select pentru a vizualiza conţinutul actual al tabelei ”abonati”.

Sub această formă strict numerică, nu se observă adevărata valoare a cheilor străine. Însă, se poate dori
extragerea de informaţii legate de tipul abonamentului unui abonat, cunoscând doar id-ul acestuia. În acest
caz, se poate executa o instrucţiune de tip join pentru a returna informaţiile legate de abonatul respectiv. O
astfel de instrucţiune poate avea următoarea formă:
select ABONATI.nume, ABONATI.prenume, ABONATI.telefon, ABONATI.cnp,
tip_abonament.pret_euro
from abonati join tip_abonament
on abonati.id_tip_abonament=tip_abonament.id
where abonati.id=8;

Rezultatul apare sub forma de mai jos.

21
Pentru a menţine integritatea referenţială, se impun anumite restricţii pentru operaţiile de modificare a
relaţiilor prin inserare sau ştergere.
De exemplu, operaţia de inserare a unui tuplu nou se face nerestricţionat în relaţia referită, însă în cea
care referă, trebuie să se verifice că valorile din cheia străină se regăsesc într-un tuplu din relaţia referită (aşa
cum s-a văzut mai sus). Operaţia de ştergere, în schimb, se face fără restricţii în relaţia care referă, pe când în
relaţia referită se poate face doar restricţionat sau în cascadă.
Ştergerea restricţionată nu permite ştergerea unui tuplu din relaţia referită dacă acesta este referit de un
tuplu din relaţia care conţine cheia străină.
Ştergerea în cascadă şterge odată cu tuplul din relaţia referită şi tuplurile din relaţia referită care îl
refereau pe cel şters.
Mai există şi posibilitatea ca, la ştergerea unui tuplu din relaţia referită (numită, de asemenea, relaţie
părinte) să se seteze ca NULL toate valorile coloanei din relaţia care face referire care refereau exact acel
tuplu şters din relaţia referită. Pentru aceasta, este nevoie să se şteargă constrângerea de cheie străină şi să se
redefinească, specificându-se opţiunea să se seteze valorile ca NULL la ştergerea unui tuplu din tabela
referită.
Constrângerea se şterge cu o instrucţiune de tipul :

alter table abonati drop constraint fk_id_tip_abonament;

Apoi, se recrează constrângerea, astfel :


alter table ABONATI add constraint fk_id_tip_abonament foreign
key(id_tip_abonament)
references tip_abonament(id) on delete set null;

22
Pentru a ilustra efectul, se va şterge din tabela ”tip_abonament” tuplul cu id-ul de valoare 1.

delete from tip_abonament where id=1;

Se va urmări efectul pe care această operaţie l-a produs în tabela care referă, adică ”abonati”. Înainte de
a executa operaţia de ştergere, s-au setat tipurile de abonament astfel : id_tip_abonament=1 pentru id=1 şi
id=3 din tabela ”abonati” şi id_tip_abonament=2 pentru id=2 şi id=4.

update abonati set id_tip_abonament=1 where id=1 or id=3;


update abonati set id_tip_abonament=2 where id=2 or id=4;

În continuare, se va prezenta ca exerciţiu pentru crearea şi administrarea tabelelor, modelarea unei baze
de date pentru un operator de telecomunicaţii. Modelul este simplificat, având scop pur didactic în crearea şi
administrarea tabelelor.

23
Exerciţiu : Să se modeleze structura unei baze de date care ar putea servi pentru stocarea datelor unui
operator de telecomunicaţii. Structura trebuie să asigure modelarea informaţiilor despre abonaţi, despre tipul
de abonament, despre facturi şi convorbiri. Pentru a asigura legăturile între date, se vor folosi chei primare şi
chei străine. Trebuie ca baza de date să permită extragerea de informaţii privind tipul de abonament al unui
client sau legate de ultima factură emisă. De asemenea, trebuie să se poată lista convorbirile făcute de un
anumit abonat, în cazul în care acesta solicită factura detaliată.
Să se populeze tabelele create şi să se execute câteva interogări pentru exemplificarea utilizării cheilor
străine şi primare.

Rezolvare :
1) Modelarea informaţiilor despre clienţi se va face într-o tabelă „clienti” care va stoca detaliile
despre numele, prenumele, adresa şi cnp-ul clientului respectiv. Se va adăuga o coloană suplimentară
care va fi folosită drept cheie primară artificială şi o coloană care să indice dacă clientul respectiv mai
are sau nu un abonament activ.

create table clienti(id int not null,


nume varchar(50) not null,
prenume varchar(50) not null,
adresa varchar(100) not null,
cnp number(13) not null,
activ smallint not null
);

Câmpul „id” este de tip întreg şi va fi folosit ca cheie primară. Coloana „activ” va indica dacă
respectivul client mai are sau nu un abonament activ (va lua valoarea 1 în cazul în care clientul are măcar un
abonament activ şi 0 în caz contrar). Numele, prenumele şi adresa sunt de tip varchar deoarece lungimea
acestor atribute diferă pentru fiecare client în parte şi, în acest fel, spaţiul ocupat în mediul de stocare va fi cel
strict necesar fiecărui tuplu în parte. Coloana „cnp” s-a definit ca un întreg de 13 cifre.

24
În continuare, se va impune constrângerea de cheie primară asupra coloanei „id”.

alter table clienti


add constraint pk_id_clienti
primary key(id);

Apoi, se va impune constrângerea de unicitate pe coloana „cnp” deoarece nu există două persoane cu
acelaşi cnp.

alter table clienti


add constraint unique_cnp
unique(cnp);

Se va stabili valoarea 1 implicită pentru coloana „activ” (se va considera că, la inserarea unui rând nou
în tabelă, aceasta va corespunde unui nou client care tocmai îşi face un abonament nou).

alter table clienti


modify activ default 1;

25
Pentru a evita necesitatea de a şti ce id trebuie specificat când se inserează un nou tuplu, se vor defini o
secvenţă şi un trigger la inserare. Astfel, secvenţa va putea returna următorul număr şi acesta va fi folosit în
trigger pentru a completa coloana „id” a tuplului respectiv.
Crearea secvenţei se face după cum urmează :
create sequence seq_id_client start with 1 increment by 1 nomaxvalue;

Se observă că secvenţa a fost denumită pentru a se putea identifica uşor faptul că este folosită pentru id-
ul clientului. În continuare, trebuie să se definească triggerul pe tabela „clienti”. Acesta se va executa înaintea
fiecărei operaţii de tip inserare şi va completa câmpul „id” cu valoarea următoare a secvenţei de numere
creată.

create or replace trigger trig_bi_id_client


before insert on clienti
for each row
begin
select seq_id_client.nextval into :new.id from dual;
end;

26
Câmpul „cnp” este de tip întreg cu maxim 13 cifre, dar acest lucru nu garantează că vor fi introduse doar
valori pozitive şi care să aibă strict 13 cifre. Pentru a asigura corectitudinea datelor introduse în acest câmp, se
va impune o constrângere de tip „check”. Aceasta nu va permite inserarea sau modificarea unui câmp dintr-un
tuplu dacă noua valoare nu respectă condiţiile impuse.

alter table clienti


add constraint check_cnp
check(length(cnp) = 13 and cnp>0);

2) Modelarea informaţiilor despre tipurile de abonament se va face printr-o tabelă,


„tip_abonament”. Aceasta va avea coloane prin care se va specifica numărul de minute în reţea/în afara
reţelei, numărul de sms-uri în reţea/în afara reţelei care sunt incluse în abonament. Va exista şi o coloană
care va specifica dacă abonamentul respectiv beneficiază şi de servicii de Internet sau nu şi, bineînţeles,
o coloană care să conţină preţul lunar al abonamentului de tipul respectiv. Vor exista şi coloane care să
specifice tarifele pentru convorbiri şi mesaje în reţea şi în afara reţelei.
create table tip_abonament(id int not null,
min_retea int,
min_extra int,
sms_retea int,
sms_extra int,

27
serviciu_internet smallint,
pret_lunar number(10, 2),
pret_min_retea number(4, 2),
pret_min_extra number(4, 2),
pret_sms_retea number(4, 2),
pret_sms_extra number(4, 2)
);

În continuare, este necesar să se definească o constrângere de tip cheie primară pe această tabelă.

alter table tip_abonament


add constraint pk_id_tip_abonament
primary key(id);

La fel ca şi pentru tabela de clienţi, se va crea o secvenţă şi un trigger pentru a emula comportamentul
„autoincrement” pe coloana „id”.
Secvenţa se creează prin :

create sequence seq_id_tip_abonament start with 1 increment by 1 nomaxvalue;


28
Se defineşte, apoi, şi triggerul.

create or replace trigger trig_bi_id_tip_abonament


before insert on tip_abonament
for each row
begin
select seq_id_tip_abonament.nextval into :new.id from dual;
end;

3) Modelarea abonamentelor se va face printr-o tabelă numită „abonamente”. Aceasta va conţine


informaţii privind data de început şi de încheiere a abonamentului, numărul de telefon pe care s-a făcut
abonamentul respectiv. De asemenea, este necesar să se cunoască ce tip de abonament este fiecare şi, deci,
este necesară definirea unei chei străine care să refere id-ul din tabela „tip_abonament”. O altă coloană care se
va defini şi care va avea nevoie să fie cheie străină este una care să indice id-ul clientului care are
abonamentul respectiv (un client poate avea mai multe abonamente). Şi această tabelă va avea o cheie primară
artificială.

create table abonamente(id int not null,


id_client int not null,
id_tip_abonament int not null,
data_start date not null,
data_stop date,
nr_tel varchar(20) not null
29
);

Se va defini cheia primară astfel :

alter table abonamente


add constraint pk_id_abonament primary key(id);

Se va defini o secvenţă pentru a fi folosită la inserarea unui nou tuplu în tabelă fără a specifica o valoare
pentru câmpul „id”.

create sequence seq_id_abonament start with 1 increment by 1 nomaxvalue;

Se defineşte şi triggerul corespunzător astfel :

create or replace trigger trig_bi_id_abonament


30
before insert on abonamente
for each row
begin
select seq_id_abonament.nextval into :new.id from dual;
end;

În continuare, se vor defini şi constrângerile de tip chei străine specificate anterior.


Pentru început, se va defini cheia străină pe coloana „id_client”.

alter table abonamente


add constraint fk_id_client
foreign key(id_client)
references clienti(id)
on delete cascade;

În continuare, se defineşte cheia străină pe coloana „id_tip_abonament”.

alter table abonamente


add constraint fk_id_tip_abonament
foreign key(id_tip_abonament)
references tip_abonament(id)

31
on delete cascade;

4) Modelarea facturilor se va face cu o tabelă „facturi”. Aceasta va conţine un identificator unic al


facturii (id – cheie primară artificială), numărul facturii, data emiterii şi data scadentă, suma de plată, o
coloană folosită pentru a indica dacă o anumită factură este achitată sau nu. De asemenea, este nevoie să se
cunoască abonamentul pentru care s-a emis factura respectivă. Din acest motiv, se va creea o coloană cu
constrângerea de cheie străină, care va referi coloana „id” din tabela „abonament”.
De această dată, se vor specifica cele două constrângeri (cheie primară şi cheie străină) în codul sql
pentru crearea tabelei. De asemenea, se va seta valoarea implicită pentru coloana „achitat” ca fiind 0 (adică
neachitat).
create table facturi(id int not null,
id_abonament int not null,
nr_factura number(20) not null,
suma number(10, 2) not null,
data_emiterii date,
data_scadenta date,
achitat smallint default 0,
constraint pk_id primary key(id),
constraint fk_id_abonament foreign key(id_abonament)
references abonamente(id)
);

32
Mai este nevoie doar de crearea secvenţei şi a triggerului pentru coloana „id”.

create sequence seq_id_factura start with 1 increment by 1 nomaxvalue;

create or replace trigger trig_bi_id_factura


before insert on facturi
for each row
begin
select seq_id_factura.nextval into :new.id from dual;
end;

5) Modelarea tipului de serviciu se va face cu o tabelă, „tip_serviciu”. Aceasta va avea doar două
coloane : id şi nume.

create table tip_serviciu(id int not null,


nume varchar(50) not null,
constraint pk_id_tip_serviciu primary key(id)
);

33
Se vor defini, şi în acest caz, secvenţa şi triggerul folosite pentru cheia primară.

create sequence seq_id_tip_serviciu start with 1 increment by 1 nomaxvalue;

create or replace trigger trig_bi_id_tip_serviciu


before insert on tip_serviciu
for each row
begin
select seq_id_tip_serviciu.nextval into :new.id from dual;
end;

6) Modelarea convorbirilor şi serviciilor folosite se va face cu o tabelă, „servicii_folosite”, care va


stoca informaţii despre abonamentul de pe care s-au folosit serviciile respective, numărul de telefon destinaţie
34
(dacă este cazul), momentul de timp în care a fost iniţiată folosirea serviciului respectiv, unităţile consumate
(pot fi secunde pentru apeluri, număr de sms pentru mesaje etc) şi un identificator al tipului de serviciu (va fi o
cheie străină făcând referinţă către câmpul „id” din tabela „tip_serviciu”).
create table servicii_folosite(id int not null,
id_abonament int not null,
nr_tel_dest varchar(20) default null,
data_fol date,
unitati number(20),
id_tip_serviciu int not null,
constraint pk_id_servicii_fol primary
key(id),
constraint fk_servicii_id_abonament foreignv
key(id_abonament) references abonamente(id),
constraint fk_id_tip_serviciu foreign
key(id_tip_serviciu) references tip_serviciu(id));

Şi pentru această tabelă se va emula comportamentul „autoincrement” prin crearea unei secvenţe şi a
unui trigger.

create sequence seq_id_serviciu_folosit start with 1 increment by 1


nomaxvalue;

35
create or replace trigger trig_bi_id_serviciu_folosit
before insert on servicii_folosite
for each row
begin
select seq_id_serviciu_folosit.nextval into :new.id from dual;
end;

7) Popularea bazei de date se va face cu instrucţiuni de tip insert. Se va popula fiecare dintre cele
şase tabele create cu câteva tupluri, suficient pentru a putea exemplifica interogări pe baza de date.

Popularea tabelei „clienti” se poate face cu o instrucţiune insert all, cum este cea de mai jos.

insert all
into clienti(nume, prenume, adresa, cnp, activ) values ('Wilma',
'Flintstone', 'Bedrock', 2461231256314, 1)
into clienti(nume, prenume, adresa, cnp, activ) values ('Fred',
'Flintstone', 'Bedrock', 1451231256395, 1)
into clienti(nume, prenume, adresa, cnp, activ) values ('Barney', 'Rubble',
'Bedrock', 1470231953314, 1)
into clienti(nume, prenume, adresa, cnp, activ) values ('Betty', 'Rubble',
'Bedrock', 2480131445514, 1)
into clienti(nume, prenume, adresa, cnp, activ) values ('Scooby', 'Doo',
'Mystery Machine', 1660215265341, 1)
select 1 from dual;

36
Popularea tabelei „tip_abonament” se poate face folosind :

insert all
into TIP_ABONAMENT(min_retea, min_extra, sms_retea, sms_extra,
serviciu_internet, pret_lunar, pret_min_retea, pret_min_extra,
pret_sms_retea, pret_sms_extra)
values(20, 20, 100, 100, 0, 3, 0.07, 0.12, 0.03, 0.05)
into TIP_ABONAMENT(min_retea, min_extra, sms_retea, sms_extra,
serviciu_internet, pret_lunar, pret_min_retea, pret_min_extra,
pret_sms_retea, pret_sms_extra)
values(50, 50, 250, 250, 0, 5, 0.07, 0.12, 0.03, 0.05)
into TIP_ABONAMENT(min_retea, min_extra, sms_retea, sms_extra,
serviciu_internet, pret_lunar, pret_min_retea, pret_min_extra,
pret_sms_retea, pret_sms_extra)
values(100, 100, 300, 300, 0, 7, 0.07, 0.12, 0.03, 0.05)
into TIP_ABONAMENT(min_retea, min_extra, sms_retea, sms_extra,
serviciu_internet, pret_lunar, pret_min_retea, pret_min_extra,
pret_sms_retea, pret_sms_extra)
values(250, 250, 400, 400, 0, 9, 0.07, 0.12, 0.03, 0.05)
into TIP_ABONAMENT(min_retea, min_extra, sms_retea, sms_extra,
serviciu_internet, pret_lunar, pret_min_retea, pret_min_extra,
pret_sms_retea, pret_sms_extra)
values(500, 500, 500, 500, 1, 10.5, 0.07, 0.12, 0.03, 0.05)
into TIP_ABONAMENT(min_retea, min_extra, sms_retea, sms_extra,
serviciu_internet, pret_lunar, pret_min_retea, pret_min_extra,
pret_sms_retea, pret_sms_extra)
values(1000, 1000, 750, 750, 0, 12, 0.07, 0.12, 0.03, 0.05)
select 1 from dual;

37
Popularea tabelei „abonamente” se poate face prin :

insert all
into ABONAMENTE(id_client, id_tip_abonament, data_start, data_stop,
nr_tel)
values(1, 3, to_date('10/12/1999', 'dd/mm/yyyy'), null, 0723845612)
into ABONAMENTE(id_client, id_tip_abonament, data_start, data_stop,
nr_tel)
values(2, 1, to_date('05/03/2005', 'dd/mm/yyyy'), to_date('05/03/2011',
'dd/mm/yyyy'), 0725621612)
into ABONAMENTE(id_client, id_tip_abonament, data_start, data_stop,
nr_tel)
values(3, 6, to_date('15/11/2009', 'dd/mm/yyyy'), to_date('15/11/2011',
'dd/mm/yyyy'), 07238586321)
into ABONAMENTE(id_client, id_tip_abonament, data_start, data_stop,
nr_tel)
values(4, 5, to_date('07/02/2009', 'dd/mm/yyyy'), null, 0725625612)
into ABONAMENTE(id_client, id_tip_abonament, data_start, data_stop,
nr_tel)
values(5, 2, to_date('25/04/2007', 'dd/mm/yyyy'), null, 0786545612)
into ABONAMENTE(id_client, id_tip_abonament, data_start, data_stop,
nr_tel)
values(3, 4, to_date('01/09/2000', 'dd/mm/yyyy'), null, 0723885412)
select 1 from dual;

Popularea tabelei „facturi” se poate face folosind o instrucţiune de tipul celei de mai jos :

38
insert all
into FACTURI(id_abonament, nr_factura, suma, data_emiterii,
data_scadenta, achitat)
values(7, 1, 7.05, to_date('01/12/2010', 'dd/mm/yyyy'),
to_date('31/12/2010', 'dd/mm/yyyy'), 0)
into FACTURI(id_abonament, nr_factura, suma, data_emiterii,
data_scadenta, achitat)
values(8, 2, 3, to_date('01/12/2010', 'dd/mm/yyyy'), to_date('31/12/2010',
'dd/mm/yyyy'), 0)
into FACTURI(id_abonament, nr_factura, suma, data_emiterii,
data_scadenta, achitat)
values(9, 3, 12, to_date('01/12/2010', 'dd/mm/yyyy'), to_date('31/12/2010',
'dd/mm/yyyy'), 0)
into FACTURI(id_abonament, nr_factura, suma, data_emiterii,
data_scadenta, achitat)
values(10, 4, 11, to_date('01/12/2010', 'dd/mm/yyyy'), to_date('31/12/2010',
'dd/mm/yyyy'), 0)
into FACTURI(id_abonament, nr_factura, suma, data_emiterii,
data_scadenta, achitat)
values(11, 5, 5.75, to_date('01/12/2010', 'dd/mm/yyyy'),
to_date('31/12/2010', 'dd/mm/yyyy'), 0)
into FACTURI(id_abonament, nr_factura, suma, data_emiterii,
data_scadenta, achitat)
values(12, 6, 9.25, to_date('01/12/2010', 'dd/mm/yyyy'),
to_date('31/12/2010', 'dd/mm/yyyy'), 0)
select 1 from dual;

Popularea tabelei „tip_serviciu” se poate face folosind o instrucţiune de tipul :


39
insert all
into TIP_SERVICIU(nume) values('convorbire_retea')
into TIP_SERVICIU(nume) values('convorbire_extra')
into TIP_SERVICIU(nume) values('sms_retea')
into TIP_SERVICIU(nume) values('sms_extra')
select 1 from dual;

insert all
into SERVICII_FOLOSITE(id_abonament, nr_tel_dest, data_fol, unitati,
id_tip_serviciu)
values(7, '0726512684', to_date('12/11/2010', 'dd/mm/yyyy'), 3, 3)
into SERVICII_FOLOSITE(id_abonament, nr_tel_dest, data_fol, unitati,
id_tip_serviciu)
values(8, '0214562123', to_date('02/06/2010', 'dd/mm/yyyy'), 2, 1)
into SERVICII_FOLOSITE(id_abonament, nr_tel_dest, data_fol, unitati,
id_tip_serviciu)
values(9, '0721654789', to_date('12/11/2010', 'dd/mm/yyyy'), 3, 3)
select 1 from dual;

8) Extragerea de informaţii pentru facturare se poate face astfel încât să conţină date despre
numele, prenumele, adresa clientului cu abonamentul respectiv, suma de plată, numărul facturii, data emiterii
40
şi data scadentă a facturii. Aceste informaţii pot fi folosite pentru a printa facturile, de exemplu. Rezulatele pot
fi ordonate după numeroase criterii, dar, pentru exemplificare, se vor ordona după numele şi prenumele
clienţilor.

select CLIENTI.nume, CLIENTI.prenume, CLIENTI.adresa, FACTURI.nr_factura,


FACTURI.suma, FACTURI.data_emiterii, FACTURI.data_scadenta
from CLIENTI
inner join ABONAMENTE on ABONAMENTE.id_client=CLIENTI.id
inner join FACTURI on ABONAMENTE.id=FACTURI.id_abonament
order by CLIENTI.nume, CLIENTI.prenume;

9) Extragerea de informaţii legate de serviciile folosite se poate face pentru a include date despre
numele, prenumele şi adresa clientului care a folosit serviciile, numărul de telefon destinaţie, data în care s-a
folosit un anumit serviciu, unităţile folosite, numele serviciului folosit.

select CLIENTI.nume, CLIENTI.prenume, CLIENTI.adresa,


SERVICII_FOLOSITE.nr_tel_dest, SERVICII_FOLOSITE.data_fol,
SERVICII_FOLOSITE.unitati, TIP_SERVICIU.nume
from CLIENTI
join ABONAMENTE on ABONAMENTE.id_client=CLIENTI.id
join SERVICII_FOLOSITE on ABONAMENTE.id=SERVICII_FOLOSITE.id_abonament
join TIP_SERVICIU on SERVICII_FOLOSITE.id_tip_serviciu=TIP_SERVICIU.id
order by CLIENTI.nume, CLIENTI.prenume;

41
Baza de date rezultată are o schemă de forma de mai jos.

42
Bibliografie
1. Teach_Yourself_SQL_in_21_Days_Second_Edition.pdf
2. Oracle® Database SQL Language Reference 10g Release 2 (10.2) (
www.oracle.com/pls/db102/homepage )
3. Oracle® Database PL/SQL User's Guide and Reference 10g Release 2 (10.2)
(www.oracle.com/pls/db102/homepage)
4. cursuri de baze de date

43

S-ar putea să vă placă și