Unitatea de studiu 6
Crearea unei baze de date relaionale, inclusiv a restriciilor folosind comenzi i clauze SQL:
CREATE TABLE, ALTER TABLE, DROP TABLE.
Cunoaterea comenzilor pentru editarea unei tabele; comenzile INSERT, UPDATE, DELETE
Cunoaterea celor mai importani operatori i clauze ai interogrilor SQL: fraza SELECT,
operatorii de comparaie obinuii i cei specifici: BETWEEN, IN, IS NULL, LIKE.
Folosirea clauzelor GROUP BY i HAVING i a funciilor-agregat: COUNT, SUM, AVG,
MIN, MAX pentru calcularea unor parametri sintetici din baza de date.
129
130
131
Scop
Pentru manipularea datelor
Extragerea datelor din BD
Adugarea de noi linii ntr-o tabel
tergerea de linii dintr-o tabel
132
UPDATE
CREATE TABLE
DROP TABLE
ALTER TABLE
CREATE VIEW
DROP VIEW
GRANT
REVOKE
COMMIT
ROLLBACK
133
Pentru crearea unei tabele comanda SQL este, natural, CREATE TABLE prin care se declar
numele tabelei, numele, tipul i lungimea fiecrui atribut, precum i restriciile. Astfel, primele dou
tabele din baza de date, CODPOST_LOC i CLIENI pot fi create astfel:
CREATE TABLE codPost_loc (
CodPostal CHAR(6) CONSTRAINT pk_cp PRIMARY KEY,
Localitate CHAR (35) NOT NULL,
Judet CHAR(25) NOT NULL ) ;
CREATE TABLE clienti (
CodClient INTEGER CONSTRAINT pk_cp PRIMARY KEY,
NumeClient CHAR (30) NOT NULL CONSTRAINT un_numeclient UNIQUE,
Adresa CHAR(60),
CodPostal CHAR(6) NOT NULL CONSTRAINT ref_cl_codpost
REFERENCES codpost_loc (CodPostal) ) ;
Se pot observa cu uurin clauzele PRIMARY KEY folosite pentru declararea cheilor
primare, UNIQUE pentru cheile alternative, NOT NULL pentru interzicerea valorilor nule, precum i
REFERENCES pentru declararea cheii strine. n plus, opiunea CONSTRAINT ne ajut s
botezm fiecare restricie dup cum dorim.
Spre deosebire de alte SGBD-uri, n ACCESS comenzile nu pot fi introduse direct, ci incluse
n modul program. Iat modulul CREARETABELE():
Sub CreareTabele()
Dim dbs As Database
'Set dbs = OpenDatabase("Z:\Medii_2006_ID\BD\vinzari.mdb")
Set dbs = CurrentDb
dbs.Execute "CREATE TABLE codPost_loc " _
& "(CodPostal CHAR(6) CONSTRAINT pk_cp PRIMARY KEY, " _
& " Localitate CHAR (35) NOT NULL, " _
& " Judet CHAR(25) NOT NULL) ; "
dbs.Execute "CREATE TABLE clienti " _
& "(CodClient INTEGER CONSTRAINT pk_cp PRIMARY KEY, " _
& " NumeClient CHAR (30) NOT NULL CONSTRAINT un_numeclient UNIQUE, " _
& " Adresa CHAR(60), " _
& " CodPostal CHAR(6) NOT NULL CONSTRAINT ref_cl_codpost REFERENCES codpost_loc (CodPostal) ) ;"
dbs.Execute "CREATE TABLE facturi " _
& "(NrFact INTEGER CONSTRAINT pk_facturi PRIMARY KEY, " _
& " CodClient INTEGER NOT NULL CONSTRAINT ref_fact_cl REFERENCES clienti (CodClient), " _
& " Data DATETIME NOT NULL, " _
& " ValoareTotala NUMERIC NOT NULL, " _
& " TVAColectata NUMERIC NOT NULL) ;"
End Sub
Dei n capitolul 4 spuneam c structura unei baze de date este constant, exist situaii n care
trebuie s:
- adugm un atribut;
- eliminm un atribut;
- schimbm tipul unui atribut;
- modificm lungimea unui atribut;
- s declarm o nou restricie
- s anulm o restricie n vigoare.
Deoarece baza de date este n uz, nu ne putem permite s tergem i apoi s recrem tabelele,
pentru c aceasta echivaleaz cu pierderea iremediabil a nregistrrilor existente. Aa c este necesar
134
folosirea comenzii ALTER TABLE. Dac n tabela CLIENI se dorete pstrarea i a codului fiscal al
fiecrui furnizor, este necesar adugarea atributului CodFiscal, care este un ir de caractere (un numr
precedat de litera R, dac clientul respectiv este pltitor de TVA) de lungime 10 caractere. Comanda
utilizat este:
ALTER TABLE CLIENI ADD CodFiscal CHAR(10)
n SQL tergerea unei tabele din baza de date este realizabil cu ajutorul comenzii DROP
TABLE. Iat cum ar putea lansate comenzile de tergere ale celor trei tabele pe care abia le-am creat
acum jumtate de pagin:
Sub StergeTabele()
Dim dbs As Database
Set dbs = OpenDatabase("Z:\ Medii_2006_ID\BD\vinzari.mdb")
dbs.Execute "DROP TABLE facturi ;"
dbs.Execute "DROP TABLE clienti ;"
dbs.Execute "DROP TABLE codpost_loc ;"
End Sub
Am trecut n revist pn n acest moment principalele clauze ale comenzii CREATE TABLE
(i ALTER TABLE) pentru declararea cheilor primare, alternative i strine, i valorilor nenule.
Nemeritat, a fost omis clauza CHECK prin care putem defini restricii utilizator sub forma regulilor
de validare la nivel de atribut sau nregistrare. Astfel, n tabela CLIENI valorile atributului CodClient
trebuie s nceap de la 1001, iar numele clientului se dorete a fi scris cu majuscule ntotdeauna.
Aceste dou reguli de validare pot fi definite n SQL att n momentul crerii:
CREATE TABLE clienti (
CodClient INTEGER CONSTRAINT pk_cp PRIMARY KEY
CONSTRAINT ck_codclient CHECK (CocClient > 1000),
NumeClient CHAR (30) NOT NULL CONSTRAINT un_numeclient UNIQUE
CONSTRAINT ck_numeclient CHECK (NumeClient = UPPER(NumeClient)),
Adresa CHAR(60),
CodPostal CHAR(6) NOT NULL CONSTRAINT ref_cl_codpost
REFERENCES codpost_loc (CodPostal) ) ;
ct i ulterior prin ALTER TABLE. Din pcate, ACCESSul nu este prea ngduitor n aceast privin,
clauza CHECK fiind interzis. Singura modalitate de declarare a regulilor este cea procedural:
Sub Reguli_Atribute()
Dim dbs As Database, tdf As TableDef, fld As Field
Set dbs = CurrentDb
' CLIENTI.CodClient > 1000
Set tdf = dbs.TableDefs("clienti")
Set fld = tdf.Fields("CodClient")
fld.ValidationRule = "[CodClient] > 1000"
fld.ValidationText = "Cel mai mic cod de client acceptat este 1001 !"
' CLIENTI.NumeClient se scrie numai cu majuscule
Set tdf = dbs.TableDefs("clienti")
Set fld = tdf.Fields("NumeClient")
fld.ValidationRule = "StrComp(UCase([NumeClient]), [NumeClient], 0) = 0"
fld.ValidationText = "Literele din numele clientului sunt obligatoriu majuscule !"
' Prima litera din CLIENTI.Adresa este majuscula. Restul, la alegere !
Set tdf = dbs.TableDefs("clienti")
Set fld = tdf.Fields("Adresa")
fld.ValidationRule = "StrComp(LEFT(UCase([Adresa]),1), LEFT([Adresa],1), 0) = 0"
fld.ValidationText = "Prima litera din adresa clientului trebuie sa fie majuscula !"
'Data facturii
135
Despre reguli la nivel de nregistrare, ce s mai vorbim... Astfel, dac n tabela FACTURI s-ar
dori instituirea regulii dup care TVA-ul poate fi cel mult egal cu 0,19/1,19 din valoarea total a
fiecrei facturi (prietenii tiu de ce !), restricia ar putea fi definit la creare astfel:
CREATE TABLE facturi (
NrFact INTEGER CONSTRAINT pk_facturi PRIMARY KEY,
CodClient INTEGER NOT NULL CONSTRAINT ref_fact_cl REFERENCES clienti (CodClient),
Data DATE NOT NULL,
ValoareTotala NUMERIC NOT NULL,
TVAColectata NUMERIC NOT NULL),
CONSTRAINT ck_tva_valtot CHECK (TVAColectata <= ValoareTotala * 0.19 / 1.19) );
ACCESS-ul este, dup cum v imaginai, impasibil la graiile (de tip CHECK) ale SQL-ului,
aa c singura soluie e tot cea procedural:
Sub Regula_inregistrare()
Dim dbs As Database, tdf As TableDef
Set dbs = CurrentDb
Set tdf = dbs.TableDefs("facturi")
tdf.ValidationRule = "[TVAColectata] <= [ValoareTotala] * 0.19 / 1.19"
tdf.ValidationText = "TVA poate fi maxim 0,19/1,19 din valoarea totala a facturii !"
End Sub
6.3.1. Adugare
S presupunem c, la un moment dat, ntreprinderea vinde produse i firmei RODEX SRL
care are sediul pe strada Sapienei, nr.44 bis, n localitatea Iasi. Acest nou client trebuie "introdus" n
baza de date, operaiune care n SQL, se realizeaz prin comanda:
INSERT INTO clienti VALUES (1009, RODEX SRL, Sapienei, 44 bis, 706600)
Fraza INSERT de mai sus poate fi scris i sub forma:
INSERT INTO clienti (CodClient, NumeClient, Adresa, CodPostal)
VALUES (5009, RODEX SRL, Sapienei 44 bis, 706600)
Dup cum se observ, dup numele tabelei (CLIENI) au fost enumerate toate atributele
pentru care se introduc valori prin clauza VALUES. Dac nu s-ar fi cunoscut adresa clientului RODEX,
atunci fraza INSERT ar fi avut una din formele:
INSERT INTO clienti (CodClient, NumeClient, Adresa, CodPostal)
VALUES (5009, "RODEX SRL", NULL, 6600) sau
INSERT INTO clienti (CodClient, NumeClient, CodPostal) VALUES (5009, RODEX SRL, 6600)
n noua linie a tabelei CLIENI valoarea atributului Adresa va fi NULL.
136
Coninutul celor trei tabele din finalul capitolului 4 a fost realizat n ACCESS prin modulul
urmtor n care comanda INSERT este argumentul unei comenzi DoCmd.RunSQL:
Sub Inserturi()
' CODPOST_LOC
DoCmd.RunSQL ("INSERT INTO codPost_loc VALUES ('706600', 'Iasi', 'Iasi') ;")
DoCmd.RunSQL ("INSERT INTO codPost_loc VALUES ('706610', 'Iasi', 'Iasi') ;")
DoCmd.RunSQL ("INSERT INTO codPost_loc VALUES ('705300', 'Focsani', 'Vrancea') ;")
DoCmd.RunSQL ("INSERT INTO codPost_loc VALUES ('705725', 'Pascani', 'Iasi') ;")
DoCmd.RunSQL ("INSERT INTO codPost_loc VALUES ('706750', 'Tg.Frumos', 'Iasi') ;")
' CLIENTI
DoCmd.RunSQL ("INSERT INTO clienti VALUES (1001, 'TEXTILA SA', 'Bld. Copou, 87', '706600' );")
DoCmd.RunSQL ("INSERT INTO clienti VALUES (1002, 'MODERN SRL', 'Bld. Grii, 22', '705300' ) ;")
DoCmd.RunSQL ("INSERT INTO clienti VALUES (1003, 'OCCO SRL', NULL, '706610') ;")
DoCmd.RunSQL ("INSERT INTO clienti VALUES (1004, 'FILATURA SA', 'Bld. Unirii, 145', '705300' ) ;")
DoCmd.RunSQL ("INSERT INTO clienti VALUES (1005, 'INTEGRATA SA', 'I.V.Viteazu, 115', '705725' ) ;")
DoCmd.RunSQL ("INSERT INTO clienti VALUES (1006, 'AMI SRL', 'Galatiului, 72', '706750' );")
DoCmd.RunSQL ("INSERT INTO clienti VALUES (1007, 'AXON SRL', 'Silvestru, 2', '706610' ) ;")
DoCmd.RunSQL ("INSERT INTO clienti VALUES (1008, 'ALFA SRL', 'Prosperittii, 15', '705725' ) ;")
'FACTURI
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111111, 1003, #6-17-2005#, 17000, 0) ;")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111112, 1001, #6-17-2005#, 2850, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111113, 1004, #6-18-2005#, 5850, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111114, 1003, #6-18-2005#, 2850, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111115, 1008, #6-18-2005#, 35700, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111116, 1008, #6-19-2005#, 8700, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111117, 1006, #6-20-2005#, 1100, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111118, 1007, #6-23-2005#, 15000, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111119, 1005, #6-24-2005#, 4720, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111120, 1003, #6-24-2005#, 3000, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111121, 1001, #6-24-2005#, 4250, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111122, 1007, #6-24-2005#, 8750, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111123, 1006, #6-25-2005#, 66000, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111124, 1004, #6-25-2005#, 38600, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111125, 1003, #6-30-2005#, 1280, 0);")
DoCmd.RunSQL ("INSERT INTO facturi VALUES (111126, 1002, #6-01-2005#, 54250, 0);")
End Sub
Dup cum se observ, deliberat TVA colectat este declarat zero la toate liniile inserate n
FACTURI. Aceasta pentru a avea motiv de modificare (UPDATE) vezi paragraful pe peste linia
curent.
137
extrage doar partea ntreag dintr-un numr real (se elimin, deci, partea fracionar), iar prin
mprirea rezultatului la 100 ne asigurm c TVA va avea dou poziii la partea fracionar.
n ACCESS aceast actualizare se poate face n dou moduri. Grafic, putem creea o interogare
de tip pe calapodul clasic al construirii machetelor (prezentat n paragraful 5.4), dup aducerea
tabelei FACTURI n machet, selectnd din grupul Query Type a opiunii Update vezi partea
stng a figurii 6.3. Partea dreapt a figurii prezint noua form a machetei, n care apare linia Update
To i dispar Sort i Show. La rubrica Update To se introduce expresia de calcul a valorilor atributului
TVAColectat. Cea de-a dou modalitate este cea procedural, comanda prin care se lanseaz
UPDATE-rile fiind, ca i in cazul inserrii, DoCmd.RunSQL.
Sub Updateuri()
DoCmd.RunSQL ("UPDATE facturi SET TVAColectata = INT(ValoareTotala * 19 / 1.19) / 100 ;")
DoCmd.RunSQL ("UPDATE facturi SET TVAColectata = 0 WHERE NrFact = 111117 ;")
DoCmd.RunSQL ("UPDATE facturi SET TVAColectata = INT(ValoareTotala * 9 / 1.09) / 100 " & _
" WHERE NrFact IN (111118, 111122) ;")
End Sub
6.3.3. tergeri
Operaiunea de eliminarea a una sau mai multe linii dintr-o tabel, pe baza unui predicat, se
realizeaz n SQL prin comanda DELETE care are sintaxa: DELETE FROM nume-tabel WHERE predicat
Dac am dori s eliminm din tabela CLIENI linia aferent clientului MODERN SRL (cod
1002), comanda ar fi:
DELETE FROM clienti WHERE CodClient = 1002
138
n ACCESS, similar interogrilor pentru modificare pot fi create i interogri pentru tergere,
n care se specific criteriul pe care trebuie s-l satisfac liniile pentru a fi terse din tabela indicat
(fiind n criz de spaiu, nu vom mai prezenta nici o figur n acest scop). Prin program, dac am dori
eliminarea tuturor nregistrrilor din tabelele bazei, apoi re-adugarea i re-modificarea lor, ne-am
putea servi de blocul urmtor:
Sub Stergeri()
DoCmd.RunSQL ("DELETE FROM facturi ;")
DoCmd.RunSQL ("DELETE FROM clienti ;")
DoCmd.RunSQL ("DELETE FROM codPost_loc ;")
Call Inserturi
Call Updateuri
End Sub
139
INTEROG. Dup modelul indicat n stnga figurii 6.3 i vizualizm definiia folosind opiunea SQL
View, iar apoi n fereastra care apare nlocuim fraza SELECT cu cea care ne intereseaz (fraz care nu
are, probabil, nici o legtur cu definiia actual a interogrii), dup care se apas butonul Run
(semnul mirrii).
Iar dac vrem s facem acelai lucru prin program, folosim modulul urmtor:
Sub interogareBETWEEN()
Dim consultare As QueryDef
Set consultare = CurrentDb.QueryDefs("interog")
consultare.SQL = "SELECT * FROM facturi WHERE nrfact BETWEEN 111120 AND 111124 ; "
DoCmd.OpenQuery ("interog")
End Sub
Modulul interogareBETWEEN() declar (prin Dim) obiectul consultare ca fiind definiia unei
interogri (QueryDef), preia (prin comanda Set) n consultare fraza SELECT care constituie definiia
interogrii create anterior interog , modific definiia acestea prin linia consultare.SQL = SELECT
i, n final, execut noua variant a interogrii, rezultatul fiind similar variantei din figura 6.4.
140
MITA, MATSUSHITA etc.). Despre semnul "%" (sau *) se spune c este un specificator multiplu,
joker sau masc. Un alt specificator multiplu utilizat n multe versiuni SQL este liniua-de-subliniere
("_") sau semnul de ntrebare (?). Spre deosebire de "%", "_" substituie un singur caracter. Diferena
dintre cei doi specificatori multipli este pus n eviden n continuare. Astfel, dac intereseaz care
sunt clienii ai cror nume ncepe cu litera A i sunt societi cu rspundere limitat (SRL-uri), fraza
SELECT care furnizeaz rspunsul este:
SELECT * FROM CLIENI WHERE NumeClient LIKE "A__ SRL%"
(vezi partea stng a figurii 6.4). Dac s-ar fi utilizat simbolul "%" de maniera urmtoare:
SELECT * FROM CLIENI WHERE NumeClient LIKE "A%SRL%"
rezultatul ar fi fost cel din partea dreapt a figurii.
141
n continuare vrem s obinem denumirea fiecrei localiti i judeul n care se afl, dar liniile
rezultatului trebuie ordonate n funcie de jude i, n cadrul aceluiai jude, n ordinea invers a
localitii (de la Z la A), fraza SELECT se formuleaz dup cum urmeaz, rezultatul fiind prezentat n
figura 6.7.
6.5. JONCIUNI
Dup cum afirmam i n paragraful 5.4 majoritatea informaiilor obinute dintr-o baz de date
necesit rsfoirea simultan a dou sau mai multe tabele. Interogarea Fac_dupa20iunie2005v2 din
figura 5.41 folosete trei tabele. Folosind opiunea SQLView obinem o fraz SELECT cu totul
remarcabil:
SELECT facturi.NrFact, facturi.Data, clienti.NumeClient, codPost_loc.Localitate,
[ValoareTotala]-[TVAColectata] AS Expr1, facturi.TVAColectata, facturi.ValoareTotala, *
FROM (codPost_loc INNER JOIN clienti ON codPost_loc.CodPostal = clienti.CodPostal)
INNER JOIN facturi ON clienti.CodClient = facturi.CodClient
WHERE (((facturi.NrFact)>#6/20/2005#) AND ((codPost_loc.Localitate)="Iasi"));
Clauza FROM vine acum n centrul ateniei prin apariia clauzei INNER JOIN. Iat cum stau
lucrurile: deoarece n lista pe care vrem s obinem se gsesc atribute plasate n cele trei tabele, n
clauza FROM trebuie enumerate cele trei numere; n fapt, dup cum am vzut n capitolul 4, cele trei
tabele prin legate prin restricii refereniale, atributele de legtura fiind cheile strine cheile primare.
Astfel, legtura dintre FACTURI i CLIENI se poate realiza prin intermediul atributului CodClient
care este cheie primar n CLIENI (tabela printe) i cheie strin n FACTURI (tabela copil).
142
Legtura dintre aceste dou tabele care prezint un cmp comun se numete jonciune i se
simbolizeaz n SQL prin INNER JOIN:
SELECT *
FROM facturi INNER JOIN clienti ON facturi.CodClient=clienti.CodClient
Fr a intra n prea multe detalii teoretice, reinem c, ori de cte ori informaiile necesare i
condiiile pe care trebuie s le ndeplineasc acele informaii privesc atribute aflate n tabele diferite,
trebuie fcut jonciunea acestor tabele. Cnd tabele nu pot fi joncionate direct, trebuie aduse cu
fora n clauza FROM i tabelele care s completeze lanul.
Ne intereseaz, spre exemplu, numrul i data facturilor emise clienilor din judeul Iai.
Numrul i data facturilor se gsesc n tabela FACTURI (atributele NrFact i Data), ns pentru
denumirea judeului exist un atribut (Judet) n tabela CODPOST_LOC. Cele dou tabele nu pot fi
joncionate direct, aa nct atragem n interogare i cea de-a treia tabel CLIENI:
SELECT NrFact, Data
FROM (facturi INNER JOIN clienti ON facturi.codclient=clienti.codclient)
INNER JOIN codpost_loc ON codpost_loc.codpostal=clienti.codpostal
WHERE judet=Iasi
ORDER BY NrFact
Scris sub form de modul ACCESS InterogareJONCTIUNE1() i lansat prin apsarea
butonului Run, fraza SELECT obine rezultatul este cel din figura 6.9.
Figura 6.9. Un modul ACCESS cu fraz SELECT ce joncioneaz cele trei tabele (plus rezultatul)
Lsam s se neleag, la un moment dat, c n SQL pot fi formulate interogri mult mai
complexe dect se poate realiza cu ajutorul machetei din paragraful 5.4. Haidei s lum o asemenea
problem, ce-i drept nu att de complicat precum ameninam: Care sunt facturile emise n aceeai zi
ca i factura 111113 ? Dificultatea ine de faptul c cerina este formulat indirect, adic vrem s
aflm facturile emise ntr-o zi (Data), dar noi nu tim data etalon, ci factura-etalon.
Problema propus poate fi rezolvat relativ uor folosind o subconsultare, dup cum va fi
prezentat ntr-un paragraf viitor. Pn una-alta, soluia pe care o avem n acest moment la ndemn se
bazeaz pe autojonciune. Autojonciunea nseamn jonciunea unei tabele (FACTURI) cu ea-nsi,
practic, jonciunea a dou instane ale unei aceleai tabele. Pentru a jonciune cele dou instane
trebuie s aib pseudonime (aliasuri) diferite, n cazul nostru F1 i F2. ntruct ne intereseaz facturi
emise n aceeai zi cu 111113, autojonciunea se face dup atributul Data:
SELECT *
FROM facturi F1 INNER JOIN facturi F2 ON F1.data=F2.data
WHERE F2.NrFact = 111113
143
Iat rezultatul vezi figura 6.10. Rezultatul conine 10 coloane, cinci din prima instan a
tabelei FACTURI (F1) i cinci din a doua instan (F2). ACCESSul e destul de inspirat s scrie
naintea fiecrui atribut din ce instan provine.
144
145
146
2. Se formeaza cte un grup pentru fiecare valoare distincta a atributului Data - vezi figura 6.14.
3. Pentru fiecare din cele noua grupuri se calculeaza suma valorilor atributului ValoareTotala. Tabela
rezultat va avea noua linii, ca n figura 6.15.
Dac intereseaz este numrul facturilor emise pentru fiecare client, rspunsul poate fi obinut
prin interogarea:
SELECT NumeClient, COUNT(NrFact) AS NrFacturi_pe_Client
FROM facturi INNER JOIN clienti ON facturi.CodClient = clienti.CodClient
GROUP BY NumeClient
147
148
1. Airinei, D. a., Instrumente software pentru afaceri. Aplicaii practice, Editura Sedcom
Libris, Iai, 2010
2. Fotache, M.,SQL Dialecte DB2, Oracle, Visual FoxPro, Ed. Polirom, Iai, 2001
3. Fotache, M., Proiectarea bazelor de date, Ed. Polirom, Iai, 2005
149
4. Grama, A., Fotache, M., ugui, A., Instrumente software pentru afaceri. Lucrri practice
i ntrebri gril, Editura Sedcom Libris, Iai, 2009
5. ugui, Al., Modele aplicative pentru gestiunea datelor subMICROSOFT ACCESS 2007.
Proiectul Biblio, Ed. Sedcom Libris, Iai, 2010