Sunteți pe pagina 1din 20

Capitolul 14.

Tabele temporare i virtuale


n capitolul anterior am prezentat cteva exemple de tabele create i populate
prin SELECT-uri mai mult sau mai puin complicate. O bun parte dintre acestea
ne-au folosit la stocarea temporar a unor rezultate de form tabelar, rezultate ce
ar putea constitui sursa unor rapoarte sau chiar a unor controale din formulare.
Pentru rapoarte sau popularea controalelor de tip list, combo, mult mai nimerit
este folosirea tabelelor temporare i/sau virtuale. Acestea, spre deosebire de
tabelele obinuite, nu ocup spaiu pe disc, dect pe parcursul unei sesiuni de
lucru, sau chiar mai puin, ntruct n schema bazei se stocheaz permanent doar
structura lor, nu i coninutul.
14.1. Tabele temporare
Tabelele temporare se creaz prin aceeai comand - CREATE TABLE cu cele
dou variante majore list de atribute i/sau restricii sau consultri (SELECT).
Exist dou tipuri de tabele temporare
1
, locale i globale. Coninutul ambelor tipuri
nu este vizibil dect utilizatorului curent (sesiunii curente, pentru c ntre sesiunile
de lucru coninutul unei tabele temporare poate fi stocat doar prin copierea ntr-o
tabel obinuit). Gulutzan i Pelzer le numesc tabele-dependente-de-sesiune.
2

Tabelele temporare locale sunt cele mai invizibile, coninutul lor fiind
disponibil doar nuntrul aceluiai modul de cod executat n cadrul unei sesiuni de
lucru. Doar cele globale pot fi transmise ntre modulele executate ntr-o aceeai
sesiune de lucru, ns i aici trebuie avut grij, pentru c tabelele temporare pot
pstra coninutul lor la finalizarea unei tranzacii (ON COMMIT PRESERVE
ROWS), dar l i pot pierde automat (ON COMMIT DELETE ROWS). Cele dou
clauze funcioneaz i pentru tabelele virtuale locale.

Prima utilizare major a tabelelor temporare intete simplificarea interogrilor.
Ne referim, pentru nceput, la frustrarea pe care au trit-o utilizatorii de
PostgreSQL n paragraful 9.6 dedicat expresiilor tabel (WITH... SELECT...),
paragraf n care numai PostgreSQL-itii au stat pe tu. S lum problema att de







1
[Melton & Simon 2002], pp.100-102,
2
[Gulutzan & Pelzer 1999], p.350
650 Capitolul 14
ndrgit: Care este judeul n care berea s-a vndut cel mai bine ? Folosind o tabel
temporar, i n acest dialect lucrurile pot fi simplificate sensibil:
CREATE LOCAL TEMPORARY TABLE judete_bere AS
(SELECT Judet, SUM(Cantitate*PretUnit*(1+ProcTVA)) AS Vnz_Bere
FROM judete j
INNER JOIN coduri_postale cp ON j.Jud=cp.Jud
INNER JOIN clienti c ON cp.CodPost=c.CodPost
INNER JOIN facturi f ON c.CodCl=f.CodCl
INNER JOIN liniifact lf ON f.NrFact= lf.NrFact
INNER JOIN produse p ON lf.CodPr=p.CodPr
WHERE Grupa='Bere'
GROUP BY Judet) ;

SELECT * FROM judete_bere
WHERE Vnz_Bere = (SELECT MAX(Vnz_Bere) FROM judete_bere) ;

Figura 14.1. O (alt) tulburare de personalitate a PostgreSQL-ului
Practic, expresia-tabel a devenit tabel temporar. Acum, dac tot a venit
vremea confidenelor, trebuie s v mrturisesc c PostgreSQL-ul iese din frontul
standardului, deoarece:
PostgreSQL-ul nu pstreaz definiia tabelelor temporare ntre sesiuni;
Nu face diferenierea dintre tabelele temporare globale i locale;
Dac, n lipsa clauzei ON COMMIT, standardul prevede tergerea
liniilor, n PostgreSQL este invers cnd lipsete ON COMMIT
coninutul se pstreaz;
SQL. Dialecte DB2, Oracle, PostgreSQL i SQL Server 651
Clauza ON COMMIT are, pe lng cele dou opiuni standard
PRESERVE ROWS i DELETE ROWS, clauza DROP care semnalizeaz
c, la finalul trazaciei, tabela urmeaz s fie tears.
n plus (de fapt, n minus!), nu am reuit s folosesc clauza ON COMMIT,
mesajul de eroare fiind, de fiecare dat de genul celui din figura 14.1
3
.

Care sunt cei mai mari trei 3 datornici ?
n varianta scriptural PostgreSQL pe care o vom nira mai jos vom crea trei
tabele temporare locale/globale: tFACTURAT care conine vnzrile pentru fiecare
client, tNCASAT ce conine totalul ncasrilor de la fiecare client i tDE_INCASAT,
cu restul de plat al fiecrui client:
CREATE LOCAL TEMPORARY TABLE tFacturat AS (
SELECT CodCl, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Facturat
FROM facturi F
INNER JOIN liniifact LF ON F.NrFact=LF.NrFact
INNER JOIN produse P ON LF.CodPr=P.CodPr
GROUP BY CodCl ;
CREATE LOCAL TEMPORARY TABLE tIncasat AS
SELECT CodCl, SUM(Transa) AS Incasat
FROM facturi F INNER JOIN incasfact I ON F.NrFact=I.NrFact
GROUP BY CodCl ;
CREATE LOCAL TEMPORARY TABLE tDe_INCASAT AS
SELECT DenCl, tFACTURAT.CodCl, Facturat,
COALESCE(Incasat,0) AS Incasat,
Facturat - COALESCE(Incasat,0) AS De_Incasat
FROM tFACTURAT
INNER JOIN clienti ON tFacturat.CodCl=clienti.CodCl
LEFT OUTER JOIN Tincasat
ON tFACTURAT.CodCl=tINCASAT.CodCl ;

Interogarea final face apel doar la ultima tabel temporar:
SELECT * FROM tDe_INCASAT WHERE De_Incasat >=
(SELECT MAX(De_Incasat) FROM tDe_INCASAT WHERE De_Incasat <
(SELECT MAX(De_Incasat) FROM tDe_INCASAT WHERE De_Incasat <







3
Vezi i documentaia PostgreSQL la pagina: http://www.postgresql.org/docs/8.3/interactive/
sql-createtable.html
652 Capitolul 14
(SELECT MAX(De_Incasat) FROM tDe_INCASAT)
) ) ;
Silii fiind de sintaxa rigid a funciei CONNECTBY(), n paragraful 12.5 am
creat o tabel virtual PERSONAL2_MODIF01. Puteam, la fel de bine, folosi i o
tabel temporar n locul celei virtuale. Analog simplificm obinerea traseelor Iai-
Focani (i distanei fiecrei rute vezi figura 12.33), dac recurgem la o tabel
temporar X.
CREATE LOCAL TEMPORARY TABLE x AS
SELECT ierarhie.Loc2 AS Loc1, ierarhie.Loc1 AS Loc2, distanta, ierarhie.Cale
FROM ( SELECT *
FROM CONNECTBY('distante', 'Loc2', 'Loc1', 'Iasi', 0, '**')
AS t2 (Loc1 TEXT, Loc2 TEXT, LEVEL INT, cale text)
) ierarhie, distante d
WHERE ierarhie.Loc2 =d.Loc1 AND ierarhie.Loc1=d.Loc2 ;

SELECT x1.cale, SUM(x2.distanta) AS Km
FROM x x1, x x2
WHERE x1.cale LIKE 'Iasi%Focsani' AND x1.cale LIKE x2.cale || '%'
GROUP BY x1.cale
ORDER BY Km
n capitolul 17 vom vedea la lucru tabelele temporare PostgreSQL, dar n alt
ipostaz cea de substitut al variabilelor publice.

i n Oracle apar cteva diferene fa de evangheliile SQL n materie de
tabele temporare. Poate cea mai important este c n Oracle nu pot fi create tabele
temporare locale, ci numai globale. ncercm s simplificm interogarea din
paragraful 13.1 care acum produce rezultatul din figura 13.4:
CREATE GLOBAL TEMPORARY TABLE tab ON COMMIT PRESERVE ROWS AS
SELECT SYS_CONNECT_BY_PATH (Loc1, '**') || '**' || Loc2 AS Traseu,
LEVEL AS Nivel, Loc1, Loc2,
EXTRACT (HOUR FROM Durata) * 60 + EXTRACT (MINUTE
FROM durata) AS Durata_Min, ROWNUM AS Ord
FROM distante d START WITH Loc1='Iasi'
CONNECT BY PRIOR Loc2 = Loc1 AND Loc1<>'Focsani' AND Level < 20
ORDER BY Ord ;

SELECT 10001 AS IdCursa, 1 AS "StatieNr", 'Iasi' AS "Statie",
TIMESTAMP'2008-04-01 09:05:00' AS "Data/Ora"
FROM dual
UNION
SELECT 10001, t2.Nivel + 1, t2.Loc2, TIMESTAMP'2008-04-01 09:05:00' +
TO_DSINTERVAL ('0 ' || FLOOR(
SQL. Dialecte DB2, Oracle, PostgreSQL i SQL Server 653
(SELECT SUM(Durata_Min) FROM tab WHERE t2.Traseu LIKE Traseu || '%' )
/ 60) || ':' ||
MOD ( (SELECT SUM( Durata_Min ) FROM tab WHERE t2.Traseu
LIKE Traseu || '%' ), 60)
|| ':00')
FROM tab t1 INNER JOIN tab t2 ON t1.Traseu LIKE '**Iasi**Vaslui%Focsani'
AND t1.traseu LIKE t2.Traseu || '%'
ORDER BY 1,2

Interesant este i modul n care tergem o tabel temporar n Oracle. Astfel,
comanda DROP fr o pregtire prealabil genereaz mesajul de eroare din
figura 14.2.

Figura 14.2. Tentativ euat de tergere a unei tabele temporare n Oracle
Din mesaj ar rezulta c tabela temporar ar trebui, mai nti tears cu TRUNC.
ntr-adevr, prin succesiunea celor dou comenzi:
TRUNCATE TABLE tab ;
DROP TABLE tab ;
tergerea decurge fr probleme.

DB2 face (ca i SQL Server, dar n alt direcie) opinie separat n materie de
tabele temporare. n DB2 o tabel temporar este definit la nivel de sesiune, ns
descrierea sa nu apare n dicionarul de date (catalogul de sistem). De aceea, o
tabel temporar nu poate fi partajat cu alte sesiuni. La terminarea sesiunii, orice
urm a tabelei temporare dispare. Comanda nu este CREATE TABLE, ci DECLA-
654 Capitolul 14
RE GLOBAL TEMPORARY TABLE care este ns destul de limitat. Copiem
atributele tabelei CURSE din paragraful 13.1 n tabela temporar tCURSE:
DECLARE GLOBAL TEMPORARY TABLE tCurse AS (
SELECT 1001 AS IdCursa, CAST (' ' AS VARCHAR(200)) AS Traseu,
CAST ('2008-04-01 09:05:00' AS TIMESTAMP) AS DataOra_Plecare
FROM sysibm.sysdummy1 ) DEFINITION ONLY ;

Figura 14.3. Tabele extrem de temporare n DB2
n schimb, cnd ncercm s populm tabela temporar cu INSERT-ul din
paragraful 13.3.1 avem parte de un mesaj destul de ciudat, potrivit cruia tCURSE
este un obiect necunoscut (un fel de OZN) - vezi figura 14.3. Explicaia este c
DB2-ul uit extrem de repede definiia tabelei temporare. Firete c v, ntrebai, ca
i mine, la ce bune tabelele temporare n DB2. Rspunsul va fi oferit n urmtoarea
ediie a crii (care, dac ar urma trendul acesteia, ar aprea prin 2015).

i SQL Server este departe de sintaxa din standardul SQL. Cele dou tipuri de
tabele temporare sunt specificate prin simboluri # plasate naintea numelui. Astfel,
SQL. Dialecte DB2, Oracle, PostgreSQL i SQL Server 655
dac, la creare, numele tabelei este prefixat de un singur simbol #, atunci tabela
temporar este una local, vizibil deci numai n sesiunea curent. Dac prefixul
numelui tabelei este format din dou semne # (##), atunci tabela temporar este
global. Relum exemplul celor trei datornici prezentat i pentru PostgreSQL:
SELECT CodCl, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Facturat
INTO #tFacturat
FROM facturi F INNER JOIN liniifact LF ON F.NrFact=LF.NrFact
INNER JOIN produse P ON LF.CodPr=P.CodPr
GROUP BY CodCl ;
SELECT CodCl, SUM(Transa) AS Incasat
INTO #tIncasat
FROM facturi F INNER JOIN incasfact I ON F.NrFact=I.NrFact
GROUP BY CodCl ;
SELECT DenCl, #tFacturat.CodCl, Facturat, COALESCE(Incasat,0) AS Incasat,
Facturat - COALESCE(Incasat,0) AS De_Incasat
INTO #tDe_INCASAT
FROM #tFacturat
INNER JOIN clienti ON #tFacturat.CodCl=clienti.CodCl
LEFT OUTER JOIN #tIncasat ON #tFacturat.CodCl = #tIncasat.CodCl ;

SELECT * FROM #tDe_INCASAT WHERE De_Incasat >=
(SELECT MAX(De_Incasat) FROM #tDe_INCASAT WHERE De_Incasat <
(SELECT MAX(De_Incasat) FROM #tDe_INCASAT WHERE De_Incasat <
(SELECT MAX(De_Incasat) FROM #tDe_INCASAT)
) ) ;
14.2. Tabele virtuale n interogri
Ca i cele temporare, tabelele virtuale (view-urile) sunt construite prin
subconsultri aplicate asupra tabelelor de baz i/sau altor tabele temporare sau
view-uri. Tot similar celor temporare, numai definiia (structura) este persistent i
public (accesibil altor sesiuni/utilizatori), coninutul nu ! Exist ns i diferene
majore. O tabel virtual nu are coninut propriu-zis, fiind instaniat doar la
invocarea acesteia (invocare ce nseamn, de fapt, execuia interogrii-definiie).
Dar cea mai important deosebire este c numai o parte dintre view-uri sunt
actualizabile, iar modificarea lor se propag automat n tabelele surs. Inserarea,
modificarea sau tergerea unei linii dintr-un view actualizabil se va traduce, n fapt,
n inserarea, modificarea sau tergerea de linii n/din una sau mai multe dintre
tabelele surs.
Avantajele tabelelor virtuale sunt multiple:
uurarea interogrilor, prin salvarea rezultatelor intermediare;
656 Capitolul 14
construirea unor surse de date pentru rapoarte i controale ale formu-
larelor (liste, combo-uri sau chiar grid-uri);
facilitatea vizualizrii informaiilor presrate n multe tabele;
mai bun securitate a datelor; anumitor categorii de utilizatori, n locul
accesului la tabelele bazei, li se poate acorda acces exclusiv la view-uri
n care pot vedea datele pe care sunt autorizai s le consulte/modifice.
Standardul SQL-92 a introdus view-urile ca tabele virtuale ce sunt materializate
la invocarea numelui lor. Materializarea nseamn execuia frazei SELECT ce
constituie definiia tabelei virtuale i popularea cu nregistrrile-rezultat ale
interogrii. Formatul simplist al comenzii de creare a unei tabele virtuale este:
CREATE VIEW <nume-tabel-virtual> [<list-coloane>] AS
<fraz SELECT>
[WITH [<clauz de niveluri>] CHECK OPTION]
unde <clauz de niveluri> ::= CASCADED | LOCAL.

Odat creat tabela virtual, definiia sa este salvat n schema bazei. Ulterior,
ori de cte ori este necesar, la deschiderea/remprosptarea tabelei virtuale, aceasta
este (re) populat cu nregistrri extrase din cele ale tabelelor propriu-zise ce apar
n clauza FROM a interogrii. O tabel virtual poate fi deci privit i ca o expresie
de subconsultare a unei tabele persistente, stocat n baz i invocat prin numele
su.
Potrivit standardelor SQL, unei tabele virtuale nu i se pot asocia indeci i nici
defini restricii, ns unele SGBD-uri optimizeaz lucrul cu view-urile folosind
indecii tabelelor persistente. Numele tabelelor virtuale este unic, i nu se poate
auto-referi, dei un view poate fi creat pe baza unei combinaii de tabele persistente
i/sau alte view-uri.
tergerea unei tabele viruale din schema bazei de date se realizeaz prin
comanda DROP VIEW:
DROP VIEW <nume-tabel-virtual> <comportament>
unde <comportament> ::= CASCADE | RESTRICT. Prin clauza CASCADE, se terg
att tabela virtual curent, ct i toate cele create pe baza acesteia, n timp ce
RESTRICT interzice operaiunea, atta timp ct exist mcar un view construit pe
baza tabelei virtuale curente.
De regul, formatul general al comenzii de creare a tabelelor vrtuale este mai
generos dect cel al unei tabele temporare. Spre exemplu, n DB2 la definirirea unei
tabele temporare nu putem include o expresie tabel, n timp ce la o tabel virtual
da. Comanda urmtoare definete o tabel virtual, NUMERE_NATURALE, ce va
conine numere naturale (atributul Numar) de la 0 la 9999:
CREATE VIEW numere_naturale AS
WITH temp1 (Numar) AS (
VALUES (0) UNION ALL
SELECT Numar + 1 FROM temp1 WHERE Numar < 10000
)
SELECT * FROM temp1;
SQL. Dialecte DB2, Oracle, PostgreSQL i SQL Server 657
Reformulm, astfel, o problem pentru care am formulat soluia n paragraful
12.6: Care sunt numerele de facturi nefolosite (n tabela FACTURI)?
SELECT Numar AS Numar_nefolosit
FROM (SELECT Numar FROM numere_naturale WHERE Numar BETWEEN
(SELECT MIN(NrFact) FROM facturi) AND
(SELECT MAX(NrFact) FROM facturi )
) LEFT OUTER JOIN facturi f ON Numar = NrFact
WHERE f.Nrfact IS NULL
ORDER BY 1

Tabelele virtuale sunt preferabile celor temporare i datorit modului de rem-
prosptare a coninutului. n cadrul aceleai sesiuni, o tabel temporar global nu-
i schimb coninutul (dac a fost definit cu clauza ON COMMIT PRESERVE
ROWS). Dac sesiunea este lung, este probabil ca tabelele din care provine tabela
temporar s fi fost actualizate. Modificrile sunt reflectate automat la urmtoarea
invocare a view-ului, n timp ce tabela temporar va fi remprosptat abia la
urmtoarea iniializare (urmtoarea sesiune).

Figura 14.4. Actualizarea dinamic a tabelelor virtuale din tabelele-surs
Figura 14.4 prezint o sesiune n care sunt create o tabel temporar
(tFacturatCl) i una virtual (vFacturatCl) care, iniial, au coninut identic. n
aceeai sesiune, se adaug o factur (3120) cu dou linii pentru Clientul 1 SQL.
Dup inserare, coninutul tabelei temporare este identic celui de dup crearea sa, n
658 Capitolul 14
timp ce prima linie din tabela virtual se mprospteaz cu noua valoare a
atributului Facturat pentru Client 1 SRL.
14.3. Probleme ale actualizrilor tabelelor surs pe
baza modificrii tabelelor virtuale
Am vzut n figura 14.4 c o tabel virtual se mprospteaz automat cu
modificrile operate n tabelele-Surs. Ei, bine, n unele cazuri i reciproca este
valabil, adic o serie de view-uri sunt modificabile (d.p.d.v. al coninutului), iar
modificrile se propag n tabela sau tabelele surs. Numai c aici exist o serie
ntreag de restricii. Pentru vFacturatCl discuia se simplific din start deoarece la
creare s-a folosit opiunea READ ONLY, ceea ce nseamn c orice tentativ de
inserare, modificare i tergere va fi automat tratat cu dumnie.
Pentru a fi actualizabil, fiecare nregistrare a unei tabele virtuale trebuie s
poat fi asociat unei singure linii dintr-o tabel surs. Astfel, modificarea unei linii
din view poate fi propagat fr probleme de ambiguitate. Firete, o tabel derivat
de genul:
CREATE VIEW vJudete1 AS
SELECT * FROM Judete
WHERE regiune = 'Moldova' OR regiune = 'Dobrogea'
nu va crea probleme insolubile la actualizare. n schimb, dei cu un coninut identic
vJudete1, tabela virtual vJudete2, creat prin comanda care urmeaz, nu este
actualizabil nici n SQL, nici n majoritatea SGBD-urilor importante.
CREATE VIEW vJudete2 AS
SELECT * FROM Judete WHERE regiune = 'Moldova'
UNION
SELECT * FROM Judete WHERE regiune = 'Dobrogea'

Pentru a intra n cteva detalii, s imaginm o tabel derivat denumit INCA-
SARI_CLIENTI figura 14.5 - ce conine documentul de ncasare, suma ncasat
(corespunztoare documentului) i clientul care a efectuat plata. Comanda de
creare a tabelei virtuale este:
CREATE VIEW vINCASARI_CLIENTI AS (
SELECT DenCl, CodDoc, NrDoc, DataDoc, SUM(Transa) AS SumaPlatita
FROM clienti c
INNER JOIN facturi f ON c.CodCl = f.CodCl
INNER JOIN incasfact incf ON f.NrFact = incf.NrFact
INNER JOIN incasari inc ON incf.CodInc = inc.CodInc
GROUP BY DenCl, CodDoc, NrDoc, DataDoc
);
SQL. Dialecte DB2, Oracle, PostgreSQL i SQL Server 659

Figura 14.5. O tabel virtual
Utilitatea unei asemenea relaii virtuale poate fi certificat de un angajat al com-
partimentului financiar care se ocup, printre altele, i cu evidena ncasrilor de la
clieni. Pe ct de util, pe att de problematic devine INCASARI_CLIENTI atunci
cnd se pune problema actualizrii sale i propagrii modificrilor n tabelele de
baz din care provine, CLIENTI, FACTURI, INCASARI i INCASFACT. Iat cteva
dintre probleme:
modificarea atributului SumaPlatit este imposibil de operat n tabela de
baz, INCASFACT. O linie din tabela virtual corespunde uneia sau mai
multor linii din tabela de baz (depinde cte facturi sunt achitate prin
documentul de plat respectiv). Dac pentru ordinul de plat 111 din 10
august 2007 se dorete modificarea (corecia) sumei din 155 971 n 156 000,
nu se poate cunoate trana i factura unde s-a comis eroarea i, normal,
unde trebuie operat modificarea.
modificarea celui de-al patrulea tuplu din (Client 2 SA, OP, 555, 10-Aug-
2007, 106275) n (Client 5 SRL, OP, 555, 10-Aug-2007, 106275), adic
modificarea clientului pentru Ordinul de plat nr. 555 din 10.08.2007 poate
fi operat n trei moduri n tabele de baz:
o fie se modific n FACTURI pentru factura 1113 valoarea
atributului CodCl din 1002 n 1005;
o fie se modific n linia din INCASFACT codul ncasrii (CodInc)
din 1238 n 1235;
o fie se modific n INCASFACT valoarea atributului NrFact din
1113 n 1112, pstrndu-se codul ncasrii neschimbat;
chiar dac nu la fel de plauzibile, cele trei variante genereaz o stare de
confuzie pentru SGBD.
inserarea unei linii n view este o aciune temerar, dar fr prea muli sori
de izbnd: oricare ar fi cele patru tabele surs n care s-ar face inserarea,
cel puin un atribut important (ce nu poate avea valori NULL) nu are
valori specificate, astfel nct se ncalc una dintre restricii (de entitate sau
comportament). Spre exemplu, convenim c inserarea trebuie s se
propage numai n INCASFACT. Nu se cunoate ns numrul facturii
ncasate. Cum NrFact este un component al cheii primare a tabelei, este clar
c operaiunea va fi interzis de SGBD.
tergerea unei linii din tabela virtual ridic problema identificrii liniei
sau liniilor dintr-una sau mai multe tabele de baz.
660 Capitolul 14

n plus, sursele unei tabele virtuale pot fi att tabele propriu-zise, ct i alte
tabele virtuale, situaie n care vorbim de mai multe niveluri ale tabelelor virtuale.
Spre exemplu, pentru a construi o tabel virtual vCLIENTI n care pot fi
modificate: denumirea clientului, adresa sa, denumirea localitii n care i are
sediul i numele judeului, sunt necesare urmtoarele view-uri:
CREATE VIEW vJudete AS
SELECT * FROM Judete ;
CREATE VIEW vCoduri_Postale AS
SELECT CodPost, Loc, vJudete.Jud, Judet, Regiune
FROM coduri_postale cp INNER JOIN vJUDETE ON cp.Jud = vJUDETE.Jud ;
CREATE VIEW vClienti AS
SELECT CodCl, DenCl, Adresa, vCoduri_Postale.CodPost, Loc, Jud,
Judet, Regiune
FROM clienti INNER JOIN vCoduri_Postale
ON clienti.CodPost = vCoduri_Postale.CodPost ;

Tabelele virtuale trebuie s includ toate coloanele cheilor primare/alternative
ale tabelelor de baz. O linie dintr-o tabel virtual trebuie s corespund unei
singure linii din tabela de baz. Toate coloanele neincluse n view trebuie s
permit valori NULL sau s prezinte valori implicite. Altminteri, inserarea unei
linii ntr-o tabel derivat ar fi imposibil. Nu poate fi actualizat o tabel virtual
creat prin fraze SELECT n care apar:
clauze GROUP BY / HAVING,
funcii agregat,
coloane calculate,
operatorii: UNION, INTESECT, EXCEPT (MINUS),
SELECT DISTINCT.

SQL:1999 definete civa termeni legai de tabelele virtuale sau consultrile-
argument ale comenzilor de actualizare
4
:
- potenial actualizabil;
- actualizabil;
- banal-actualizabil;
- inserabil-n.







4
[Melton & Simon 2002], pp.111-112, 283-284
SQL. Dialecte DB2, Oracle, PostgreSQL i SQL Server 661
O specificaie de interogare (care poate fi expresia de definire a unui view, sau o
expresie ce urmeaz comenzii UPDATE vezi paragraful 13.3.4) este potenial
actualizabil dac i numai dac
5
:
nu se folosete clauza DISTINCT;
toate coloanele din clauza SELECT apar n list o singur dat;
nu se folosesc clauze GROUP BY/HAVING.
Dac specificaia de interogare este potenial actualizabil, iar n clauza FROM
apare o singur tabel, atunci toate coloanele din aceast tabel care apar n
specificaie sunt actualizabile. Dac specificaia este potenial actualizabil, ns n
clauza FROM apar dou sau mai multe tabele, atunci o coloan din specificaie este
actualizabil numai dac:
provine dintr-o singur tabel dintre cele din clauza FROM;
ntre tabela respectiv i specificaia de interogare exist o relaie de
coresponden a unicitii, adic tabela i pstreaz n interogare cheia
primar sau cheile alternative.
Altfel spus, valoarea unui atribut dintr-o tabel virtual este actualizabil doar
dac aceasta corespunde unei singure valori dintr-o tabel surs. O specificaie
(expresie) de interogare este actualizabil dac mcar una dintre coloane este
actualizabil, i banal-actualizabil dac:
este actualizabil,
clauza FROM conine o singur tabel, i
toate coloanele sale sunt actualizabile.
Termenul (destul de nenorocit) inserabil-n desemneaz o specificaie/expresie
actualizabil, dac toate tabelele din clauza FROM accept adugri de linii
(INSERT-uri) i dac expresia nu include operatorii UNION, INTERSECT i
EXCEPT.
SGBD-urile actuale prezint cteva diferene n materie de reguli privind
actualizarea tabelelor virtuale. Cel mai flexibil mecanism este, ns, cel al declana-
toarelor de tip INSTEAD OF, despre care vom discuta pe scurt n capitolul 17.
14.3.1. Tabele virtuale n DB2
Documentaia IBM i bibliografia DB2
6
delimiteaz tabelele virtuale DB2 n
funcie de operaiunile pe care le accept, dup cum urmeaz:
tabele virtuale ce permit tergerea (deletable);







5
[Melton & Simon 2002], pp.111-112, 284
6
Vezi spre exemplu [Baklarz & Zikopoulos 2008]
662 Capitolul 14
tabele virtuale ce permit modificarea (updatable);
tabele virtuale ce permit inserarea (insertable);
tabele virtuale ce permit doar citirea, non-modificabile (read-only);
Cele din prima categorie accept comanda DELETE, comand pe baza creia
tergerea se propag ntr-o tabel-surs. Pentru a putea propaga tergerea, fraza
SELECT de creare a tabelei virtuale nu trebuie s conin clauze precum DIS-
TINCT, GROUP BY/HAVING, VALUES, UNION, INTERSECT, EXCEPT, iar
fiecare linie din view corespunde unei singure linii dintr-o tabel surs. Singurul
operator ansamblist acceptabil n unele situaii este UNION ALL. Un view este
actualizabil (updatable) dac, n plus, are mcar un atribut al crui valoare poate fi
modificat i inserabil (insertable) dac toate coloanele sale sunt actualizabile.
Astfel, vClienti, creat ceva mai sus, nu permite tergerea unei linii vezi figura
14.6. Cum nici mcar tergerile nu sunt admise, cu att mai puin modificrile i
inserrile.
Tabela virtual vLiniiFact7001, fiind definit printr-o selecie aplicat unei
singure tabele LINIIFACT:
CREATE VIEW vLiniifact7001 AS
SELECT NrFact, Linie, CodPr, Cantitate, PretUnit
FROM liniifact WHERE NrFact=7001 ;

Figura 14.6. O tabel virtual din care nu pot fi terse linii
accept orice gen de modificare, fiind inserabil, suport oricare dintre comenzile
urmtoare:
DELETE FROM vLiniifact7001 WHERE Linie=3 ;
UPDATE vLiniifact7001 SET Cantitate = 100 WHERE Linie=2 ;
INSERT INTO vLiniifact7001 VALUES (7001, 3, 4, 30, 730) ;

Nici tabela virtual vLiniifact7001_7002 nu accept tergerea de nregistrri, dei
operatorul folosit este UNION ALL:
CREATE VIEW vLiniifact7001_7002 AS
SQL. Dialecte DB2, Oracle, PostgreSQL i SQL Server 663
SELECT * FROM liniifact WHERE NrFact=7001
UNION ALL
SELECT * FROM liniifact WHERE NrFact=7002 ;
Comanda urmtoare se va solda cu un mesaj de eroare similar celui din figura
14.6, deoarece, pentru a funciona, UNION ALL trebuie s conecteze tabele-surs
diferite:
DELETE FROM vLiniifact7001_7002 WHERE NrFact=7001 AND Linie=3
14.3.2. Tabele virtuale n Oracle
Sintaxa Oracle pentru definirea tabelelor virtuale este un pic mai generoas
dect cea din DB2. Dac n DB2, pentru modificarea frazei SELECT ce definete un
view aveam nevoie de o combinaie DROP VIEW/CREATE VIEW, n Oracle se
poate folosi CREATE OR REPLACE VIEW. De asemenea, n DB2, o tabel virtual
era READ ONLY n funcie de modul su de definire. n Oracle se poate folosi
clauza READ ONLY pentru a bloca modificarea coninului unui view actualizabil.
CREATE OR REPLACE VIEW vLiniifact7001 AS
SELECT NrFact, Linie, CodPr, Cantitate, PretUnit
FROM liniifact WHERE NrFact=7001 ;
Regulile privind actualizarea coninutului unei tabele virtuale n vederea
propagrii n tabelele surs sunt apropiate de cele din DB2, aa c nu mai insistm.
14.3.3. Tabele virtuale n PostgreSQL
PostgreSQL are, probabil, cea mai srac sintax (dintre cele patru dialecte) n
materie de CREATE VIEW:
CREATE VIEW vJudete AS
SELECT * FROM judete ORDER BY Judet;
Dup creare, un view este destul de apatic n PostgreSQL. Astfel, dac se
ncearc inserarea unei noi nregistrri corespunztoare judeului Galai, mesajul
este cel din figura 14.7. Asta nseamn c toate tabelele virtuale sunt READ ONLY
n PostgreSQL. Totui, din explicaii deducem c trebuie s apelm la mecanismul
de reguli.
664 Capitolul 14

Figura 14.7. Probleme PostgreSQL chiar i la inserri ntr-o tabel virtual banal- actualiza-
bil
Acest mecanism de reguli prefaeaz destul de bine scurta discuie din capitolul
17 dedicat declanatoarelor de tip INSTEAD OF. Tabela virtual vJudete este
creat dintr-o singur tabel, JUDETE, aa c inserarea unei linii n view trebuie s
atrag dup sine adugarea nregistrrii respective n tabel:
CREATE RULE vJudete_ins AS ON INSERT TO vJudete DO INSTEAD
INSERT INTO Judete VALUES (NEW.Jud, NEW.Judet, NEW.Regiune) ;
Numele fiecrui atribut este prefixat n clauza VALUES cu NEW., ceea ce
semnalizeaz c este vorba despre valorile atributelor de pe noua linie a tabelei
virtuale. Acum comanda INSERT funcioneaz:
INSERT INTO vJudete VALUES ('GL', 'Galati', 'Moldova') ;
La modificare unei nregistrri, vom folosi i clauza OLD. pentru a califica
valoarea dinaintea eventualei modificri a atributului cheie primar Jud.
CREATE RULE vJudete_upd AS ON UPDATE TO vJudete DO INSTEAD
UPDATE judete
SET Jud = NEW.Jud, Judet = NEW.Judet, Regiune = NEW.Regiune
WHERE Jud = OLD.Jud;
Redactarea regulii pentru tergere devine acum o treab simpl:
CREATE RULE vJudete_del AS ON DELETE TO vJudete
DO INSTEAD DELETE FROM judete WHERE Jud = OLD.Jud;

Firete, o modificare a unei valori din view care ncalc restricii definite n
tabela de baz, va genera un mesaj de eroare vezi figura 14.8.
SQL. Dialecte DB2, Oracle, PostgreSQL i SQL Server 665

Figura 14.8. nclcarea unei restricii la inserarea ntr-o tabel virtual
Mecanismul regulilor ne permite s ncercm rezolvarea problemelor de
actualizare chiar i pentru tabele virtuale cu dou sau mai multe tabele surs
joncionate. Iat cazul view-ului vFacturi:
CREATE VIEW vFacturi AS
SELECT lf.NrFact, DataFact, DenCl, Linie, lf.CodPr, DenPr, UM,
ProcTVA, Grupa,
Cantitate, PretUnit, Cantitate * PretUnit AS ValFaraTVALinie,
Cantitate * PretUnit * ProcTVA AS TVALinie,
Cantitate * PretUnit * (1+ProcTVA) AS ValTotLinie
FROM liniifact lf
INNER JOIN produse p ON lf.CodPr=p.CodPr
INNER JOIN facturi f ON lf.NrFact=f.NrFact
INNER JOIN clienti c ON f.CodCl=c.CodCl
ORDER BY lf.NrFact, Linie;
Dei sunt nu mai puin de patru tabele surs, tabela cea mai de jos este
LINIIFACT, aa c suntem tentai s scriem regula de inserare astfel:
CREATE OR REPLACE RULE vFacturi_ins AS
ON INSERT TO vFacturi DO INSTEAD
INSERT INTO liniifact VALUES ( NEW.NrFact, NEW.Linie,
NEW.CodPr, NEW.Cantitate, NEW.PretUnit ) ;
Regula funcioneaz dac adugm o linie unei facturi existente, ca de exemplu
a cincea linie din factura 3119:
INSERT INTO vFacturi (NrFact, Linie, CodPr, Cantitate, PretUnit)
VALUES (3119, 5, 6, 1000, 1200) ;
Dar ns adugm o linie ntr-o factur nou - s zicem factura cu nr. 3120 -,
firete c vom nclca restricia referenial vezi figura 14.9.
666 Capitolul 14

Figura 14.9. nclcarea restriciei refereniale la inserarea ntr-un view cu mai multe tabele-
surs
Avem soluii i pentru acest gen de probleme. S redefinim tabela virtual,
prelund i codul clientului:
DROP VIEW vFacturi ;
CREATE VIEW vFacturi AS
SELECT lf.NrFact, DataFact, DenCl, f.CodCl, Linie, lf.CodPr, DenPr,
UM, ProcTVA, Grupa,
Cantitate, PretUnit, Cantitate * PretUnit AS ValFaraTVALinie,
Cantitate * PretUnit * ProcTVA AS TVALinie,
Cantitate * PretUnit * (1+ProcTVA) AS ValTotLinie
FROM liniifact lf
INNER JOIN produse p ON lf.CodPr=p.CodPr
INNER JOIN facturi f ON lf.NrFact=f.NrFact
INNER JOIN clienti c ON f.CodCl=c.CodCl
ORDER BY lf.NrFact, Linie;

Problema cea mai delicat la rescrierea regulii de inserare este c un INSERT n
vFacturi poate s se propage n tabele surs n mai multe moduri:
inserarea unei linii numai n tabela LINIIFACT;
inserare a cte o linie n tabelele LINIIFACT i FACTURI;
inserare a cte o linie n tabelele LINIIFACT i PRODUSE;
inserare a cte o linie n tabelele LINIIFACT, FACTURI i PRODUSE.
Din fericire, putem formula mai multe reguli pentru aceeai operaiune (
inserare), astfel:
DROP RULE IF EXISTS vFacturi_ins ON vFacturi ;
CREATE OR REPLACE RULE vFacturi_ins1 AS ON INSERT TO vFacturi
WHERE NOT EXISTS (SELECT 1 FROM facturi WHERE NrFact=NEW.NrFact
DO INSTEAD
INSERT INTO facturi VALUES (NEW.NrFact, NEW.DataFact, NEW.CodCl) ;
SQL. Dialecte DB2, Oracle, PostgreSQL i SQL Server 667
CREATE OR REPLACE RULE vFacturi_ins2 AS ON INSERT TO vFacturi
WHERE NOT EXISTS (SELECT 1 FROM produse WHERE CodPr=NEW.CodPr)
DO INSTEAD
INSERT INTO produse VALUES (NEW.CodPr, NEW.DenPr, NEW.UM,
NEW.Grupa, NEW.ProcTVA ) ;
CREATE OR REPLACE RULE vFacturi_ins3 AS ON INSERT TO vFacturi
DO INSTEAD
INSERT INTO liniifact VALUES ( NEW.NrFact, NEW.Linie, NEW.CodPr,
NEW.Cantitate, NEW.PretUnit ) ;

Figura 14.10. Propagarea inserrii n tabelele PRODUSE i LINIIFACT
Testarea funcionalitii o vom face printr-un INSERT n care vom aduga o
linie dintr-o factur nou i un produs la fel de nou:
INSERT INTO vFacturi (NrFact, Linie, CodPr, Cantitate, PretUnit, DataFact,
CodCl, DenPr, UM, Grupa, ProcTVA)
VALUES (3120, 1, 8, 400, 1200, DATE'2007-10-01', 1001, 'Produs 8', 'buc', 'Cafea', 0.19) ;

Figura 14.10 demonstreaz c s-au fcut inserrile n cele trei tabele (n tabela
FACTURI inserarea este implicit, altminteri restricia referenial ar fi fost
nclcat).
14.3.4. Tabele virtuale n SQL Server
SQL Server este comparabil n materie de tabele virtuale cu dialectele din DB2 i
Oracle. Comenzile rulate n DB2 descrise n paragraful 14.3.1 sunt funcionale i n
SQL Server, aa c nu mai insistm.
668 Capitolul 14
14.4. Restricii n tabele virtuale
Dup cum am vzut la nceputul paragrafului 14.2, formatul general al
comenzii CREATE VIEW are i o clauz important pe care o vom discuta pe scurt
n continuare WITH CHECK OPTION. Aceast clauz permite ca la orice inserare
sau modificare, nregistrrile din tabela virtual s respecte predicatul formulat n
clauza WHERE. vJudeteMoldova conine nregistrrile tabelei JUDEE pentru care
valoarea atributului Regiune este Moldova:
CREATE VIEW vJudeteMoldova AS
SELECT * FROM judete WHERE Regiune='Moldova' WITH CHECK OPTION

Comanda INSERT urmtoare:
INSERT INTO vJudeteMoldova VALUES ('BC', 'Bacau', 'Moldova')
se execut, fr probleme, ceea ce nu este valabil i pentru (vezi figura 14.11):
INSERT INTO vJudeteMoldova VALUES ('AR', 'Arad', 'Banat')

Figura 14.11. Blocarea unei inserri de ctre clauza WITH CHECK OPTION
Dac sursa unei tabele virtuale este tot o tabel virtual care, la rndul su, este
tot o tabel virtual s.a.m.d., clauza CHECK OPTION poate folosi opiunea
CASCADED pentru a verifica dac inserarea/modificarea respect predicatele din
clauza WHERE ale tuturor view-urilor (de pe toate nivelurile) sau LOCAL pentru a
verifica numei respectarea predicatului din clauza WHERE a tabelei virtuale
curente. Dintre cele patru dialecte SQL, numai PostgreSQL nu are implementat
clauza WITH CHECK OPTION, aa c i pentru acest gen de restricii sunt
necesare declanatoare sau reguli.
Standardele SQL nu prevd declararea de restricii clasice (cheie primar,
cheie alternativ, restricie referenial etc.) ntr-o tabel virtual. Nici dialectele
DB2, SQL Server i PostgreSQL. Oracle permite trei tipuri de restricii declarabile
pentru un view: chei primare, chei alternative i restricii refereniale.

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