Sunteți pe pagina 1din 97

LABORATOR 1 SQL

CERERI MONOTABEL

1. Analizaţi sintaxa simplificată a comenzii SELECT. Care dintre clauze sunt obligatorii?
SELECT { [ {DISTINCT | UNIQUE} | ALL] lista_campuri | *}
FROM [nume_schemă.]nume_obiect ]
[, [nume_schemă.]nume_obiect …]
[WHERE condiţie_clauza_where]
[GROUP BY expresie [, expresie …]
[HAVING condiţie_clauza_having] ]
[ORDER BY {expresie | poziţie} [, {expresie | poziţie} …] ]
2. Găsiţi eroarea din instrucţiunea următoare.
SELECT employee_id, last_name, salary * 12 salariu anual
FROM employees;
Obs: SALARIU ANUAL este un alias pentru câmpul care reprezintă salariul anual.
Dacă un alias conţine blank-uri, el va fi scris obligatoriu între ghilimele. Altfel, ghilimelele pot fi
omise. Alias-ul apare în rezultat, ca antet de coloană pentru expresia respectivă. Doar cele specificate între
ghilimele sunt case-sensitive, celelalte fiind scrise implicit cu majuscule.

Varianta 1:
SELECT employee_id, last_name, salary * 12 salariu_anual
FROM employees;

Varianta 2:
SELECT employee_id, last_name, salary * 12 " Salariu Anual "
FROM employees;

3. Să se listeze structura tabelelor din schema HR (EMPLOYEES, DEPARTMENTS, JOB_HISTORY,


JOBS, LOCATIONS, COUNTRIES, REGIONS), observând tipurile de date ale coloanelor.
Obs: Se va utiliza comanda SQL*Plus
DESCRIBE nume_tabel
4. Să se listeze conţinutul tabelelor din schema considerată, afişând valorile tuturor câmpurilor.
Obs: Se va utiliza comanda SQL
SELECT * FROM nume_tabel;
5. Să se obţină încă o dată rezultatul cererii precedente, fără a rescrie cererea.
Obs: Ultima comandă SQL lansată de către client este păstrată în buffer-ul SQL.
Pentru rularea acesteia se utilizează “/” sau RUN.
6. Listaţi structura tabelului EMPLOYEES şi apoi daţi comanda RUN (sau “/”). Ce observaţi? Comenzile
SQL*Plus sunt păstrate în buffer?
DESC employees
RUN

1
7. Să se afişeze codul angajatului, numele, codul job-ului, data angajării. Salvaţi instrucţiunea SQL într-un
fişier numit p1_14.sql.
Obs: Pentru salvarea ultimei comenzi SQL se utilizează comanda SAVE. Precizarea extensiei „.sql” a
fişierului nu este obligatorie.
SELECT employee_id, last_name, job_id, hire_date
FROM employees;
SAVE z:\…\ p1_14.sql
8. Reexecutaţi cererea folosind fişierul p1_14.sql.
START z:\…\ p1_14.sql
sau
@ z:\…\ p1_14.sql
9. Editaţi fişierul p1_14.sql, adăugând coloanelor câte un alias (cod, nume, cod job, data angajarii).
EDIT z:\…\ p1_14.sql

Cererea se modifică astfel:


SELECT employee_id cod, last_name nume, job_id " cod job ", hire_date " data angajarii "
FROM employees;

@ z:\…\ p1_14.sql

10. Să se listeze, cu şi fără duplicate, codurile job-urilor din tabelul EMPLOYEES.


SELECT job_id FROM employees;
SELECT DISTINCT job_id FROM employees;
SELECT UNIQUE job_id FROM employees;
Obs. DISTINCT = UNIQUE
11. Să se afişeze numele concatenat cu prenumele, separate prin spaţiu. Etichetaţi coloana “Nume si
prenume”.
Obs: Operatorul de concatenare este “||”. Şirurile de caractere se specifică între apostrofuri (NU ghilimele,
caz în care ar fi interpretate ca alias-uri).
SELECT last_name|| ' ' || first_name " Nume si prenume "
FROM employees;
12. Să se listeze numele şi salariul angajaţilor care câştigă mai mult de 10000 $.
SELECT last_name, salary
FROM employees
WHERE salary > 10000;
13. Să se modifice cererea anterioară astfel încât să afişeze numele şi salariul pentru toţi angajaţii al căror
salariu este cuprins între 5000$ şi10000$.
Obs: Pentru testarea apartenenţei la un domeniu de valori se poate utiliza operatorul
[NOT] BETWEEN valoare1 AND valoare2
SELECT last_name, salary
FROM employees
WHERE salary BETWEEN 5000 AND 10000;
14. Să se creeze o cerere pentru a afişa numele angajatului şi numărul departamentului pentru angajatul 104.
2
SELECT last_name, department_id
FROM employees
WHERE employee_id =104;
15. Să se afişeze numele şi salariul pentru toţi angajaţii din departamentele 10 sau 30, în ordine alfabetică a
numelor.
Obs: Apartenenţa la o mulţime finită de valori se poate testa prin intermediul operatorului IN, urmat de
lista valorilor între paranteze şi separate prin virgule:
expresie IN (valoare_1, valoare_2, …, valoare_n)
SELECT last_name, salary
FROM employees
WHERE department_id IN (10, 30)
ORDER BY last_name;
16. Să listeze numele şi salariile angajaţilor care câştigă mai mult de 10000 $ şi lucrează în departamentul 10
sau 30. Se vor eticheta coloanele drept Angajat si Salariu lunar.
17. Care este data curentă?
Obs: Pseudocoloana care returnează data curentă este SYSDATE. Pentru completarea sintaxei obligatorii
a comenzii SELECT, se utilizează tabelul DUAL:
SELECT SYSDATE
FROM dual;
Datele calendaristice pot fi formatate cu ajutorul funcţiei TO_CHAR(data, format), unde formatul poate fi
alcătuit dintr-o combinaţie a următoarelor elemente:
Element Semnificaţie
D Numărul zilei din săptămână (duminică=1;
luni=2; …sâmbătă=6).
DD Numărul zilei din lună.
DDD Numărul zilei din an.
DY Numele zilei din săptămână, printr-o
abreviere de 3 litere (MON, THU etc.)
DAY Numele zilei din săptămână, scris în
întregime.
MM Numărul lunii din an.
MON Numele lunii din an, printr-o abreviere de 3
litere (JAN, FEB etc.)
MONTH Numele lunii din an, scris în întregime.
Y Ultima cifră din an
YY, YYY, YYYY Ultimele 2, 3, respectiv 4 cifre din an.
YEAR Anul, scris în litere (ex: two thousand
four).
HH12, HH24 Orele din zi, între 0-12, respectiv 0-24.
MI Minutele din oră.
SS Secundele din minut.
SSSSS Secundele trecute de la miezul nopţii.

18. Să se afişeze numele şi data angajării pentru fiecare salariat care a fost angajat în 1987. Se cer 2 soluţii:
una în care se lucrează cu formatul implicit al datei şi alta prin care se formatează data.
3
Varianta1:
SELECT first_name, last_name, hire_date
FROM employees
WHERE hire_date LIKE („%87‟);
Varianta 2:
SELECT first_name, last_name, hire_date
FROM employees
WHERE TO_CHAR(hire_date, „YYYY‟)=‟1987‟;
Sunt obligatorii ghilimelele de la şirul „1987‟? Ce observaţi?
19. Să se afişeze numele şi job-ul pentru toţi angajaţii care nu au manager.
SELECT last_name, job_id
FROM employees
WHERE manager_id IS NULL;
20. Să se afişeze numele, salariul şi comisionul pentru toţi salariaţii care câştigă comisioane. Să se sorteze
datele în ordine descrescătoare a salariilor, iar pentru cei care au acelaşi salariu în ordine crescătoare a
comisioanelor.
SELECT last_name, salary, commission_pct
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC, commission_pct ASC;
21. Să se listeze numele tuturor angajaţilor care au a treia litera din nume 'a'.
Obs: Pentru a forma măştile de caractere utilizate împreună cu operatorul LIKE cu scopul de a compara
şirurile de caractere, se utilizează:
% - reprezentând orice şir de caractere, inclusiv şirul vid;
_ (underscore) – reprezentând un singur caracter.
SELECT DISTINCT last_name
FROM employees
WHERE last_name LIKE '__a%';

22. Folosind data curentă să se afişeze următoarele informaţii:


- numele zilei, numărul zilei din săptămână, numărul zilei din luna, respectiv numărul zilei din an;
- numărul lunii din an, numele lunii cu abreviere la 3 caractere, respectiv numele complet al lunii;
- ora curentă (ora, minute, secunde).
23. Să se listeze numele departamentelor care funcţionează în locaţia având codul 1700 şi al căror manager
este cunoscut.
24. Să se afişeze codurile departamentelor în care lucrează salariaţi.
25. Să se afişeze numele şi prenumele salariaţilor angajaţi în luna mai 1987.
26. Să se listeze codurile angajaţilor care au avut şi alte joburi faţă de cel prezent. Să se ordoneze rezultatul
descrescător după codul angajatului.
27. Să se afişeze numele şi data angajării pentru cei care lucrează în departamentul 80 şi au fost angajaţi în
luna martie a anului 1997.
28. Să se afişeze numele joburilor care permit un salariu cuprins între 8300$ şi 14000$.
29. Care este grila de salarizare pentru un salariu de 10000$?

4
30. Să se listeze numele tuturor angajaţilor care au 2 litere 'L' în nume şi lucrează în departamentul 30 sau
managerul lor este 123.
31. Să se afişeze numele, job-ul şi salariul pentru toţi salariaţii al căror job conţine şirul 'CLERK' sau 'REP' şi
salariul nu este egal cu 1000, 2000 sau 3000 $.
32. Să se afişeze numele, salariul şi comisionul pentru toţi angajaţii al căror salariu este mai mare decât de 5
ori valoarea comisionului (salary*commission_pct*5).

5
LABORATOR 2 - SQL
FUNCŢII SQL (single-row)

Funcţiile SQL sunt predefinite în sistemul Oracle şi pot fi utilizate în instrucţiuni SQL. Ele nu
trebuie confundate cu funcţiile definite de utilizator, scrise în PL/SQL.
 Dacă o funcţie SQL este apelată cu un argument având un alt tip de date decât cel aşteptat, sistemul
converteşte implicit argumentul înainte să evalueze funcţia.
 Dacă o funcţie SQL este apelată cu un argument null, atunci aceasta returnează valoarea null.
Singurele funcţii care nu urmează această regulă sunt CONCAT, NVL şi REPLACE.
Principalele funcţii SQL pot fi clasificate în următoarele categorii:
 Funcţii single-row
 Funcţii multiple-row (funcţii agregat)
Funcţiile single-row returnează câte o linie rezultat pentru fiecare linie a tabelului sau vizualizării
interogate. Aceste funcţii pot apărea în listele SELECT, clauzele WHERE, START WITH, CONNECT BY
şi HAVING.

1. Analizaţi următoarele funcţii pentru prelucrarea şirurilor de caractere:


Funcţie Semnificaţie Exemplu
Converteşte un şir de caractere
LOWER (expresie) LOWER ('AbCdE') = 'abcde'
la minuscule.
Converteşte un şir de caractere
UPPER (expresie) UPPER ('AbCdE') = 'ABCDE'
la majuscule.
Converteşte un şir de caractere
la un şir care începe cu
INITCAP (expresie) INITCAP ('AbCdE') = 'Abcde'
majusculă şi continuă cu
minuscule.
Extrage din expresia de tip şir
de caractere, n caractere
începând cu poziţia m. Dacă
SUBSTR ('AbCdE', 2, 2) = 'bC'
lipseşte argumentul n, atunci
SUBSTR ('AbCdE', 2) = 'bCdE'
SUBSTR (expresie, m[, n]) extrage toate caracterele până la
SUBSTR ('AbCdE', -3,2) = 'Cd'
sfârşitul şirului. Dacă m este
SUBSTR ('AbCdE', -3) = 'CdE'
negativ numărătoarea poziţiilor
începe de la sfârşitul şirului de
caractere spre început.
Returnează numărul de
LENGTH (expresie) LENGTH ('AbCdE') = 5
caractere al expresiei.
Returnează poziţia la care se
găseşte a n-a ocurentă a
INSTR (LOWER('AbC aBcDe'), 'ab', 5, 2)
expresiei 'expr1' în cadrul
=0
INSTR (expresie, expr1[, m][, n]) expresiei 'expresie', căutarea
INSTR (LOWER('AbCdE aBcDe'), 'ab', 5)
începând de la poziţia m. Daca
=7
m sau n lipsesc, valorile
implicite sunt 1 pentru ambele.
LTRIM (expresie[, expr1]) sau Reversul funcţiilor LPAD, RTRIM ('abcdeXXXX', 'X')
RTRIM (expresie[, expr1]) RPAD. Trunchează expresia = 'abcde'
caracter la stânga sau la dreapta LTRIM (' abcde') = 'abcde'
prin eliminarea succesivă a
caracterelor din expresia expr1.
Implicit, daca lipseşte, expr1
este ' ' un spaţiu.
TRIM (LEADING 'X' FROM
Permite eliminarea caracterelor 'XXXabcdeXXX') = 'abcdeXXX'
TRIM (LEADING | TRAILING | specificate (caractere_trim) de TRIM (TRAILING 'X' FROM
BOTH caractere_trim FROM la începutul (leading) , sfârşitul 'XXXabcdeXXX') = 'XXXabcde'
expresie) (trailing) sau din ambele părţi, TRIM ( BOTH 'X' FROM
dintr-o expresie caracter data. 'XXXabcdeXXX') = 'abcde'

TRIM (' abcde ') = 'abcde'

2. Să se afişeze pentru fiecare angajat din departamentul 20 un şir de caractere de forma "Funcţia
salariatului {prenume} {nume} este {cod functie}". Să se afişeze prenumele cu iniţiala litera mare, iar
numele cu litere mari (Stephen KING), iar codul funcţiei să se afişeze cu litere mici.
3. Să se afişeze pentru angajatul cu numele 'HIGGINS' codul, numele şi codul departamentului. Cum se
scrie condiţia din WHERE astfel încât să existe siguranţa ca angajatul 'HIGGINS' va fi găsit oricum ar fi
fost introdus numele acestuia? Căutarea trebuie să nu fie case-sensitive, iar eventualele blank-uri care
preced sau urmează numelui trebuie ignorate.
UPPER(TRIM(last_name))='HIGGINS';
4. Să se afişeze pentru toţi angajaţii al căror nume se termină în 'n', codul, numele, lungimea numelui şi
poziţia din nume în care apare prima data litera 'a'. Asociaţi aliasuri coloanelor returnate de cerere.
SELECT employee_id, last_name, LENGTH(last_name), INSTR(UPPER(last_name), 'A')
FROM employees
WHERE SUBSTR(last_name,-1)='n';
5. Analizaţi următoarele funcţii aritmetice:
Funcţie Semnificaţie Exemplu
ROUND(1.6) = 2
Returnează valoarea rotunjită a expresiei
ROUND(1.4) = 1
până la n zecimale. Daca n este negativ sunt
ROUND (expresie [, n]) ROUND (1234.56,1) = 1234.6
rotunjite cifre din stânga virgulei. Valoarea
ROUND (1230.56, -2) = 1200
implicită pentru n este 0.
ROUND (1260.56, -2) = 1300

MOD (11, 4) = MOD (11, -4) = 3


MOD (m,n) Returnează restul împărţirii lui m la n.
MOD(-11, 4) = MOD (-11, -4) = -3

6. Să se afişeze detalii despre salariaţii care au lucrat un număr întreg de săptămâni până la data curentă.
MOD(ROUND(SYSDATE – hire_date), 7)=0;
7. Să se afişeze numele, salariul şi numărul de mii al salariului rotunjit la 2 zecimale pentru cei care nu au
salariul divizibil cu 1000.
8. Analizaţi următoarele operaţii pe expresii de tip dată calendaristică:
Tipul de date al
Operaţie Descriere
rezultatului
date -/+ number Date Scade/Adaugă un număr de zile dintr-o / la o dată.
date1 - date2 Number Întoarce numărul de zile dintre două date calendaristice.
date +/-
Date Scade/Adaugă un număr de ore la o / dintr-o dată calendaristică.
number/24
9. Să se afişeze data (luna, ziua, ora, minutul si secunda) de peste 10 zile.
SYSDATE+10
10. Să se afişeze numărul de zile rămase până la sfârşitul anului.
ROUND(TO_DATE(‟31-DEC-2009‟)-SYSDATE)
11. a. Să se afişeze data de peste 12 ore.
SYSDATE+12/24
b. Să se afişeze data de peste 5 minute.
SYSDATE+1/288
12. Analizaţi următoarele funcţii pentru prelucrarea datelor calendaristice:
Funcţie Semnificaţie Exemplu
SYSDATE Întoarce data şi timpul curent
Returnează numărul de luni dintre
data date1 şi data date2. Rezultatul
MONTHS_BETWEEN poate fi pozitiv sau negativ după cum ROUND(MONTHS_BETWEEN
(date1, date2) date1 este mai recentă sau nu faţă de (SYSDATE + 31, SYSDATE)) = 1
date2. Zecimalele reprezintă parţi
dintr-o luna!
Adaugă n luni la o data specificată. MONTHS_BETWEEN
ADD_MONTHS (date, n) Valoarea n trebuie să fie întreagă (ADD_MONTHS(SYSDATE, 3),
(pozitivă sau negativă). SYSDATE) = 3

NEXT_DAY('15-dec-2006','Monday')
Returnează data corespunzătoare
= '18-dec-2006'
NEXT_DAY (date, char) primei zile a săptămânii specificate
NEXT_DAY ('15-dec-2006',1)
(char) care urmează după date.
= '18-dec-2006'

13. Să se afişeze numele angajatului, data angajării şi data negocierii salariului, care a avut loc în prima zi de
Luni, după 6 luni de serviciu. Etichetaţi această coloană “Negociere”.
NEXT_DAY(ADD_MONTHS(hire_date, 6), „Monday‟)
14. Pentru fiecare angajat să se afişeze numele şi numărul de luni de la data angajării. Etichetaţi coloana
“Luni lucrate”. Să se ordoneze rezultatul după numărul de luni lucrate. Se va rotunji numărul de luni la
cel mai apropiat număr întreg.
SELECT last_name, ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)) “Luni lucrate”
FROM employees
ORDER BY MONTHS_BETWEEN(SYSDATE, hire_date);
SELECT last_name, ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)) “Luni lucrate”
FROM employees
ORDER BY “Luni lucrate”;

SELECT last_name, ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)) “Luni lucrate”


FROM employees
ORDER BY 2;
15. Analizaţi următoarele funcţii de conversie:
Obs. Conversiile implicite asigurate de server-ul Oracle sunt:
 de la VARCHAR2 sau CHAR la NUMBER;
 de la VARCHAR2 sau CHAR la DATE;
 de la NUMBER la VARCHAR2 sau CHAR;
 de la DATE la VARCHAR2 sau CHAR.
SELECT last_name
FROM employees
WHERE TO_CHAR(hire_date,'yyyy')=1994;
SELECT last_name
FROM employees
WHERE hire_date='07-JUN-1994';
SELECT employee_id||' '||last_name||' '||hire_date
FROM employees
WHERE department_id=10;
Conversiile explicite se realizează cu ajutorul funcţiilor de tip TO_{tip}
Funcţie Semnificaţie Exemplu
Converteşte o valoare de tip numeric sau dată TO_CHAR('3') = ' 3'
calendaristică, la un şir de caractere conform cu TO_CHAR(-12) = '-12'
TO_CHAR
formatul specificat sau cu setările naţionale TO_CHAR(sysdate, 'DDMMYYYY')
(expr_number_sau
specificate (NLS - National Language Support).
_date[, format][, = ' 09122004'
Daca formatul sau parametrii lipsesc se
nlsparameters]) TO_CHAR (sysdate + 365 * 57,
utilizează formatul şi parametrii impliciţi.
Formatul este case sensitive. 'ddmmyyyy') = ' 25112061'
TO_NUMBER Converteşte o valoare de tip şir de caractere la o
(expr_char[, valoare numerică conform cu formatul TO_NUMBER ('-12.22', 'S99.99')
format][, specificat. Dacă formatul sau parametrii lipsesc = -12.22
nlsparameters]) se utilizează formatul şi parametrii impliciţi.
Converteşte o valoare de tip şir de caractere la o
TO_DATE
valoare de tip dată calendaristică în
(expr_char[, TO_DATE ('15-feb-2006','dd-mon-
conformitate cu formatul specificat. Dacă
format][, yyyy')
formatul sau parametrii lipsesc se utilizează
nlsparameters])
formatul şi parametrii impliciţi.
16. Să se afişeze numele şi prenumele pentru toţi angajaţii care s-au angajat în luna mai.
17. Analizaţi următoarele funcţii SQL:
Funcţie Semnificaţie Exemplu
Returnează expr1 dacă aceasta nu este NVL(NULL, 1) = 1
NULL, expr2 în caz contrar. Cele 2 expresii NVL(2, 1) = 2
NVL (expr1, expr2) trebuie să aibă acelaşi tip sau expr2 să NVL('c', 1) = 'c' -- face conversie
permită conversia implicită la tipul NVL(1, 'c') -- eroare
expresiei expr1. --nu face conversie
Dacă expr1 este nenulă atunci returnează NVL2 (1, 2, 3) = 2
NVL2 (expr1, expr2, expr3)
expr2, altfel Returnează expr3 NVL2 (NULL, 2, 3) = 3
18. Să se afişeze numele angajaţilor şi comisionul. Dacă un angajat nu câştigă comision, să se scrie “Fara
comision”. Etichetaţi coloana “Comision”.
NVL(TO_CHAR(commission_pct), „Fara comision‟)
19. Să se listeze numele, salariul şi comisionul tuturor angajaţilor al căror venit lunar depăşeşte 10000$.
salary * NVL(commission_pct, 0) venit_lunar

20. Analizaţi expresia CASE şi funcţia DECODE:

Funcţie/Expresie Semnificaţie Exemplu


CASE expr WHEN expr_bool1
În funcţie de valoarea unei expresii
THEN return_expr1
returnează valoarea primei perechi
[WHEN expr_bool2 THEN
WHEN .. THEN care se potriveşte sau
return_expr2
dacă nu se potriveşte nici una expresia
...
din ELSE. Nu se poate specifica NULL
WHEN expr_booln THEN
pentru toate expresiile de returnat.
return_exprn ]
(return_expri). Toate expresiile trebuie
[ELSE return_expr]
sa aibă acelaşi tip de date
END
DECODE (expr, expr_cautare1, Decodifică valoarea expresiei. Dacă
expr_rezultat1, valoarea expresiei este expr_cautarei
DECODE (1, 1, 2, 3) = 2
[expr_cautare2, expr_rezultat2, atunci e returnată expr_rezultati. Dacă
DECODE (2, 1, 2, 3) = 3
.. nu se potriveşte nici o expresie de
DECODE (3, 1, 2, 3) = 3
expr_cautaren, expr_rezultatn, ] căutare atunci e returnat
[rezultat_implicit]) rezultat_implicit.
21. Să se afişeze numele, codul funcţiei, salariul şi o coloana care să arate salariul după mărire. Se ştie că
pentru IT_PROG are loc o mărire de 10%, pentru ST_CLERK 15%, iar pentru SA_REP o mărire de
20%. Pentru ceilalţi angajaţi nu se acordă mărire. Să se denumească coloana "Salariu revizuit".
SELECT last_name, job_id, salary,
DECODE(job_id,
„IT_PROG‟, salary*1.1,
‟ST_CLERK‟, salary*1.15,
„SA_REP‟, salary*1.2,
salary ) “salariu revizuit”
FROM employees;

SELECT last_name, job_id, salary,


CASE job_id WHEN „IT_PROG‟ THEN salary* 1.1
WHEN ‟ST_CLERK‟ THEN salary*1.15
WHEN „SA_REP‟ THEN salary*1.2
ELSE salary
END “salariu revizuit”
FROM employees;

SELECT last_name, job_id, salary,


CASE WHEN job_id= „IT_PROG‟ THEN salary* 1.1
WHEN job_id=‟ST_CLERK‟ THEN salary*1.15
WHEN job_id =„SA_REP‟ THEN salary*1.2
ELSE salary
END “salariu revizuit”
FROM employees;
22. Să se afişeze numele salariatului şi codul departamentului în care acesta lucrează. Dacă există salariaţi
care nu au un cod de departament asociat, atunci pe coloana id_depratment să se afişeze:
a. textul “fara departament”;
b. valoarea zero.
23. a. Să se afişeze numele angajaţilor care nu au manager.
b. Să se afişeze numele angajaţilor şi codul managerilor lor. Pentru angajaţii care nu au manager să apară
textul “nu are sef”.
24. Să se afişeze numele salariatului şi:
- venitul anual dacă are comision;
- salariul dacă nu are comision.
Se va utiliza funcţia NVL2.
25. Să se afişeze numele salariatului, salariul şi salariul revizuit astfel:
- dacă lucrează de mai mult de 200 de luni atunci salariul va fi mărit cu 20%;
- dacă lucrează de mai mult de 150 de luni, dar mai puţin de 200 de luni, atunci salariul va fi mărit cu
15%;
- dacă lucrează de mai mult de 100 de luni, dar mai puţin de 150 de luni, atunci salariul va fi mărit cu
10%;
- altfel, salariul va fi mărit cu 5%.
LABORATOR 3 - SQL
CERERI MULTITABEL, SUBCERERI

Atunci când în clauza FROM a unei comenzi SELECT apar mai multe tabele se realizează produsul
cartezian al acestora. De aceea numărul de linii rezultat creşte considerabil, fiind necesară restricţionarea
acestora cu o clauza WHERE.
Atunci când este necesară obţinerea de informaţii din mai multe tabele se utilizează condiţii de join.
Join-ul este operaţia de regăsire a datelor din două sau mai multe tabele, pe baza valorilor comune ale unor
coloane. Condiţiile de corelare utilizează de obicei coloanele cheie primară şi cheie externă.
Pentru claritatea şi eficienţa accesului la baza de date se recomandă prefixarea numelor coloanelor cu
numele tabelelor din care fac parte (tabel.coloana). De asemenea, există posibilitatea de a utiliza aliasuri pentru
tabelele din clauza FROM şi utilizarea lor în cadrul comenzii SELECT respective (alias.coloana). Această
identificare (prin 'tabel.coloana' sau 'alias.coloana') este obligatorie atunci când se face referinţă la o coloana ce
apare în mai mult de un tabel din clauza FROM.
Tipuri de join:
 equijoin (se mai numeşte inner join sau simple join) - compunerea a două tabele diferite după o
condiţie ce conţine operatorul de egalitate.
SELECT last_name, department_name, location_id, e.department_id
FROM employees e, departments d
WHERE e.department_id = d.department_id;
Obs: Numele sau alias-urile tabelelor sunt obligatorii în dreptul coloanelor care au acelaşi nume în
mai multe tabele.
 nonequijoin - compunerea a două relaţii tabele după o condiţie oarecare, ce NU conţine operatorul
de egalitate.
SELECT last_name, salary, grade_level
FROM employees, job_grades
WHERE salary BETWEEN lowest_sal AND highest_sal;
 outerjoin - compunerea externă a două tabele diferite completând una dintre relaţii cu valori NULL
acolo unde nu există în aceasta nici un tuplu ce îndeplineşte condiţia de corelare. Relaţia completată
cu valori NULL este cea în dreptul căreia apare “(+)”. Operatorul (+) poate fi plasat în orice parte a
condiţiei de join, dar nu în ambele părţi. Full outer join = Left outer join UNION Right outer join.
SELECT last_name, department_name,location_id
FROM employees e, departments d
WHERE e.department_id(+) = d.department_id;
 selfjoin - compunerea externă a unui tabel cu el însuşi după o condiţie dată.
SELECT sef.last_name, angajat.last_name
FROM employees sef, employees angajat
WHERE sef.employee_id = angajat.manager_id
ORDER BY sef.last_name;
1. Pentru fiecare angajat să se afişeze numele, codul şi numele departamentului.
SELECT last_name, e.department_id, department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id;
2. Să se afişeze numele angajatului, numele departamentului pentru toţi angajaţii care câştigă comision.
3. Să se listeze numele job-urile care există în departamentul 30.
SELECT DISTINCT job_title
FROM employees e, jobs j
WHERE e.job_id = j.job_id
AND department_id = 30;
4. Să se afişeze numele, job-ul şi numele departamentului pentru toţi angajaţii care lucrează în Seattle.
SELECT last_name, job_id, department_name
FROM employees e, departments d, locations s
WHERE e.department_id = d.department_id
AND d.location_id = s.location_id
AND city = „Seattle‟;
5. Să se afişeze numele, salariul, data angajării şi numele departamentului pentru toţi programatorii care
lucrează în America.
region_name = „Americas‟
job_title = „Programmer‟
6. Să se afişeze numele salariaţilor şi numele departamentelor în care lucrează. Se vor afişa şi salariaţii care nu
lucrează într-un departament (right outher join).
SELECT last_name, department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id(+);
7. Să se afişeze numele departamentelor şi numele salariaţilor care lucrează în ele. Se vor afişa şi
departamentele care nu au salariaţi (left outher join).
8. Să se afişeze numele, job-ul, numele departamentului, salariul şi grila de salarizare pentru toţi angajaţii.
9. Să se afişeze codul angajatului şi numele acestuia, împreună cu numele şi codul şefului său direct. Se vor
eticheta coloanele Ang#, Angajat, Mgr#, Manager. Să se salveze instrucţiunea într-un fişier numit p3_9.sql.
SELECT a.employee_id “Ang#”, a.last_name “Angajat”, b.employee_id “Mgr#”, b.last_name “Manager”
FROM employees a, employees b
WHERE a.manager_id = b. employee_id;
10. Să se modifice p3_9.sql pentru a afişa toţi salariaţii, inclusiv pe cei care nu au şef.
11. Să se afişeze numele salariatului şi data angajării împreună cu numele şi data angajării şefului direct pentru
salariaţii care au fost angajaţi înaintea şefilor lor. Se vor eticheta coloanele Angajat, Data_ang, Manager si
Data_mgr.
12. Pentru fiecare angajat din departamentele 20 şi 30 să afişeze numele, codul departamentului şi toţi colegii
săi (salariaţii care lucrează în acelaşi departament cu el). Se vor eticheta coloanele corespunzător.
SELECT a.last_name “Angajat”, a.department_id ”Departament”, b.last_name “Coleg”
FROM employees a, employees b
WHERE a.department_id = b.department_id
AND a.employee_id <> b.employee_id
AND a.department_id IN (20,30)
ORDER BY a.last_name;
13. Să se afişeze numele şi data angajării pentru salariaţii care au fost angajaţi după Fay.
SELECT last_name, hire_date
FROM employees
WHERE hire_date > (SELECT hire_date
FROM employees
WHERE last_name = „Fay‟);
sau
SELECT a.last_name, a.hire_date
FROM employees a, employees b
WHERE UPPER(b.last_name)=‟FAY‟ AND a.hire_date>b.hire_date;

14. Scrieţi o cerere pentru a afişa numele şi salariul pentru toţi colegii (din acelaşi departament) lui Fay. Se va
exclude Fay.
SELECT last_name, salary
FROM employees
WHERE last_name <> „Fay‟
AND department_id = (SELECT department_id
FROM employees
WHERE last_name = „Fay‟);
15. Să se afişeze codul departamentului, codul şi numele angajaţilor care lucrează în acelaşi departament cu cel
puţin un angajat al cărui nume conţine litera “T”. Să se ordoneze după codul departamentului.
SELECT employee_id, last_name, department_id
FROM employees
WHERE department_id IN (SELECT DISTINCT department_id
FROM employees
WHERE UPPER(last_name) LIKE „%T%‟)
ORDER BY department_id;
16. Să se afişeze numele şi salariul angajaţilor conduşi direct de Steven King.
SELECT last_name, salary
FROM employees
WHERE manager_id = (SELECT employee_id
FROM employees
WHERE UPPER(last_name) ='KING'
AND UPPER(first_name) ='STEVEN' );
17. Să se afişeze numele şi job-ul tuturor angajaţilor din departamentul „Sales‟.
SELECT last_name, job_id
FROM employees
WHERE department_id = (SELECT department_id
FROM departments
WHERE department_name ='Sales');

18. Să se afişeze numele angajaţilor, numărul departamentului şi job-ul tuturor salariaţilor al căror departament
este localizat în Seattle.
SELECT last_name, job_id, department_id
FROM employees
WHERE department_id IN (SELECT department_id
FROM departments
WHERE location_id = (SELECT location_id
FROM locations
WHERE city = „Seattle‟));
Rezolvaţi această problemă utilizând join-uri.
19. Să se afle dacă există angajaţi care nu lucrează în departamentul „Sales‟ şi al căror salariu şi comision
coincid cu salariul şi comisionul unui angajat din departamentul „Sales‟.
SELECT last_name, salary, commission_pct, department_id
FROM employees
WHERE (salary, commission_pct) IN (SELECT salary, commission_pct
FROM employees e, departments d
WHERE e.department_id = d.department_id
AND department_name = „Sales‟)
AND department_id <> (SELECT department_id
FROM departments
WHERE department_name = „Sales‟);
20. Scrieţi o cerere pentru a afişa numele, numele departamentului şi salariul angajaţilor care nu câştigă
comision, dar al căror manager coincide cu managerul unui angajat care câştigă comision.
SELECT last_name, department_name, salary
FROM employees e, departments d
WHERE e.department_id = d.department_id
AND e.manager_id IN (SELECT DISTINCT manager_id
FROM employees
WHERE commission_pct IS NOT NULL)
AND commission_pct IS NULL;
21. Scrieţi o cerere pentru a afişa angajaţii care câştigă mai mult decât oricare funcţionar. Sortaţi rezultatele
după salariu, în ordine descrescătoare.
SELECT last_name, salary, job_id
FROM employees
WHERE salary > (SELECT MAX(salary)
FROM employees
WHERE job_id LIKE '%CLERK')
ORDER BY salary DESC;
22. Să se afişeze codul, numele şi salariul tuturor angajaţilor care câştigă mai mult decât salariul mediu.
23. Să se afişeze pentru fiecare salariat angajat în luna martie numele său, data angajării şi numele jobului.
24. Să se afişeze pentru fiecare salariat al cărui câştig total lunar este mai mare decât 12000 numele său,
câştigul total lunar şi numele departamentului în care lucrează.
25. Să se afişeze pentru fiecare angajat codul său şi numele joburilor sale anterioare, precum şi intervalul de
timp în care a lucrat pe jobul respectiv.
26. Să se modifice cererea de la punctul 25 astfel încât să se afişeze şi numele angajatului, respectiv codul
jobului său curent.
27. Să se modifice cererea de la punctul 26 astfel încât să se afişeze şi numele jobului său curent.
28. Să se afişeze salariaţii care au acelaşi manager ca şi angajatul având codul 140.
29. Să se afişeze numele departamentelor care funcţionează în America.
LABORATOR 4 - SQL
Funcţii multiple-row (grup). Gruparea datelor.

Aceste tipuri de funcţii pot fi utilizate pentru a returna informaţia corespunzătoare fiecăruia dintre
grupurile obţinute în urma divizării liniilor tabelului cu ajutorul clauzei GROUP BY.
Pot apărea în clauzele SELECT, ORDER BY şi HAVING. Server-ul Oracle aplică aceste funcţii fiecărui
grup de linii şi returnează un singur rezultat pentru fiecare mulţime.
Exemple de funcţii grup: AVG, SUM, MAX, MIN, COUNT etc.
Tipurile de date ale argumentelor funcţiilor grup pot fi CHAR, VARCHAR2, NUMBER sau DATE.
Funcţiile AVG şi SUM, operează numai asupra valorilor numerice. Funcţiile MAX şi MIN pot opera asupra
valorilor numerice, caracter sau dată calendaristică.
Toate funcţiile grup, cu excepţia lui COUNT(*), ignoră valorile null. COUNT(expresie)
returnează numărul de linii pentru care expresia dată nu are valoarea null. Funcţia COUNT returnează un
număr mai mare sau egal cu zero şi nu întoarce niciodată valoarea null.
Când este utilizată clauza GROUP BY, server-ul sortează implicit mulţimea rezultată în ordinea
crescătoare a valorilor coloanelor după care se realizează gruparea.
Absenţa clauzei GROUP BY conduce la aplicarea funcţiei grup pe mulţimea tuturor liniilor tabelului.
În clauza GROUP BY se trec obligatoriu toate coloanele prezente în clauza SELECT, care nu sunt
argument al funcţiilor grup.

1. Să se afişeze cel mai mare salariu, cel mai mic salariu, suma şi media salariilor tuturor angajatilor. Etichetaţi
coloanele Maxim, Minim, Suma, respectiv Media. Să se rotunjească rezultatele.
SELECT MIN(salary) min, MAX(salary) max, SUM(salary) suma, ROUND(AVG(salary)) media
FROM employees;
2. Utilizând funcţia grup COUNT să se determine:
a. numărul total de angajaţi;
b. numărul de angajaţi care au manager;
c. numărul de manageri.
3. Să se afişeze diferenţa dintre cel mai mare şi cel mai mic salariu. Etichetaţi coloana “Diferenta”.
4. Să se listeze numărul de angajaţi din departamentul având codul 50.
5. Caţi angajaţi din departamentul 80 câştigă comision?
6. Să se selecteze valoarea medie şi suma salariilor pentru toţi angajaţii care sunt reprezentanţi de vânzări
(SA_MAN, SA_REP).
7. Să se selecteze data angajării primei persoane care a fost angajată de companie.
8. Să se afişeze numărul de angajaţi pentru fiecare job.
SELECT job_id, COUNT(employee_id) nr_angajati
FROM employees
GROUP BY job_id;
9. Să se afişeze minimul, maximul, suma şi media salariilor pentru fiecare departament.
10. Să se afişeze codul departamentului şi media salariilor pentru fiecare job din cadrul acestuia.
SELECT department_id, job_id, AVG(salary)
FROM employees
GROUP BY department_id, job_id;
11. a. Să se afişeze codul departamentelor pentru care salariul minim depăşeşte 5000$.
SELECT department_id, MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary)>5000;

1
b. Să se modifice cererea anterioară astfel încât să se afişeze şi oraşul în care funcţionează aceste
departamente.
12. Să se obţină codul departamentelor şi numărul de angajaţi al acestora pentru departamentele care au cel
puţin 10 angajaţi.
13. Să se obţină codul departamentelor şi suma salariilor angajaţilor care lucrează în acestea, în ordine
descrescătoare după sumă. Se consideră angajaţii care au comision şi departamentele care au mai mult de 5
angajaţi.
14. Să se obţină job-ul pentru care salariul mediu este minim.
SELECT job_id
FROM employees
GROUP BY job_id
HAVING AVG(salary) = (SELECT MIN(AVG(salary))
FROM employees
GROUP BY job_id);
15. Să se afişeze cel mai mare dintre salariile medii pe departamente.
16. a. Să se afişeze codul, numele departamentului şi suma salariilor pe departamente.
SELECT d.department_id, department_name,a.suma
FROM departments d, (SELECT department_id ,SUM(salary) suma
FROM employees
GROUP BY department_id) a
WHERE d.department_id =a.department_id;

b. Daţi o altă metodă de rezolvare a acestei probleme.

17. a. Scrieţi o cerere pentru a afişa numele departamentului, numărul de angajaţi şi salariul mediu pentru
angajaţii din acel departament. Coloanele vor fi etichetate Departament, Nr. angajati, Salariu Mediu.
SELECT department_name “Departament”,
(SELECT COUNT(employee_id)
FROM employees
WHERE department_id = d.department_id ) ” Nr. angajati”,
(SELECT AVG(salary)
FROM employees
WHERE department_id = d.department_id) ”Salariu mediu”
FROM departments d;
b. Daţi o altă metodă de rezolvare pentru problema anterioară.

18. Să se creeze o cerere prin care să se afişeze numărul total de angajaţi şi, din acest total, numărul celor care
au fost angajaţi în 1997, 1998, 1999 şi 2000. Datele vor fi afişate în forma următoare:
Total 1997 1998 1999 2000
--------------------------------------------------------------
50 10 5 25 1

SUM(DECODE(TO_CHAR(hire_date,'yyyy'),1997,1,0))

2
Operatorii ROLLUP şi CUBE

Clauza GROUP BY permite gruparea liniilor selectate după valorile expresiilor precizate în aceasta.
Pentru fiecare grup, va fi returnată o singură linie de informaţie. Clauza GROUP BY poate produce grupări
superagregat utilizând extensiile CUBE sau ROLLUP.
ROLLUP grupează liniile selectate pe baza valorilor primelor n, n - 1, …, 0 expresii din
specificaţia GROUP BY şi returnează o singură linie pentru fiecare grup. ROLLUP creează grupări prin
deplasarea într-o singură direcţie, de la dreapta la stânga, de-a lungul listei de coloane specificate în
clauza GROUP BY. Apoi, se aplică funcţia agregat acestor grupări. Dacă sunt specificate n expresii în
operatorul ROLLUP, numărul de grupări generate va fi n + 1. Liniile care se bazează pe valoarea primelor
n expresii se numesc linii obişnuite, iar celelalte se numesc linii superagregat.
GROUP BY ROLLUP (expr_1, expr_2, …, expr_n) generează n+1 tipuri de linii, corespunzătoare
următoarelor grupări:
 GROUP BY (expr_1, expr_2, …, expr_n-1, expr_n)
 GROUP BY (expr_1, expr_2, …, expr_n-1)
 …
 GROUP BY (expr_1, expr_2)
 GROUP BY (expr_1)
 GROUP BY () – corespunzător absenţei clauzei GROUP BY şi deci, calculului funcţiilor grup din
cerere pentru întreg tabelul.

CUBE grupează liniile selectate pe baza valorilor tuturor combinaţiilor posibile ale expresiilor
specificate şi returnează câte o linie totalizatoare pentru fiecare grup. Acest operator este folosit pentru a
produce mulţimi de rezultate care sunt utilizate în rapoarte. În vreme ce ROLLUP produce subtotalurile
doar pentru o parte dintre combinaţiile posibile, operatorul CUBE produce subtotaluri pentru toate
combinaţiile posibile de grupări specificate în clauza GROUP BY, precum şi un total general.
Dacă există n coloane sau expresii în clauza GROUP BY, vor exista 2n combinaţii posibile
superagregat.
19. Să se afişeze codurile departamentelor în care lucrează cel puţin un angajat, iar pentru fiecare dintre acestea
şi pentru fiecare manager care lucrează în departamentul respectiv să se afişeze numărul de salariaţi. De
asemenea, să se afişeze numărul de salariaţi pentru fiecare departament indiferent de manager şi numărul
total de angajaţi din companie.

SELECT department_id, manager_id, COUNT(employee_id)


FROM employees
WHERE manager_id IS NOT NULL AND department_id IS NOT NULL
GROUP BY ROLLUP (department_id, manager_id);

department_id manager_id COUNT(employee_id)


---------------------------------------------------------------------
10 7782 1
10 7839 1
10 2
-----------------------------------------------------------------------
20 7566 2
20 7788 1
20 7839 1
20 7902 1
20 5
-----------------------------------------------------------------------
30 7698 5
30 7839 1
30 6
-----------------------------------------------------------------------
13
3
20. Să se afişeze codurile departamentelor în care lucrează cel puţin un angajat, iar pentru fiecare dintre acestea
şi pentru fiecare manager care lucrează în departamentul respectiv să se afişeze numărul de salariaţi. De
asemenea, să se afişeze numărul de salariaţi pentru fiecare departament indiferent de manager, numărul de
angajaţi subordonaţi unui manager indiferent de departament şi numărul total de angajaţi din companie.

SELECT department_id, manager_id, COUNT(employee_id)


FROM employees
WHERE manager_id IS NOT NULL AND department_id IS NOT NULL
GROUP BY CUBE (department_id, manager_id);

department_id manager_id COUNT(employee_id)


---------------------------------------------------------------------
10 7782 1
10 7839 1
10 2
-----------------------------------------------------------------------
20 7566 2
20 7788 1
20 7839 1
20 7902 1
20 5
-----------------------------------------------------------------------
30 7698 5
30 7839 1
30 6
-----------------------------------------------------------------------
7566 2
7698 5
7782 1
7788 1
7839 3
7902 1
----------------------------------------------------------------------
13
21. Pentru fiecare departament, job, respectiv an al angajării să se afişeze numărul de salariaţi. De asemenea se
va afişa numărul de angajaţi:
- pentru fiecare departament şi job, indiferent de anul angajării;
- pentru fiecare departament, indiferent de job şi de anul angajării;
- la nivel de companie.
22. Să se afişeze suma alocată pentru plata salariilor pe joburi (codul jobului), în cadrul departamentului (codul
departamentului). De asemenea, să se afişeze valoarea totală necesară pentru plata salariilor la nivel de
departament, valoarea totală necesară pentru plata salariilor la nivel de job, indiferent de departament şi
valoarea totală necesară pentru plata salariilor la nivel de companie.
23. Funcţia GROUPING(expresie) întoarce:
- valoarea 0, dacă expresia a fost utilizată pentru calculul valorii agregat
- valoarea 1, dacă expresia nu a fost utilizată.
24. Să se afişeze numele departamentelor, titlurile job-urilor şi valoarea medie a salariilor, pentru:
- fiecare departament şi, în cadrul său pentru fiecare job;
- fiecare departament (indiferent de job);
- întreg tabelul.
De asemenea, să se afişeze şi o coloană care indică intervenţia coloanelor department_name şi job_title în obţinerea
rezultatului.

4
25. Modificaţi cererea anterioară astfel încât să se afişeze numele departamentelor, titlurile job-urilor şi
valoarea medie a salariilor, pentru:
- fiecare departament şi, în cadrul său pentru fiecare job;
- fiecare departament (indiferent de job);
- fiecare job(indiferent de departament);
- întreg tabelul.
Cum intervin coloanele în obţinerea rezultatului?
Să se afişeze ‟Dept‟, dacă departamentul a intervenit în agregare şi „Job‟, dacă job-ul a intervenit în agregare.
DECODE(GROUPING(department_name), 0, „Dept‟)
26. Utilizaţi cererea de la punctul 20.
a. Eliminaţi clauza WHERE din această cerere. Analizaţi rezultatul obţinut.
b. Modificaţi cererea obţinută astfel încât să se identifice dacă o valoare null din rezultat este stocată pe
una dintre coloanele manager_id sau department_id sau este produsă de operatorul CUBE.
27. Clauza GROUPING SETS. Permite obţinerea numai a anumitor grupări superagregat. Acestea pot fi
precizate prin intermediul clauzei:

GROUP BY GROUPING SETS ((expr_11, expr_12, …, expr_1n), (expr_21, expr_22, …expr_2m), …)


28. Să se afişeze numele departamentelor, numele job-urilor, codurile managerilor angajaţilor, maximul şi suma
salariilor pentru:
- fiecare departament şi, în cadrul său, fiecare job;
- fiecare job şi, în cadrul său, pentru fiecare manager;
- întreg tabelul.

GROUPING SETS ((department_name, job_title), (job_title, e.manager_id), ());

5
LABORATOR 5 SQL
I. Limbajul de control al datelor (COMMIT, SAVEPOINT, ROLLBACK)
II. Limbajul de prelucrare a datelor (LMD). INSERT, UPDATE, DELETE.

I. Limbajul de control al datelor (COMMIT, SAVEPOINT, ROLLBACK)

 Comanda COMMIT permanentizează modificările care au fost realizate de tranzacţia curentă (o


tranzacţie este un set de comenzi DML - INSERT, UPDATE, DELETE sau MERGE); comanda suprimă
toate punctele intermediare definite în tranzacţie şi eliberează blocările tranzacţiei.
Observaţie:
Sistemul realizează un commit implicit:
- la închiderea normală a unui client Oracle (de exemplu SQL*Plus),
- după fiecare comandă LDD (CREATE, ALTER, DROP).
 Comanda SAVEPOINT marchează un punct intermediar în procesarea tranzacţiei. În acest mod este
posibilă împărţirea tranzacţiei în subtranzacţii.
Comanda SAVEPOINT are sintaxa:
SAVEPOINT nume_pct_intermediar;
 Comanda ROLLBACK permite renunţarea la modificările efectuate; aceasta determină încheierea
tranzacţiei, anularea modificărilor asupra datelor şi restaurarea stării lor precedente.
Comanda ROLLBACK are sintaxa:-
ROLLBACK [TO SAVEPOINT nume_punct_salvare];
Observaţie:
- sistemul realizează un rollback implicit dacă sistemul se închide anormal (defecţiune hardware sau
software, pană de curent etc.);
- nici o comanda LDD (CREATE, ALTER; DROP) nu poate fi anulată.
1. Ce efect are următoarea secvenţă de instrucţiuni?

CREATE TABLE dept_*** AS


SELECT * FROM departments;

SELECT *
FROM dept_***;

SAVEPOINT a;

DELETE FROM dept_***;

INSERT INTO dept_***


VALUES (300,’Economic’,100,1000);

INSERT INTO dept_***


VALUES (350,’Cercetare’,200,2000);

SAVEPOINT b;

INSERT INTO dept_***


VALUES (400,’Juritic’,150,3000);

SELECT COUNT(*)
FROM dept_***;

ROLLBACK TO b;
1
SELECT COUNT(*)
FROM dept_***;

ROLLBACK TO a;

INSERT INTO dept_***


VALUES (500,’Contabilitate’,175,1500);

COMMIT;

SELECT *
FROM dept_***;

II. Limbajul de prelucrare a datelor (LMD). INSERT, UPDATE, DELETE, MERGE.

1. Să se creeze tabele emp_*** şi dept_*** (dacă nu este deja creat), având aceeaşi structură şi date ca şi
tabelele employees, respectiv departments.
2. Să se selecteze toate înregistrările din cele două tabele create anterior.
3. Ştergeţi toate înregistrările din cele 2 tabele create anterior. Salvati modificarile.
DELETE FROM emp_***;
...
COMMIT;
4. Să se listeze structura tabelului employees şi să se compare cu structura tabelului emp_***. Ce observaţi?
5. Sintaxa simplificată a comenzii INSERT
- pentru inserarea unei singuri linii:
INSERT INTO nume_tabel [(col1,col2,...)]
VALUES (expresie1, expresie2, ...);
- pentru inserarea liniilor rezultat ale unei comenzi SELECT:
INSERT INTO nume_tabel [(col1,col2,...)]
{comanda_SELECT};
Observatii:
- lista de coloane (dacă este precizată) trebuie să se potrivească ca număr şi tip de date cu lista de
expresii;
- în loc de tabel (nume_tabel), insererea se mai poate face si prin intermediul unei vizualizări;
- daca se omit coloane în lista de inserare, acestea primesc valoarea NULL sau valoarea implicită.
- posibile probleme la inserare:
- lipsa de valori pentru coloane NOT NULL,
- nepotrivirea listei de coloane cu cea de expresii,
- valori duplicate ce încalcă o constrângere de unicitate,
- încălcarea vreunei constrângeri de integritate referenţială,
- încălcarea unei constrângeri de tip CHECK,
- nepotrivire de tip de date,
- valoare prea mare pentru coloana respectivă.
- dacă se foloseşte expresia DEFAULT, atunci valoarea inserată este NULL sau valoarea implicită
setată la nivel de tabel.
6. Să se exemplifice câteva din erorile care pot să apară la inserare şi să se observe mesajul returnat de sistem.
- lipsa de valori pentru coloane NOT NULL (coloana department_name este NOT NULL)
INSERT INTO dept_*** (department_id, location_id)
VALUES (200, 2000);
- nepotrivirea listei de coloane cu cea de expresii
2
INSERT INTO dept_***
VALUES (200, 2000);
INSERT INTO dept_*** (department_id, department_name,location_id)
VALUES (200, 2000);
- nepotrivire de tip de date,
INSERT INTO dept_*** (department_id, location_id)
VALUES (‘D23’, 2000);
- valoare prea mare pentru coloană
INSERT INTO dept_*** (department_id, location_id)
VALUES (15000, 2000);
7. Copiaţi în tabelul emp_*** salariaţii (din tabelul employees) al căror comision depăşeşte 25% din salariu.
8. Creaţi tabele emp1_***, emp2_*** şi emp3_*** cu aceeaşi structură ca tabelul employees. Copiaţi din
tabelul employees:
- în tabelul emp1_*** salariaţii care au salariul mai mic decât 6000;
- în tabelul emp2_*** salariaţii care au salariul cuprins între 6000 şi 10000;
- în tabelul emp3_*** salariaţii care au salariul mai mare decât 10000.
Verificaţi rezultatele, apoi ştergeţi toate înregistrările din aceste tabele.

Obs. Clauza ALL determină evaluarea tuturor condiţiilor din clauzele WHEN. Pentru cele a căror valoare
este TRUE, se inserează înregistrarea specificată în opţiunea INTO corespunzătoare.

CREATE TABLE emp1_***


AS SELECT * FROM employees WHERE 1=0;

CREATE TABLE emp2_***


AS SELECT * FROM employees WHERE 1=0;

CREATE TABLE emp3_***


AS SELECT * FROM employees WHERE 1=0;

INSERT ALL
WHEN salary < =6000 THEN
INTO emp1_***
WHEN salary > = 6000 AND salary <= 10000 THEN
INTO emp2_***
ELSE
INTO emp3_***
SELECT * FROM employees;

DELETE FROM emp1_***;


DELETE FROM emp2_***;
DELETE FROM emp3_***;
COMMIT;
9. Să se creeze tabelul emp0_*** cu aceeaşi structură ca tabelul employees. Copiaţi din tabelul employees:
- în tabelul emp0_*** salariaţii care lucrează în departamentul 80;
- în tabelul emp1_*** salariaţii care au salariul mai mic decât 6000;
- în tabelul emp2_*** salariaţii care au salariul cuprins între 6000 şi 10000;
- în tabelul emp3_*** salariaţii care au salariul mai mare decât 10000.
Dacă un salariat se încadrează în tabelul emp0_*** atunci acesta nu va mai fi inserat şi în alt tabel
(tabelul corespunzător salariului său).

3
Obs. Clauza FIRST determină inserarea corespunzătoare primei clauze WHEN a cărei condiţie este evaluată
TRUE. Toate celelalte clauze WHEN sunt ignorate.

Sintaxa simplificată a comenzii DELETE


DELETE FROM nume_tabel
[WHERE conditie];

10. Ştergeţi toate înregistrările din tabelele emp_*** şi dept_***. Inseraţi în aceste tabele toate înregistrările
corespunzătoare din employees, respectiv departments. Permanentizaţi modificările.

11. Ştergeţi angajaţii care nu au comision. Anulaţi modificările.

DELETE FROM emp_***


WHERE commission_pct IS NULL;
ROLLBACK;

12. Eliminaţi departamentele care nu au nici un angajat. Anulaţi modificările.

13. Eliminaţi angajaţii care nu aparţin unui departament valid. Anulaţi modificările.

14. Sintaxa simplificată a comenzii UPDATE:


UPDATE nume_tabel [alias]
SET col1 = expr1[, col2=expr2]
[WHERE conditie];
sau
UPDATE nume_tabel [alias]
SET (col1,col2,...) = (subcerere)
[WHERE conditie];
Observatii:
- de obicei pentru identificarea unei linii se foloseşte o condiţie ce implică cheia primară;
- dacă nu apare clauza WHERE atunci sunt afectate toate liniile tabelului specificat.
15. Măriţi salariul tuturor angajaţilor din tabelul emp_*** cu 5%. Anulaţi modificările.

UPDATE emp_***
SET salary = salary * 1.05;
ROLLBACK;

16. Schimbaţi jobul tuturor salariaţilor din departamentul 80 care au comision în 'SA_REP'. Anulaţi
modificările.

17. Să se promoveze Douglas Grant la manager în departamentul 20, având o creştere de salariu cu 1000$.

18. Să se modifice jobul şi departamentul angajatului având codul 114, astfel încât să fie la fel cu cele ale
angajatului având codul 205.

19. Schimbaţi salariul şi comisionul celui mai prost plătit salariat din firmă, astfel încât să fie egale cu
salariul si comisionul directorului.

UPDATE emp_***
SET (salary, commission_pct) = (SELECT salary, commission_pct
FROM emp_***
4
WHERE manager_id IS NULL)
WHERE salary = (SELECT MIN(salary)
FROM emp_***);
ROLLBACK;

20. Pentru fiecare departament să se mărească salariul celor care au fost angajaţi primii astfel încât să devină
media salariilor din companie.

21. Să se modifice valoarea emailului pentru angajaţii care câştigă cel mai mult în departamentul în care
lucrează astfel încât acesta să devină iniţiala numelui concatenată cu “_” concatenat cu prenumele.
Anulaţi modificările.

5
Limbajul de definire a datelor (DDL). CREATE, ALTER, DROP
Definirea tabelelor
CREATE TABLE [schema.]nume_tabel (
nume_coloana tip_de_date [DEFAULT1 expr], ...);

CREATE TABLE nume_tabel [(col1, col2...)]


AS subcerere;
1. CreaŃi tabelul salariat_*** având următoarea structură:
Nume Caracteristici Tip
cod_ang NOT NULL NUMBER(4)
nume VARCHAR2(25)
prenume VARCHAR2(25)
functia VARCHAR2(20)
sef NUMBER(4)
Valoare implicită data
data_angajarii DATE
curentă
varsta NUMBER(2)
email CHAR(50)
salariu Valoare implicită 0 NUMBER(9,2)

CREATE TABLE salariat_*** (


cod_ang NUMBER(4) NOT NULL,
nume VARCHAR2(25),
prenume VARCHAR2(25),
functia VARCHAR2(20),
sef NUMBER(4),
data_angajarii DATE DEFAULT SYSDATE,
varsta NUMBER(2),
email CHAR(50),
salariu NUMBER(9,2) DEFAULT 0);
2. AfişaŃi structura tabelului creat anterior.
3. Se dau următoarele valori:
COD _ANG NUME PRENUME FUNCTIA SEF DATA_ANG VARSTA EMAIL SALARIU
1 ..... ..... director null ........ 30 ..... 5500
2 ..... ..... functionar 1 ......... 25 ..... 0
3 ..... ...... economist 1 ......... 45 ..... 3000
4 ..... .... functionar 1 ......... 35 ...... 1000
4. InseraŃi în tabelul salariat_*** prima înregistrare din tabelul de mai sus fără să precizaŃi lista de coloane
în comanda INSERT.
5. InseraŃi a doua înregistrare folosind o listă de coloane din care excludeŃi data_angajarii şi salariul care au
valori implicite. ObservaŃi apoi rezultatul.
6. InseraŃi înregistrările 3 şi 4.
7. CreaŃi tabelul functionar_*** care să conŃină funcŃionarii din tabelul salariat_***, având următoarele
coloane: codul, numele, salariul anual şi data angajării. VerificaŃi cum a fost creat tabelul şi ce date
conŃine.

1
Modificarea tabelelor

8. AdăugaŃi o nouă coloană tabelului salariat_*** care să conŃină data naşterii.

ALTER TABLE salariat_***


ADD (datan DATE);

9. ModificaŃi dimensiunea coloanei nume la 30 si pe cea a salariului la 12 cu 3 zecimale.

ALTER TABLE salariat_***


MODIFY (nume VARCHAR2(30), salariu NUMBER(12,3));

10. ModificaŃi tipul coloanei email la VARCHAR2.


11. ModificaŃi valoarea implicită a coloanei data_angajarii la data sistemului + o zi.
12. EliminaŃi coloana varsta din tabelul salariat_***.

ALTER TABLE salariat_***


DROP COLUMN varsta;

Redenumirea şi eliminarea tabelelor

RENAME nume_tabel TO nume_nou;


DROP TABLE nume_tabel;
13. RedenumiŃi tabelul functionar_*** cu funct_***.
14. RecreaŃi tabelul functionar_*** utilizând tabelul funct_***..
15. EliminaŃi tabelul funct_***.

Constrângeri

Adăugarea constrângerilor la crearea tabelului (CREATE TABLE)


CREATE TABLE [schema.]nume_tabel (
nume_coloana tip_de_date [DEFAULT expr]
[constrangere_de_coloana], ...
..[constrangere la nivel de tabel])
16. ŞtergeŃi şi apoi creaŃi din nou tabelul salariat_*** cu următoarea structură.
NUME TIP CONSTRÂNGERE
cod_ang NUMBER(4) Cheie primară
nume VARCHAR2(25) NOT NULL
prenume VARCHAR2(25)
data_nasterii DATE data_nasterii<data_angajarii
functia VARCHAR2(9) NOT NULL
Referă ca şi cheie externă cod_ang din acelaşi
sef NUMBER(4)
tabel
data_angajarii DATE
email VARCHAR2(20) unic
salariu NUMBER(12,3) >0
cod_dept NUMBER(4) CombinaŃia NUME + PRENUME să fie unică

2
ObservaŃie:
Constrângerile de tip CHECK se pot implementa la nivel de coloană doar dacă nu referă o altă coloană a
tabelului.

DROP TABLE salariat_***;


CREATE TABLE salariat_*** (
cod_ang NUMBER(4) PRIMARY KEY,
nume VARCHAR2(25) NOT NULL,
prenume VARCHAR2(25),
data_nasterii DATE,
functia VARCHAR2(9) NOT NULL,
sef NUMBER(4) REFERENCES salariat_*** (cod_ang),
data_angajarii DATE DEFAULT SYSDATE,
email VARCHAR2(20) UNIQUE,
salariu NUMBER(9,2) CHECK (salariu > 0),
cod_dep NUMBER(4),
CONSTRAINT const_c_*** CHECK (data_angajarii > data_nasterii),
CONSTRAINT const_u_*** UNIQUE (nume,prenume,data_nasterii));
17. ŞtergeŃi tabelul salariat_***, iar apoi recreaŃi-l implementând toate constrângerile la nivel de tabel.
ObservaŃie: Constrângerea de tip NOT NULL se poate declara doar la nivel de coloană.

DROP TABLE salariat_***;


CREATE TABLE salariat_*** (
cod_ang NUMBER(4),
nume VARCHAR2(25) NOT NULL,
prenume VARCHAR2(25),
data_nasterii DATE,
functia VARCHAR2(9) NOT NULL,
sef NUMBER(4),
data_angajarii DATE DEFAULT SYSDATE,
email VARCHAR2(20),
salariu NUMBER(9,2),
cod_dep NUMBER(4),
CONSTRAINT ccp_*** PRIMARY KEY (cod_ang),
CONSTRAINT cce_*** FOREIGN KEY (sef) REFERENCES salariat_*** (cod_ang),
CONSTRAINT cu1_*** UNIQUE (email),
CONSTRAINT cc1_*** CHECK (data_angajarii > data_nasterii),
CONSTRAINT cc2_***CHECK (salariu > 0),
CONSTRAINT cu2_*** UNIQUE (nume,prenume,data_nasterii));
18. CreaŃi tabelul departament_*** care să aibă următoarea structură.

NUME TIP CONSTRÂNGERI


COD_DEP NUMBER(4) Cheie primară
NUME VARCHAR2(20) Not null
ORAS VARCHAR2(25)

Adăugarea constrângerilor ulterior creării tabelului, eliminarea, activarea sau dezactivarea


constrângerilor (ALTER TABLE)

- adaugă constrângeri
ALTER TABLE nume_tabel
ADD [CONSTRAINT nume_constr] tip_constr (coloana);
- elimină constrângeri
ALTER TABLE nume_tabel
3
DROP [CONSTRAINT nume_constr] tip_constr (coloana);
- activare/dezactivare constrângere
ALTER TABLE nume_tabel
MODIFY CONSTRAINT nume_constr ENABLE|DISABLE;
sau
ALTER TABLE nume_tabel
ENABLE| DISABLE nume_constr;
19. InseraŃi o nouă înregistrare în salariat_*** de forma:
cod_dep
cod nume prenume data_n functia sef data_ang email salariu
11-JUN-
2 N2 P2 economist 1 Sysdate E2 2000 10
1960
Ce observaŃi? IntroduceŃi înregistrarea dar specificând valoarea NULL pentru coloana sef.
20. ÎncercaŃi să adăugaŃi o constrângere de cheie externă pe cod_dep din salariat_***. Ce observaŃi?

ALTER TABLE salariat_***


ADD CONSTRAINT cce2_*** FOREIGN KEY (cod_dep) REFERENCES departament_***
(cod_dep);

21. InseraŃi o nouă înregistrare în departament_***. Apoi adăugaŃi constrângerea de cheie externă definită
anterior.

cod_dep nume loc


10 Economic Bucuresti

22. InseraŃi noi înregistrări în salariat_***, respectiv în departament_***. Care trebuie să fie ordinea de
inserare?
cod_dep
cod nume prenume data_n functia sef data_ang email salariu
11-JUN- 20
3 N3 P3 jurist 2 Sysdate E3 2500
1967

cod_dep nume loc


20 Juritic Constanta

23. ŞtergeŃi departamentul 20 din tabelul departament_***. Ce observaŃi?


24. ŞtergeŃi constrângerea cce2_***. RecreaŃi această constrângere adăugând opŃiunea ON DELETE
CASCADE.
25. ŞtergeŃi departamentul 20 din tabelul departament_***. Ce observaŃi în tabelul salariat_***? AnulaŃi
modificările.
26. ŞtergeŃi constrângerea cce2_***. RecreaŃi această constrângere adăugând opŃiunea ON DELETE SET
NULL.
27. ÎncercaŃi să ştergeŃi departamentul 10 din tabelul departament_***. Ce observaŃi?

Consultarea dicŃionarului datelor

InformaŃii despre tabelele create se găsesc în vizualizările :


• USER_TABLES – informaŃii complete despre tabelele utilizatorului curent.
4
• ALL_TABLES – informaŃii complete despre tabelele tuturor utilizatorilor.
• COLS – informaŃii despre coloane.
• TAB – informaŃii de bază despre tabelele existente în schema utilizatorului curent.

InformaŃii despre constrângeri găsim în :


• USER_CONSTRAINTS – informaŃii despre constrângerile definite de utilizatorul curent;
• ALL_CONSTRAINTS – informaŃii despre cosntrângerile definite de toŃi utilizatorii.

Definirea vizualizărilor
Sintaxa simplificată a comenzii CREATE VIEW este:
CREATE [OR REPLACE] [FORCE | NOFORCE] VIEW nume_view [(alias, alias, ..)]
AS subcerere
[WITH CHECK OPTION [CONSTRAINT nume_constr]]
[WITH READ ONLY [CONSTRAINT nume_constr]];
- FORCE permite crearea vizualizarea înainte de a defini tabelele de bază;
- subcererea poate fi oricât de complexă dar nu poate conŃine clauza ORDER BY;
- WITH CHECK OPTION permite inserarea şi modificarea prin intermediul vizualizării numai a
liniilor ce sunt accesibile vizualizării; dacă lipseşte numele constrângerii atunci sistemul asociază
un nume implicit de tip SYS_Cn acestei constrângeri;
- WITH READ ONLY asigură că prin intermediul vizualizării nu se pot executa operaŃii LMD.
Eliminarea unei vizualizări se face prin comanda DROP VIEW :
DROP VIEW nume_viz;

1. Să se creeze vizualizarea v_emp_*** care să conŃină codul şi numele salariaŃilor din tabelul emp_***. Să
se afişeze conŃinutul acesteia. Să se insereze o nouă înregistrare în această vizualizare. Ce observaŃi? Să
se şteargă vizualizarea v_emp_***.

CREATE VIEW v_emp_*** (cod, nume)


AS
SELECT employee_id, last_name
FROM emp_***;

INSERT INTO v_emp_***


VALUES (400,’N1’);

DROP VIEW v_emp_***;

2. Să se creeze vizualizarea v_emp_*** care să conŃină codul, numele, emailul, data angajării, salariul şi
codul jobului salariaŃilor din tabelul emp_***. Să se analizeze structura şi conŃinutul vizualizării. Să se
insereze o nouă înregistrare în această vizualizare. Să se verifice că noua înregistrare a fost inserată şi în
tabelul de bază.

CREATE VIEW v_emp_***


AS
SELECT employee_id, last_name, email, hire_date, salary,job_id
FROM emp_***;

DESC v_emp_***

SELECT * FROM v_emp_***;

5
INSERT INTO v_emp_***
VALUES (400,’N1’,’E1’,SYSDATE,5000,’SA_REP’);

SELECT employee_id, last_name, email, hire_date, salary, job_id


FROM emp_***;
3. Să se mărească cu 1000 salariul angajatului având codul 400 din vizualizarea creată anterior. Ce efect va
avea această acŃiune asupra tabelului de bază?
4. Să se şteargă angajatul având codul 400 din vizualizarea creată anterior. Ce efect va avea această acŃiune
asupra tabelului de bază?

5. a) Să se creeze vizualizarea v_emp_dept_*** care să conŃină employee_id, last_name, hire_date,


job_id, department_id din tabelul emp_*** şi coloana department_name din tabelul dept_***.
b) Să încerce inserarea înregistrării (500, 'N2', 'E2',SYSDATE,’SA_REP’,30, 'Administrativ') în
vizualizarea creată anterior.
c) Care dintre coloanele vizualizării v_emp_dept_*** sunt actualizabile?

SELECT column_name, updatable


FROM user_updatable_columns
WHERE UPPER(table_name) = UPPER('v_emp_dept_***');

d) AdăugaŃi tabelului emp_*** constrângerea de cheie externă care referă tabelul dept_***, apoi
verificaŃi ce coloane din vizualizarea v_emp_dept_*** sunt actualizabile.
e) RecreaŃi vizualizarea v_emp_dept_***, apoi verificaŃi ce coloane sunt actualizabile.
f) InseraŃi o linie prin intermediul acestei vizualizări.
Obs. Tabelul ale cărui coloane sunt actualizabile este protejat prin cheie.
g) Ce efect are o operaŃie de ştergere prin intermediul vizualizării v_emp_dept_***? ComentaŃi.

6. Să se creeze vizualizarea v_dept_*** care să conŃine codul şi numele departamentului, numărul de


angajaŃi din departamentul respectiv şi suma alocată pentru plata salariilor. Această vizualizare permite
actualizări?

CREATE VIEW v_dept_*** (cod, nume, nr_angajati, val_salarii)


AS
SELECT e.department_id, department_name, COUNT(*) nr_angajati,
SUM(salary) val_salarii
FROM emp_*** e, dept_*** d
WHERE e.department_id = d.department_id
GROUP BY e.department_id, department_name;

7. a) Să se creeze vizualizarea v_emp30_*** care să conŃină numele, emailul, data angajării, salariul,
codul jobului şi codul departamentului celor care lucrează în departamentul 30. În această
vizualizare nu se va permite modificarea sau inserarea liniilor ce nu sunt accesibile ei. DaŃi un nume
constrângerii.

CREATE VIEW v_emp30_*** AS


SELECT employee_id, last_name, email, hire_date, salary, job_id,
department_id
FROM emp_***
WHERE department_id=30
WITH CHECK OPTION CONSTRAINT ck_option1_***;

b) Să se listeze structura şi conŃinutul vizualizării v_emp30_***.

6
c) Să se încerce prin intermediul vizualizării inserarea unui angajat în departamentul 10 şi a unui angajat
în departamentul 30.
d) Să se încerce prin intermediul vizualizării modificarea departamentului unui angajat.

UPDATE v_emp30_***
SET department_id =20
WHERE employee_id = 11;
8. Să se creeze o vizualizare (v_dept_***) asupra tabelului dept_*** să nu permită efectuarea nici unei
operaŃii LMD. TestaŃi operaŃiile de inserare, modificare şi ştergere asupra acestei vizualizări.

CREATE VIEW v_dept_*** AS


SELECT *
FROM dept_***
WITH READ ONLY;

9. Să se consulte informaŃii despre vizualizarea v_dept_***. FolosiŃi vizualizarea dicŃionarului datelor


USER_VIEWS (coloanele VIEW_NAME şi TEXT).

Obs: Coloana TEXT este de tip LONG. În cazul selectării unei coloane de tip LONG trebuie utilizată
comanda SET LONG n pentru a seta numărul de caractere afişate.

SET LONG 200

SELECT view_name, text


FROM user_views
WHERE UPPER(view_name)=UPPER(’v_dept_***’);

Definirea secvenŃelor
Sintaxa comenzii CREATE SEQUENCE este:
CREATE SEQUENCE nume_secvenŃă
[INCREMENT BY n]
[START WITH valoare_start]
[ {MAXVALUE valoare_maximă | NOMAXVALUE} ]
[ {MINVALUE valoare_minimă | NOMINVALUE} ]
[ {CYCLE | NOCYCLE} ]
[ {CACHE n | NOCACHE} ];
Ştergerea secvenŃelor se realizează cu ajutorul comenzii DROP SEQUENCE.
DROP SEQUENCE nume_secv;
10. Să se creeze o secvenŃă care are pasul de incrementare 10 şi începe de la 10, are ca valoare maximă
10000 şi nu ciclează.

CREATE SEQUENCE sec_***


INCREMENT BY 10
START WITH 10
MAXVALUE 10000
NOCYCLE;
11. Să se modifice toate liniile din tabelul emp_***, regenerând codul angajaŃilor astfel încât să utilizeze
secvenŃa sec_emp***. Să se anuleze modificările.

7
UPDATE emp_***
SET employee_id = sec_emp***.NEXTVAL;
ROLLBACK;

12. Să se introducă un nou salariat în tabelul emp_*** folosindu-se pentru codul salariatului secvenŃa creată.
13. Să se afişeze valoarea curentă a secvenŃei.

SELECT sec_***.CURRVAL valoare


FROM DUAL;

ExerciŃiu
a) CreaŃi o secvenŃă pentru generarea codurilor de departamente, seq_dept_***. SecvenŃa va începe de
la 200, va creşte cu 10 la fiecare pas şi va avea valoarea maximă 20000, nu va cicla.
b) Să se selecteze informaŃii despre secvenŃele utilizatorului curent (nume, valoare minimă, maximă,
de incrementare, ultimul număr generat). Se va utiliza vizualizarea user_sequences.
c) Să se insereze o înregistrare nouă în DEPT_*** utilizând secvenŃa creată.
d) Să se selecteze valoarea curentă a secvenŃei.
e) Să se şteargă secvenŃa.

Definirea indecşilor
Sintaxa comenzii CREATE INDEX:
CREATE [UNIQUE] INDEX nume_index
ON tabel (coloana1 [, coloana2…]);
Modificarea unui index se face prin comanda ALTER INDEX.
Eliminarea unui index se face prin comanda: DROP INDEX nume_index;

14. Să se creeze un index neunic, emp_last_name_idx_***, asupra coloanei last_name din tabelul
emp_***.
15. Să se creeze indecşi unici asupra codului angajatului (employee_id) şi asupra combinaŃiei last_name,
first_name, hire_date.
16. CreaŃi un index neunic asupra coloanei department_id din emp_*** pentru a eficientiza joinurile
dintre acest tabel şi dept_***.

Definirea sinonimelor
Comanda pentru crearea sinonimelor este:
CREATE [PUBLIC] SYNONYM nume_sinonim
FOR obiect;
Eliminarea sinonimelor se face prin comanda DROP SYNONYM nume_sinonim;
17. CreaŃi un sinonim public se_*** pentru tabelul emp_***.
18. CreaŃi un sinonim pentru vizualizarea v_dept_***.
19. Utilizând sinonimele create anterior, afişaŃi informaŃii depre salariŃi şi despre departamente.

8
Limbajul de interogare al datelor (DQL). SELECT
CERERI MONOTABEL

1. AnalizaŃi sintaxa simplificată a comenzii SELECT. Care dintre clauze sunt obligatorii?
SELECT { [ {DISTINCT | UNIQUE} | ALL] lista_campuri | *}
FROM [nume_schemă.]nume_obiect ]
[, [nume_schemă.]nume_obiect …]
[WHERE condiŃie_clauza_where]
[GROUP BY expresie [, expresie …]
[HAVING condiŃie_clauza_having] ]
[ORDER BY {expresie | poziŃie} [, {expresie | poziŃie} …] ]

2. Să se listeze structura tabelelor din schema HR (EMPLOYEES, DEPARTMENTS, JOB_HISTORY,


JOBS, LOCATIONS, COUNTRIES, REGIONS), observând tipurile de date ale coloanelor.
Obs: Se va utiliza comanda SQL*Plus
DESCRIBE nume_tabel
3. Să se listeze conŃinutul tabelelor din schema considerată, afişând valorile tuturor câmpurilor.
Obs: Se va utiliza comanda SQL
SELECT * FROM nume_tabel;
4. Să se obŃină încă o dată rezultatul cererii precedente, fără a rescrie cererea.
Obs: Ultima comandă SQL lansată de către client este păstrată în buffer-ul SQL.
Pentru rularea acesteia se utilizează “/” sau RUN.
5. ListaŃi structura tabelului EMPLOYEES şi apoi daŃi comanda RUN (sau “/”). Ce observaŃi? Comenzile
SQL*Plus sunt păstrate în buffer?
DESC employees
RUN
6. Să se afişeze codul angajatului, numele, codul job-ului, data angajării. SalvaŃi instrucŃiunea SQL într-un
fişier numit p1_14.sql.
Obs: Pentru salvarea ultimei comenzi SQL se utilizează comanda SAVE. Precizarea extensiei „.sql” a
fişierului nu este obligatorie.
SELECT employee_id, last_name, job_id, hire_date
FROM employees;
SAVE z:\…\ p1_14.sql
7. ReexecutaŃi cererea folosind fişierul p1_14.sql.
START z:\…\ p1_14.sql
sau
@ z:\…\ p1_14.sql
8. EditaŃi fişierul p1_14.sql, adăugând coloanelor câte un alias (cod, nume, cod job, data angajarii).
EDIT z:\…\ p1_14.sql
9. Să se listeze, cu şi fără duplicate, codurile job-urilor din tabelul EMPLOYEES.
Obs. DISTINCT = UNIQUE
10. Să se afişeze numele concatenat cu prenumele, separate prin spaŃiu. EtichetaŃi coloana “Nume si
prenume”.

9
Obs: Operatorul de concatenare este “||”. Şirurile de caractere se specifică între apostrofuri (NU
ghilimele, caz în care ar fi interpretate ca alias-uri).
SELECT last_name|| ' ' || first_name " Nume si prenume "
FROM employees;
11. Să se listeze numele şi salariul angajaŃilor care câştigă mai mult de 10000 $.
SELECT last_name, salary
FROM employees
WHERE salary > 10000;
12. Să se modifice cererea anterioară astfel încât să afişeze numele şi salariul pentru toŃi angajaŃii al căror
salariu este cuprins între 5000$ şi10000$.
Obs: Pentru testarea apartenenŃei la un domeniu de valori se poate utiliza operatorul
[NOT] BETWEEN valoare1 AND valoare2
SELECT last_name, salary
FROM employees
WHERE salary BETWEEN 5000 AND 10000;
13. Să se creeze o cerere pentru a afişa numele angajatului şi numărul departamentului pentru angajatul 104.
14. Să se afişeze numele şi salariul pentru toŃi angajaŃii din departamentele 10 sau 30, în ordine alfabetică a
numelor.
Obs: ApartenenŃa la o mulŃime finită de valori se poate testa prin intermediul operatorului IN, urmat de
lista valorilor între paranteze şi separate prin virgule:
expresie IN (valoare_1, valoare_2, …, valoare_n)
15. Să listeze numele şi salariile angajaŃilor care câştigă mai mult de 10000 $ şi lucrează în departamentul
10 sau 30. Se vor eticheta coloanele drept Angajat si Salariu lunar.
16. Care este data curentă?
Obs: Pseudocoloana care returnează data curentă este SYSDATE. Pentru completarea sintaxei obligatorii
a comenzii SELECT, se utilizează tabelul DUAL:
SELECT SYSDATE
FROM dual;
Datele calendaristice pot fi formatate cu ajutorul funcŃiei TO_CHAR(data, format), unde formatul poate
fi alcătuit dintr-o combinaŃie a următoarelor elemente:
Element SemnificaŃie
D Numărul zilei din săptămână (duminică=1;
luni=2; …sâmbătă=6).
DD Numărul zilei din lună.
DDD Numărul zilei din an.
DY Numele zilei din săptămână, printr-o
abreviere de 3 litere (MON, THU etc.)
DAY Numele zilei din săptămână, scris în
întregime.
MM Numărul lunii din an.
MON Numele lunii din an, printr-o abreviere de 3
litere (JAN, FEB etc.)
MONTH Numele lunii din an, scris în întregime.
Y Ultima cifră din an
YY, YYY, YYYY Ultimele 2, 3, respectiv 4 cifre din an.
YEAR Anul, scris în litere (ex: two thousand
four).
HH12, HH24 Orele din zi, între 0-12, respectiv 0-24.
10
MI Minutele din oră.
SS Secundele din minut.
SSSSS Secundele trecute de la miezul nopŃii.

17. Să se afişeze numele şi data angajării pentru fiecare salariat care a fost angajat în 1987. Se cer 2 soluŃii:
una în care se lucrează cu formatul implicit al datei şi alta prin care se formatează data.
Varianta1:
SELECT first_name, last_name, hire_date
FROM employees
WHERE hire_date LIKE (‘%87’);
Varianta 2:
SELECT first_name, last_name, hire_date
FROM employees
WHERE TO_CHAR(hire_date, ‘YYYY’)=’1987’;
Sunt obligatorii ghilimelele de la şirul ‘1987’? Ce observaŃi?
18. Să se afişeze numele şi job-ul pentru toŃi angajaŃii care nu au manager.
SELECT last_name, job_id
FROM employees
WHERE manager_id IS NULL;
19. Să se afişeze numele, salariul şi comisionul pentru toŃi salariaŃii care câştigă comisioane. Să se sorteze
datele în ordine descrescătoare a salariilor, iar pentru cei care au acelaşi salariu în ordine crescătoare a
comisioanelor.
SELECT last_name, salary, commission_pct
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC, commission_pct ASC;
20. Să se listeze numele tuturor angajaŃilor care au a treia litera din nume 'a'.
Obs: Pentru a forma măştile de caractere utilizate împreună cu operatorul LIKE cu scopul de a compara
şirurile de caractere, se utilizează:
% - reprezentând orice şir de caractere, inclusiv şirul vid;
_ (underscore) – reprezentând un singur caracter.
SELECT DISTINCT last_name
FROM employees
WHERE last_name LIKE '__a%';

21. Folosind data curentă să se afişeze următoarele informaŃii:


- numele zilei, numărul zilei din săptămână, numărul zilei din luna, respectiv numărul zilei din an;
- numărul lunii din an, numele lunii cu abreviere la 3 caractere, respectiv numele complet al lunii;
- ora curentă (ora, minute, secunde).
22. Să se listeze numele departamentelor care funcŃionează în locaŃia având codul 1700 şi al căror manager
este cunoscut.
23. Să se afişeze codurile departamentelor în care lucrează salariaŃi.
24. Să se afişeze numele şi prenumele salariaŃilor angajaŃi în luna mai 1987.
25. Să se listeze codurile angajaŃilor care au avut şi alte joburi faŃă de cel prezent. Să se ordoneze rezultatul
descrescător după codul angajatului.
26. Să se afişeze numele şi data angajării pentru cei care lucrează în departamentul 80 şi au fost angajaŃi în
luna martie a anului 1997.
27. Să se afişeze numele joburilor care permit un salariu cuprins între 8300$ şi 14000$.
11
28. Care este grila de salarizare pentru un salariu de 10000$?
29. Să se listeze numele tuturor angajaŃilor care au 2 litere 'L' în nume şi lucrează în departamentul 30 sau
managerul lor este 123.
30. Să se afişeze numele, job-ul şi salariul pentru toŃi salariaŃii al căror job conŃine şirul 'CLERK' sau 'REP'
şi salariul nu este egal cu 1000, 2000 sau 3000 $.
31. Să se afişeze numele, salariul şi comisionul pentru toŃi angajaŃii al căror salariu este mai mare decât de 5
ori valoarea comisionului (salary*commission_pct*5).

FUNCłII SQL (single-row)

Principalele funcŃii SQL pot fi clasificate în următoarele categorii:


• FuncŃii single-row
• FuncŃii multiple-row (funcŃii agregat)
FuncŃiile single-row returnează câte o linie rezultat pentru fiecare linie a tabelului sau vizualizării
interogate. Aceste funcŃii pot apărea în listele SELECT, clauzele WHERE, START WITH, CONNECT BY
şi HAVING.

1. AnalizaŃi următoarele funcŃii pentru prelucrarea şirurilor de caractere:


FuncŃie SemnificaŃie Exemplu
Converteşte un şir de caractere
LOWER (expresie) LOWER ('AbCdE') = 'abcde'
la minuscule.
Converteşte un şir de caractere
UPPER (expresie) UPPER ('AbCdE') = 'ABCDE'
la majuscule.
Converteşte un şir de caractere
la un şir care începe cu
INITCAP (expresie) INITCAP ('AbCdE') = 'Abcde'
majusculă şi continuă cu
minuscule.
Extrage din expresia de tip şir
de caractere, n caractere
începând cu poziŃia m. Dacă
SUBSTR ('AbCdE', 2, 2) = 'bC'
lipseşte argumentul n, atunci
SUBSTR ('AbCdE', 2) = 'bCdE'
SUBSTR (expresie, m[, n]) extrage toate caracterele până la
SUBSTR ('AbCdE', -3,2) = 'Cd'
sfârşitul şirului. Dacă m este
SUBSTR ('AbCdE', -3) = 'CdE'
negativ numărătoarea poziŃiilor
începe de la sfârşitul şirului de
caractere spre început.
Returnează numărul de
LENGTH (expresie) LENGTH ('AbCdE') = 5
caractere al expresiei.
Returnează poziŃia la care se
găseşte a n-a ocurentă a
INSTR (LOWER('AbC aBcDe'), 'ab', 5, 2)
expresiei 'expr1' în cadrul
=0
INSTR (expresie, expr1[, m][, n]) expresiei 'expresie', căutarea
INSTR (LOWER('AbCdE aBcDe'), 'ab', 5)
începând de la poziŃia m. Daca
=7
m sau n lipsesc, valorile
implicite sunt 1 pentru ambele.
Reversul funcŃiilor LPAD,
RPAD. Trunchează expresia
RTRIM ('abcdeXXXX', 'X')
LTRIM (expresie[, expr1]) sau caracter la stânga sau la dreapta
= 'abcde'
RTRIM (expresie[, expr1]) prin eliminarea succesivă a
LTRIM (' abcde') = 'abcde'
caracterelor din expresia expr1.
Implicit, daca lipseşte, expr1
12
este ' ' un spaŃiu.
TRIM (LEADING 'X' FROM
Permite eliminarea caracterelor 'XXXabcdeXXX') = 'abcdeXXX'
TRIM (LEADING | TRAILING | specificate (caractere_trim) de TRIM (TRAILING 'X' FROM
BOTH caractere_trim FROM la începutul (leading) , sfârşitul 'XXXabcdeXXX') = 'XXXabcde'
expresie) (trailing) sau din ambele părŃi,
TRIM ( BOTH 'X' FROM
dintr-o expresie caracter data. 'XXXabcdeXXX') = 'abcde'

TRIM (' abcde ') = 'abcde'

2. Să se afişeze pentru fiecare angajat din departamentul 20 un şir de caractere de forma "FuncŃia
salariatului {prenume} {nume} este {cod functie}". Să se afişeze prenumele cu iniŃiala litera mare, iar
numele cu litere mari (Stephen KING), iar codul funcŃiei să se afişeze cu litere mici.
3. Să se afişeze pentru angajatul cu numele 'HIGGINS' codul, numele şi codul departamentului. Cum se
scrie condiŃia din WHERE astfel încât să existe siguranŃa ca angajatul 'HIGGINS' va fi găsit oricum ar fi
fost introdus numele acestuia? Căutarea trebuie să nu fie case-sensitive, iar eventualele blank-uri care
preced sau urmează numelui trebuie ignorate.
UPPER(TRIM(last_name))='HIGGINS';
4. Să se afişeze pentru toŃi angajaŃii al căror nume se termină în 'n', codul, numele, lungimea numelui şi
poziŃia din nume în care apare prima data litera 'a'. AsociaŃi aliasuri coloanelor returnate de cerere.
SELECT employee_id, last_name, LENGTH(last_name), INSTR(UPPER(last_name), 'A')
FROM employees
WHERE SUBSTR(last_name,-1)='n';
5. AnalizaŃi următoarele funcŃii aritmetice:
FuncŃie SemnificaŃie Exemplu
ROUND(1.6) = 2
Returnează valoarea rotunjită a expresiei
ROUND(1.4) = 1
până la n zecimale. Daca n este negativ sunt
ROUND (expresie [, n]) ROUND (1234.56,1) = 1234.6
rotunjite cifre din stânga virgulei. Valoarea
ROUND (1230.56, -2) = 1200
implicită pentru n este 0.
ROUND (1260.56, -2) = 1300

MOD (11, 4) = MOD (11, -4) = 3


MOD (m,n) Returnează restul împărŃirii lui m la n.
MOD(-11, 4) = MOD (-11, -4) = -3

6. Să se afişeze detalii despre salariaŃii care au lucrat un număr întreg de săptămâni până la data curentă.
MOD(ROUND(SYSDATE – hire_date), 7)=0;
7. Să se afişeze numele, salariul şi numărul de mii al salariului rotunjit la 2 zecimale pentru cei care nu au
salariul divizibil cu 1000.
8. AnalizaŃi următoarele operaŃii pe expresii de tip dată calendaristică:
Tipul de date al
OperaŃie Descriere
rezultatului
date -/+ number Date Scade/Adaugă un număr de zile dintr-o / la o dată.
date1 - date2 Number Întoarce numărul de zile dintre două date calendaristice.
date +/-
Date Scade/Adaugă un număr de ore la o / dintr-o dată calendaristică.
number/24
9. Să se afişeze data (luna, ziua, ora, minutul si secunda) de peste 10 zile.
13
SYSDATE+10
10. Să se afişeze numărul de zile rămase până la sfârşitul anului.
ROUND(TO_DATE(’31-DEC-2009’)-SYSDATE)
11. a. Să se afişeze data de peste 12 ore.
SYSDATE+12/24
b. Să se afişeze data de peste 5 minute.
SYSDATE+1/288
12. AnalizaŃi următoarele funcŃii pentru prelucrarea datelor calendaristice:
FuncŃie SemnificaŃie Exemplu
SYSDATE Întoarce data şi timpul curent
Returnează numărul de luni dintre
data date1 şi data date2. Rezultatul
MONTHS_BETWEEN poate fi pozitiv sau negativ după cum ROUND(MONTHS_BETWEEN
(date1, date2) date1 este mai recentă sau nu faŃă de (SYSDATE + 31, SYSDATE)) = 1
date2. Zecimalele reprezintă parŃi
dintr-o luna!
Adaugă n luni la o data specificată. MONTHS_BETWEEN
ADD_MONTHS (date, n) Valoarea n trebuie să fie întreagă (ADD_MONTHS(SYSDATE, 3),
(pozitivă sau negativă). SYSDATE) = 3

NEXT_DAY('15-dec-2006','Monday')
Returnează data corespunzătoare
= '18-dec-2006'
NEXT_DAY (date, char) primei zile a săptămânii specificate
NEXT_DAY ('15-dec-2006',1)
(char) care urmează după date.
= '18-dec-2006'

13. Să se afişeze numele angajatului, data angajării şi data negocierii salariului, care a avut loc în prima zi de
Luni, după 6 luni de serviciu. EtichetaŃi această coloană “Negociere”.
NEXT_DAY(ADD_MONTHS(hire_date, 6), ‘Monday’)
14. Pentru fiecare angajat să se afişeze numele şi numărul de luni de la data angajării. EtichetaŃi coloana
“Luni lucrate”. Să se ordoneze rezultatul după numărul de luni lucrate. Se va rotunji numărul de luni la
cel mai apropiat număr întreg.
SELECT last_name, ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)) “Luni lucrate”
FROM employees
ORDER BY MONTHS_BETWEEN(SYSDATE, hire_date);

SELECT last_name, ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)) “Luni lucrate”


FROM employees
ORDER BY “Luni lucrate”;

SELECT last_name, ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)) “Luni lucrate”


FROM employees
ORDER BY 2;
15. AnalizaŃi următoarele funcŃii de conversie:
Obs. Conversiile implicite asigurate de server-ul Oracle sunt:
• de la VARCHAR2 sau CHAR la NUMBER;
• de la VARCHAR2 sau CHAR la DATE;
• de la NUMBER la VARCHAR2 sau CHAR;
• de la DATE la VARCHAR2 sau CHAR.
14
SELECT last_name
FROM employees
WHERE TO_CHAR(hire_date,'yyyy')=1994;
SELECT last_name
FROM employees
WHERE hire_date='07-JUN-1994';
SELECT employee_id||' '||last_name||' '||hire_date
FROM employees
WHERE department_id=10;
Conversiile explicite se realizează cu ajutorul funcŃiilor de tip TO_{tip}
FuncŃie SemnificaŃie Exemplu
Converteşte o valoare de tip numeric sau dată TO_CHAR('3') = ' 3'
calendaristică, la un şir de caractere conform cu TO_CHAR(-12) = '-12'
TO_CHAR
formatul specificat sau cu setările naŃionale TO_CHAR(sysdate, 'DDMMYYYY')
(expr_number_sau
specificate (NLS - National Language Support).
_date[, format][, = ' 09122004'
Daca formatul sau parametrii lipsesc se
nlsparameters]) TO_CHAR (sysdate + 365 * 57,
utilizează formatul şi parametrii impliciŃi.
Formatul este case sensitive. 'ddmmyyyy') = ' 25112061'
TO_NUMBER Converteşte o valoare de tip şir de caractere la o
(expr_char[, valoare numerică conform cu formatul TO_NUMBER ('-12.22', 'S99.99')
format][, specificat. Dacă formatul sau parametrii lipsesc = -12.22
nlsparameters]) se utilizează formatul şi parametrii impliciŃi.
Converteşte o valoare de tip şir de caractere la o
TO_DATE
valoare de tip dată calendaristică în
(expr_char[, TO_DATE ('15-feb-2006','dd-mon-
conformitate cu formatul specificat. Dacă
format][, yyyy')
formatul sau parametrii lipsesc se utilizează
nlsparameters])
formatul şi parametrii impliciŃi.
16. Să se afişeze numele şi prenumele pentru toŃi angajaŃii care s-au angajat în luna mai.
17. AnalizaŃi următoarele funcŃii SQL:
FuncŃie SemnificaŃie Exemplu
Returnează expr1 dacă aceasta nu este NVL(NULL, 1) = 1
NULL, expr2 în caz contrar. Cele 2 expresii NVL(2, 1) = 2
NVL (expr1, expr2) trebuie să aibă acelaşi tip sau expr2 să NVL('c', 1) = 'c' -- face conversie
permită conversia implicită la tipul NVL(1, 'c') -- eroare
expresiei expr1. --nu face conversie
Dacă expr1 este nenulă atunci returnează NVL2 (1, 2, 3) = 2
NVL2 (expr1, expr2, expr3)
expr2, altfel Returnează expr3 NVL2 (NULL, 2, 3) = 3
18. Să se afişeze numele angajaŃilor şi comisionul. Dacă un angajat nu câştigă comision, să se scrie “Fara
comision”. EtichetaŃi coloana “Comision”.
NVL(TO_CHAR(commission_pct), ‘Fara comision’)
19. Să se listeze numele, salariul şi comisionul tuturor angajaŃilor al căror venit lunar depăşeşte 10000$.
salary * NVL(commission_pct, 0) venit_lunar

20. AnalizaŃi expresia CASE şi funcŃia DECODE:

FuncŃie/Expresie SemnificaŃie Exemplu


CASE expr WHEN expr_bool1 În funcŃie de valoarea unei expresii
THEN return_expr1 returnează valoarea primei perechi
15
[WHEN expr_bool2 THEN WHEN .. THEN care se potriveşte sau
return_expr2 dacă nu se potriveşte nici una expresia
... din ELSE. Nu se poate specifica NULL
WHEN expr_booln THEN pentru toate expresiile de returnat.
return_exprn ] (return_expri). Toate expresiile trebuie
[ELSE return_expr] sa aibă acelaşi tip de date
END
DECODE (expr, expr_cautare1, Decodifică valoarea expresiei. Dacă
expr_rezultat1, valoarea expresiei este expr_cautarei
DECODE (1, 1, 2, 3) = 2
[expr_cautare2, expr_rezultat2, atunci e returnată expr_rezultati. Dacă
DECODE (2, 1, 2, 3) = 3
.. nu se potriveşte nici o expresie de
DECODE (3, 1, 2, 3) = 3
expr_cautaren, expr_rezultatn, ] căutare atunci e returnat
[rezultat_implicit]) rezultat_implicit.
21. Să se afişeze numele, codul funcŃiei, salariul şi o coloana care să arate salariul după mărire. Se ştie că
pentru IT_PROG are loc o mărire de 10%, pentru ST_CLERK 15%, iar pentru SA_REP o mărire de
20%. Pentru ceilalŃi angajaŃi nu se acordă mărire. Să se denumească coloana "Salariu revizuit".
SELECT last_name, job_id, salary,
DECODE(job_id,
‘IT_PROG’, salary*1.1,
’ST_CLERK’, salary*1.15,
‘SA_REP’, salary*1.2,
salary ) “salariu revizuit”
FROM employees;

SELECT last_name, job_id, salary,


CASE job_id WHEN ‘IT_PROG’ THEN salary* 1.1
WHEN ’ST_CLERK’ THEN salary*1.15
WHEN ‘SA_REP’ THEN salary*1.2
ELSE salary
END “salariu revizuit”
FROM employees;

SELECT last_name, job_id, salary,


CASE WHEN job_id= ‘IT_PROG’ THEN salary* 1.1
WHEN job_id=’ST_CLERK’ THEN salary*1.15
WHEN job_id =‘SA_REP’ THEN salary*1.2
ELSE salary
END “salariu revizuit”
FROM employees;
22. Să se afişeze numele salariatului şi codul departamentului în care acesta lucrează. Dacă există salariaŃi
care nu au un cod de departament asociat, atunci pe coloana id_depratment să se afişeze:
textul “fara departament”;
valoarea zero.
23. a. Să se afişeze numele angajaŃilor care nu au manager.
b. Să se afişeze numele angajaŃilor şi codul managerilor lor. Pentru angajaŃii care nu au manager să apară
textul “nu are sef”.
24. Să se afişeze numele salariatului şi:
• venitul anual dacă are comision;
• salariul dacă nu are comision.
Se va utiliza funcŃia NVL2.
25. Să se afişeze numele salariatului, salariul şi salariul revizuit astfel:

16
- dacă lucrează de mai mult de 200 de luni atunci salariul va fi mărit cu 20%;
- dacă lucrează de mai mult de 150 de luni, dar mai puŃin de 200 de luni, atunci salariul va fi mărit cu
15%;
- dacă lucrează de mai mult de 100 de luni, dar mai puŃin de 150 de luni, atunci salariul va fi mărit cu
10%;
- altfel, salariul va fi mărit cu 5%.

CERERI MULTITABEL, SUBCERERI

Tipuri de join:
• equijoin (se mai numeşte inner join sau simple join) - compunerea a două tabele diferite după o
condiŃie ce conŃine operatorul de egalitate.
SELECT last_name, department_name, location_id, e.department_id
FROM employees e, departments d
WHERE e.department_id = d.department_id;
Obs: Numele sau alias-urile tabelelor sunt obligatorii în dreptul coloanelor care au acelaşi nume
în mai multe tabele.
• nonequijoin - compunerea a două relaŃii tabele după o condiŃie oarecare, ce NU conŃine
operatorul de egalitate.
SELECT last_name, salary, grade_level
FROM employees, job_grades
WHERE salary BETWEEN lowest_sal AND highest_sal;
• outerjoin - compunerea externă a două tabele diferite completând una dintre relaŃii cu valori
NULL acolo unde nu există în aceasta nici un tuplu ce îndeplineşte condiŃia de corelare. RelaŃia
completată cu valori NULL este cea în dreptul căreia apare “(+)”. Operatorul (+) poate fi plasat
în orice parte a condiŃiei de join, dar nu în ambele părŃi. Full outer join = Left outer join UNION
Right outer join.
SELECT last_name, department_name,location_id
FROM employees e, departments d
WHERE e.department_id(+) = d.department_id;
• selfjoin - compunerea externă a unui tabel cu el însuşi după o condiŃie dată.
SELECT sef.last_name, angajat.last_name
FROM employees sef, employees angajat
WHERE sef.employee_id = angajat.manager_id
ORDER BY sef.last_name;
1. Pentru fiecare angajat să se afişeze numele, codul şi numele departamentului.
SELECT last_name, e.department_id, department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id;
2. Să se afişeze numele angajatului, numele departamentului pentru toŃi angajaŃii care câştigă comision.
3. Să se listeze numele job-urile care există în departamentul 30.
SELECT DISTINCT job_title
FROM employees e, jobs j
WHERE e.job_id = j.job_id
AND department_id = 30;
4. Să se afişeze numele, job-ul şi numele departamentului pentru toŃi angajaŃii care lucrează în Seattle.
5. Să se afişeze numele, salariul, data angajării şi numele departamentului pentru toŃi programatorii care
lucrează în America.
region_name = ‘Americas’
17
job_title = ‘Programmer’
6. Să se afişeze numele salariaŃilor şi numele departamentelor în care lucrează. Se vor afişa şi salariaŃii care
nu lucrează într-un departament (right outher join).
SELECT last_name, department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id(+);
7. Să se afişeze numele departamentelor şi numele salariaŃilor care lucrează în ele. Se vor afişa şi
departamentele care nu au salariaŃi (left outher join).
8. Să se afişeze numele, job-ul, numele departamentului, salariul şi grila de salarizare pentru toŃi angajaŃii.
9. Să se afişeze codul angajatului şi numele acestuia, împreună cu numele şi codul şefului său direct. Se vor
eticheta coloanele Ang#, Angajat, Mgr#, Manager. Să se salveze instrucŃiunea într-un fişier numit
p3_9.sql.
SELECT a.employee_id “Ang#”, a.last_name “Angajat”, b.employee_id “Mgr#”, b.last_name
“Manager”
FROM employees a, employees b
WHERE a.manager_id = b. employee_id;
10. Să se modifice p3_9.sql pentru a afişa toŃi salariaŃii, inclusiv pe cei care nu au şef.
11. Să se afişeze numele salariatului şi data angajării împreună cu numele şi data angajării şefului direct
pentru salariaŃii care au fost angajaŃi înaintea şefilor lor. Se vor eticheta coloanele Angajat, Data_ang,
Manager si Data_mgr.
12. Pentru fiecare angajat din departamentele 20 şi 30 să afişeze numele, codul departamentului şi toŃi
colegii săi (salariaŃii care lucrează în acelaşi departament cu el). Se vor eticheta coloanele corespunzător.
13. Să se afişeze numele şi data angajării pentru salariaŃii care au fost angajaŃi după Fay.
SELECT last_name, hire_date
FROM employees
WHERE hire_date > (SELECT hire_date
FROM employees
WHERE last_name = ‘Fay’);
sau
SELECT a.last_name, a.hire_date
FROM employees a, employees b
WHERE UPPER(b.last_name)=’FAY’ AND a.hire_date>b.hire_date;

14. ScrieŃi o cerere pentru a afişa numele şi salariul pentru toŃi colegii (din acelaşi departament) lui Fay. Se
va exclude Fay.
15. Să se afişeze codul departamentului, codul şi numele angajaŃilor care lucrează în acelaşi departament cu
cel puŃin un angajat al cărui nume conŃine litera “T”. Să se ordoneze după codul departamentului.
SELECT employee_id, last_name, department_id
FROM employees
WHERE department_id IN (SELECT DISTINCT department_id
FROM employees
WHERE UPPER(last_name) LIKE ‘%T%’)
ORDER BY department_id;
16. Să se afişeze numele şi salariul angajaŃilor conduşi direct de Steven King.
SELECT last_name, salary
FROM employees
WHERE manager_id = (SELECT employee_id
FROM employees
18
WHERE UPPER(last_name) ='KING'
AND UPPER(first_name) ='STEVEN' );
17. Să se afişeze numele şi job-ul tuturor angajaŃilor din departamentul ‘Sales’.

18. Să se afişeze numele angajaŃilor, numărul departamentului şi job-ul tuturor salariaŃilor al căror
departament este localizat în Seattle.
SELECT last_name, job_id, department_id
FROM employees
WHERE department_id IN (SELECT department_id
FROM departments
WHERE location_id = (SELECT location_id
FROM locations
WHERE city = ‘Seattle’));
RezolvaŃi această problemă utilizând join-uri.
19. Să se afle dacă există angajaŃi care nu lucrează în departamentul ‘Sales’ şi al căror salariu şi comision
coincid cu salariul şi comisionul unui angajat din departamentul ‘Sales’.
SELECT last_name, salary, commission_pct, department_id
FROM employees
WHERE (salary, commission_pct) IN (SELECT salary, commission_pct
FROM employees e, departments d
WHERE e.department_id = d.department_id
AND department_name = ‘Sales’)
AND department_id <> (SELECT department_id
FROM departments
WHERE department_name = ‘Sales’);
20. ScrieŃi o cerere pentru a afişa numele, numele departamentului şi salariul angajaŃilor care nu câştigă
comision, dar al căror manager coincide cu managerul unui angajat care câştigă comision.
21. ScrieŃi o cerere pentru a afişa angajaŃii care câştigă mai mult decât oricare funcŃionar. SortaŃi rezultatele
după salariu, în ordine descrescătoare.
SELECT last_name, salary, job_id
FROM employees
WHERE salary > (SELECT MAX(salary)
FROM employees
WHERE job_id LIKE '%CLERK')
ORDER BY salary DESC;
22. Să se afişeze codul, numele şi salariul tuturor angajaŃilor care câştigă mai mult decât salariul mediu.
23. Să se afişeze pentru fiecare salariat angajat în luna martie numele său, data angajării şi numele jobului.
24. Să se afişeze pentru fiecare salariat al cărui câştig total lunar este mai mare decât 12000 numele său,
câştigul total lunar şi numele departamentului în care lucrează.
25. Să se afişeze pentru fiecare angajat codul său şi numele joburilor sale anterioare, precum şi intervalul de
timp în care a lucrat pe jobul respectiv.
26. Să se modifice cererea de la punctul 25 astfel încât să se afişeze şi numele angajatului, respectiv codul
jobului său curent.
27. Să se modifice cererea de la punctul 26 astfel încât să se afişeze şi numele jobului său curent.
28. Să se afişeze salariaŃii care au acelaşi manager ca şi angajatul având codul 140.
29. Să se afişeze numele departamentelor care funcŃionează în America.

19
FuncŃii multiple-row (grup). Gruparea datelor.

Aceste tipuri de funcŃii pot fi utilizate pentru a returna informaŃia corespunzătoare fiecăruia dintre
grupurile obŃinute în urma divizării liniilor tabelului cu ajutorul clauzei GROUP BY.
Pot apărea în clauzele SELECT, ORDER BY şi HAVING. Server-ul Oracle aplică aceste funcŃii
fiecărui grup de linii şi returnează un singur rezultat pentru fiecare mulŃime.
Exemple de funcŃii grup: AVG, SUM, MAX, MIN, COUNT etc.
Tipurile de date ale argumentelor funcŃiilor grup pot fi CHAR, VARCHAR2, NUMBER sau DATE.
FuncŃiile AVG şi SUM, operează numai asupra valorilor numerice. FuncŃiile MAX şi MIN pot opera asupra
valorilor numerice, caracter sau dată calendaristică.
Toate funcŃiile grup, cu excepŃia lui COUNT(*), ignoră valorile null. COUNT(expresie)
returnează numărul de linii pentru care expresia dată nu are valoarea null. FuncŃia COUNT returnează
un număr mai mare sau egal cu zero şi nu întoarce niciodată valoarea null.
Când este utilizată clauza GROUP BY, server-ul sortează implicit mulŃimea rezultată în
ordinea crescătoare a valorilor coloanelor după care se realizează gruparea.
AbsenŃa clauzei GROUP BY conduce la aplicarea funcŃiei grup pe mulŃimea tuturor liniilor
tabelului.
În clauza GROUP BY se trec obligatoriu toate coloanele prezente în clauza SELECT, care nu
sunt argument al funcŃiilor grup.

1. Să se afişeze cel mai mare salariu, cel mai mic salariu, suma şi media salariilor tuturor angajatilor.
EtichetaŃi coloanele Maxim, Minim, Suma, respectiv Media. Să se rotunjească rezultatele.
SELECT MIN(salary) min, MAX(salary) max, SUM(salary) suma, ROUND(AVG(salary)) media
FROM employees;
2. Utilizând funcŃia grup COUNT să se determine:
a. numărul total de angajaŃi;
b. numărul de angajaŃi care au manager;
c. numărul de manageri.
3. Să se afişeze diferenŃa dintre cel mai mare şi cel mai mic salariu. EtichetaŃi coloana “Diferenta”.
4. Să se listeze numărul de angajaŃi din departamentul având codul 50.
5. CaŃi angajaŃi din departamentul 80 câştigă comision?
6. Să se selecteze valoarea medie şi suma salariilor pentru toŃi angajaŃii care sunt reprezentanŃi de vânzări
(SA_MAN, SA_REP).
7. Să se selecteze data angajării primei persoane care a fost angajată de companie.
8. Să se afişeze numărul de angajaŃi pentru fiecare job.
SELECT job_id, COUNT(employee_id) nr_angajati
FROM employees
GROUP BY job_id;
9. Să se afişeze minimul, maximul, suma şi media salariilor pentru fiecare departament.
10. Să se afişeze codul departamentului şi media salariilor pentru fiecare job din cadrul acestuia.
SELECT department_id, job_id, AVG(salary)
FROM employees
GROUP BY department_id, job_id;
11. a. Să se afişeze codul departamentelor pentru care salariul minim depăşeşte 5000$.
SELECT department_id, MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary)>5000;
20
b. Să se modifice cererea anterioară astfel încât să se afişeze şi oraşul în care funcŃionează aceste
departamente.
12. Să se obŃină codul departamentelor şi numărul de angajaŃi al acestora pentru departamentele care au cel
puŃin 10 angajaŃi.
13. Să se obŃină codul departamentelor şi suma salariilor angajaŃilor care lucrează în acestea, în ordine
descrescătoare după sumă. Se consideră angajaŃii care au comision şi departamentele care au mai mult
de 5 angajaŃi.
14. Să se obŃină job-ul pentru care salariul mediu este minim.
SELECT job_id
FROM employees
GROUP BY job_id
HAVING AVG(salary) = (SELECT MIN(AVG(salary))
FROM employees
GROUP BY job_id);
15. Să se afişeze cel mai mare dintre salariile medii pe departamente.
16. a. Să se afişeze codul, numele departamentului şi suma salariilor pe departamente.
SELECT d.department_id, department_name,a.suma
FROM departments d, (SELECT department_id ,SUM(salary) suma
FROM employees
GROUP BY department_id) a
WHERE d.department_id =a.department_id;

b. DaŃi o altă metodă de rezolvare a acestei probleme.

17. a. ScrieŃi o cerere pentru a afişa numele departamentului, numărul de angajaŃi şi salariul mediu pentru
angajaŃii din acel departament. Coloanele vor fi etichetate Departament, Nr. angajati, Salariu Mediu.
SELECT department_name “Departament”,
(SELECT COUNT(employee_id)
FROM employees
WHERE department_id = d.department_id ) ” Nr. angajati”,
(SELECT AVG(salary)
FROM employees
WHERE department_id = d.department_id) ”Salariu mediu”
FROM departments d;
b. DaŃi o altă metodă de rezolvare pentru problema anterioară.

18. Să se creeze o cerere prin care să se afişeze numărul total de angajaŃi şi, din acest total, numărul celor
care au fost angajaŃi în 1997, 1998, 1999 şi 2000. Datele vor fi afişate în forma următoare:
Total 1997 1998 1999 2000
--------------------------------------------------------------
50 10 5 25 1

SUM(DECODE(TO_CHAR(hire_date,'yyyy'),1997,1,0))

21
Operatorii ROLLUP şi CUBE

Clauza GROUP BY permite gruparea liniilor selectate după valorile expresiilor precizate în
aceasta. Pentru fiecare grup, va fi returnată o singură linie de informaŃie. Clauza GROUP BY poate
produce grupări superagregat utilizând extensiile CUBE sau ROLLUP.
ROLLUP grupează liniile selectate pe baza valorilor primelor n, n - 1, …, 0 expresii din
specificaŃia GROUP BY şi returnează o singură linie pentru fiecare grup. ROLLUP creează grupări
prin deplasarea într-o singură direcŃie, de la dreapta la stânga, de-a lungul listei de coloane
specificate în clauza GROUP BY. Apoi, se aplică funcŃia agregat acestor grupări. Dacă sunt
specificate n expresii în operatorul ROLLUP, numărul de grupări generate va fi n + 1. Liniile care se
bazează pe valoarea primelor n expresii se numesc linii obişnuite, iar celelalte se numesc linii
superagregat.
GROUP BY ROLLUP (expr_1, expr_2, …, expr_n) generează n+1 tipuri de linii,
corespunzătoare următoarelor grupări:
• GROUP BY (expr_1, expr_2, …, expr_n-1, expr_n)
• GROUP BY (expr_1, expr_2, …, expr_n-1)
• …
• GROUP BY (expr_1, expr_2)
• GROUP BY (expr_1)
• GROUP BY () – corespunzător absenŃei clauzei GROUP BY şi deci, calculului funcŃiilor
grup din cerere pentru întreg tabelul.

CUBE grupează liniile selectate pe baza valorilor tuturor combinaŃiilor posibile ale expresiilor
specificate şi returnează câte o linie totalizatoare pentru fiecare grup. Acest operator este folosit pentru a
produce mulŃimi de rezultate care sunt utilizate în rapoarte. În vreme ce ROLLUP produce subtotalurile
doar pentru o parte dintre combinaŃiile posibile, operatorul CUBE produce subtotaluri pentru toate
combinaŃiile posibile de grupări specificate în clauza GROUP BY, precum şi un total general.
Dacă există n coloane sau expresii în clauza GROUP BY, vor exista 2n combinaŃii posibile
superagregat.
19. Să se afişeze codurile departamentelor în care lucrează cel puŃin un angajat, iar pentru fiecare dintre
acestea şi pentru fiecare manager care lucrează în departamentul respectiv să se afişeze numărul de
salariaŃi. De asemenea, să se afişeze numărul de salariaŃi pentru fiecare departament indiferent de
manager şi numărul total de angajaŃi din companie.

SELECT department_id, manager_id, COUNT(employee_id)


FROM employees
WHERE manager_id IS NOT NULL AND department_id IS NOT NULL
GROUP BY ROLLUP (department_id, manager_id);

department_id manager_id COUNT(employee_id)


---------------------------------------------------------------------
10 7782 1
10 7839 1
10 2
-----------------------------------------------------------------------
20 7566 2
20 7788 1
20 7839 1
20 7902 1
20 5
-----------------------------------------------------------------------
30 7698 5
30 7839 1
30 6

22
-----------------------------------------------------------------------
13
20. Să se afişeze codurile departamentelor în care lucrează cel puŃin un angajat, iar pentru fiecare dintre
acestea şi pentru fiecare manager care lucrează în departamentul respectiv să se afişeze numărul de
salariaŃi. De asemenea, să se afişeze numărul de salariaŃi pentru fiecare departament indiferent de
manager, numărul de angajaŃi subordonaŃi unui manager indiferent de departament şi numărul total de
angajaŃi din companie.

SELECT department_id, manager_id, COUNT(employee_id)


FROM employees
WHERE manager_id IS NOT NULL AND department_id IS NOT NULL
GROUP BY CUBE (department_id, manager_id);

department_id manager_id COUNT(employee_id)


---------------------------------------------------------------------
10 7782 1
10 7839 1
10 2
-----------------------------------------------------------------------
20 7566 2
20 7788 1
20 7839 1
20 7902 1
20 5
-----------------------------------------------------------------------
30 7698 5
30 7839 1
30 6
-----------------------------------------------------------------------
7566 2
7698 5
7782 1
7788 1
7839 3
7902 1
----------------------------------------------------------------------
13
21. Pentru fiecare departament, job, respectiv an al angajării să se afişeze numărul de salariaŃi. De asemenea
se va afişa numărul de angajaŃi:
- pentru fiecare departament şi job, indiferent de anul angajării;
- pentru fiecare departament, indiferent de job şi de anul angajării;
- la nivel de companie.
22. Să se afişeze suma alocată pentru plata salariilor pe joburi (codul jobului), în cadrul departamentului
(codul departamentului). De asemenea, să se afişeze valoarea totală necesară pentru plata salariilor la
nivel de departament, valoarea totală necesară pentru plata salariilor la nivel de job, indiferent de
departament şi valoarea totală necesară pentru plata salariilor la nivel de companie.
23. FuncŃia GROUPING(expresie) întoarce:
- valoarea 0, dacă expresia a fost utilizată pentru calculul valorii agregat
- valoarea 1, dacă expresia nu a fost utilizată.
24. Să se afişeze numele departamentelor, titlurile job-urilor şi valoarea medie a salariilor, pentru:
- fiecare departament şi, în cadrul său pentru fiecare job;
- fiecare departament (indiferent de job);
- întreg tabelul.
23
De asemenea, să se afişeze şi o coloană care indică intervenŃia coloanelor department_name şi job_title
în obŃinerea rezultatului.

25. ModificaŃi cererea anterioară astfel încât să se afişeze numele departamentelor, titlurile job-urilor şi
valoarea medie a salariilor, pentru:
- fiecare departament şi, în cadrul său pentru fiecare job;
- fiecare departament (indiferent de job);
- fiecare job(indiferent de departament);
- întreg tabelul.
Cum intervin coloanele în obŃinerea rezultatului?
Să se afişeze ’Dept’, dacă departamentul a intervenit în agregare şi ‘Job’, dacă job-ul a intervenit în
agregare.
DECODE(GROUPING(department_name), 0, ‘Dept’)
26. UtilizaŃi cererea de la punctul 20.
a. EliminaŃi clauza WHERE din această cerere. AnalizaŃi rezultatul obŃinut.
b. ModificaŃi cererea obŃinută astfel încât să se identifice dacă o valoare null din rezultat este stocată pe
una dintre coloanele manager_id sau department_id sau este produsă de operatorul CUBE.
27. Clauza GROUPING SETS. Permite obŃinerea numai a anumitor grupări superagregat. Acestea pot fi
precizate prin intermediul clauzei:

GROUP BY GROUPING SETS ((expr_11, expr_12, …, expr_1n), (expr_21, expr_22, …expr_2m),


…)
28. Să se afişeze numele departamentelor, numele job-urilor, codurile managerilor angajaŃilor, maximul şi
suma salariilor pentru:
- fiecare departament şi, în cadrul său, fiecare job;
- fiecare job şi, în cadrul său, pentru fiecare manager;
- întreg tabelul.

GROUPING SETS ((department_name, job_title), (job_title, e.manager_id), ());

Limbajul de control al datelor (DCL). COMMIT, SAVEPOINT, ROLLBACK.


• Comanda COMMIT permanentizează modificările care au fost realizate de tranzacŃia curentă (o
tranzacŃie este un set de comenzi LMD); comanda suprimă toate punctele intermediare definite în
tranzacŃie şi eliberează blocările tranzacŃiei.
ObservaŃie:
Sistemul realizează COMMIT implicit:
- la închiderea normală a unui client Oracle (de exemplu SQL*Plus),
- după fiecare comandă LDD (CREATE, ALTER, DROP).
• Comanda SAVEPOINT marchează un punct intermediar în procesarea tranzacŃiei. În acest mod este
posibilă împărŃirea tranzacŃiei în subtranzacŃii.
Comanda SAVEPOINT are sintaxa:
SAVEPOINT nume_pct_intermediar;
• Comanda ROLLBACK permite renunŃarea la modificările efectuate; aceasta determină încheierea
tranzacŃiei, anularea modificărilor asupra datelor şi restaurarea stării lor precedente.
Comanda ROLLBACK are sintaxa:
ROLLBACK [TO SAVEPOINT nume_punct_salvare];

24
ObservaŃii:
- sistemul realizează ROLLBACK implicit dacă se închide anormal (defecŃiune hardware sau software,
pană de curent etc.);
- nici o comanda LDD (CREATE, ALTER; DROP) nu poate fi anulată.
1. Ce efect are următoarea secvenŃă de instrucŃiuni?
CREATE TABLE dept_***
AS SELECT * FROM departmets;

SELECT *
FROM dept_***;

SAVEPOINT a;

DELETE FROM dept_***;

INSERT INTO dept_***


VALUES (300,’Economic’,100,1000);

INSERT INTO dept_***


VALUES (350,’Cercetare’,200,2000);

SAVEPOINT b;

INSERT INTO dept_***


VALUES (400,’Juritic’,150,3000);

SELECT COUNT(*)
FROM dept_***;

ROLLBACK TO b;

SELECT COUNT(*)
FROM dept_***;

ROLLBACK TO a;

INSERT INTO dept_***


VALUES (500,’Contabilitate’,175,1500);

COMMIT;

SELECT *
FROM dept_***;

Limbajul de prelucrare a datelor (DML). INSERT, UPDATE, DELETE.


1. Să se creeze tabele emp_*** şi dept_***, având aceeaşi structură şi date ca şi tabelele employees,
respectiv departments.
CREATE TABLE emp_*** AS
SELECT * FROM employees;
2. Să se selecteze toate înregistrările din cele două tabele create anterior.
3. ŞtergeŃi toate înregistrările din cele 2 tabele create anterior. SalvaŃi modificările.
25
DELETE FROM emp_***;
COMMIT;
4. Să se listeze structura tabelului employees şi să se compare cu structura tabelului emp_***. Ce observaŃi?
5. Sintaxa simplificată a comenzii INSERT
- pentru inserarea unei singure linii:
INSERT INTO nume_tabel [(col1,col2,...)]
VALUES (expresie1, expresie2, ...);
- pentru inserarea liniilor rezultat ale unei comenzi SELECT:
INSERT INTO nume_tabel [(col1,col2,...)]
comanda_SELECT;
6. Să se exemplifice câteva dintre erorile care pot să apară la inserare şi să se observe mesajul returnat de sistem.
- lipsa de valori pentru coloane NOT NULL (coloana department_name este definită NOT NULL)
INSERT INTO dept_*** (department_id, location_id)
VALUES (200, 2000);
- nepotrivirea listei de coloane cu cea de expresii
INSERT INTO dept_***
VALUES (200, 2000);
INSERT INTO dept_*** (department_id, department_name,location_id)
VALUES (200, 2000);
- nepotrivirea tipului de date
INSERT INTO dept_*** (department_id, location_id)
VALUES (‘D23’, 2000);
- valoare prea mare pentru coloană
INSERT INTO dept_*** (department_id, location_id)
VALUES (15000, 2000);
7. InseraŃi în tabelul emp_*** salariaŃii (din tabelul employees) al căror comision depăşeşte 25% din salariu.

8. CreaŃi tabele emp1_***, emp2_*** şi emp3_*** cu aceeaşi structură ca tabelul employees. InseraŃi,
utilizând o singură comandă INSERT, informaŃii din tabelul employees:
- în tabelul emp1_*** salariaŃii care au salariul mai mic decât 6000;
- în tabelul emp2_*** salariaŃii care au salariul cuprins între 6000 şi 10000;
- în tabelul emp3_*** salariaŃii care au salariul mai mare decât 10000.
VerificaŃi rezultatele, apoi ştergeŃi toate înregistrările din aceste tabele.

Obs. Clauza ALL a comenzii INSERT determină evaluarea tuturor condiŃiilor din clauzele WHEN. Pentru
cele a căror valoare este TRUE, se inserează înregistrarea specificată în opŃiunea INTO corespunzătoare.
9. Să se creeze tabelul emp0_*** cu aceeaşi structură ca tabelul employees. InseraŃi, utilizând o singură
comandă INSERT, informaŃii din tabelul employees:
- în tabelul emp0_*** salariaŃii care lucrează în departamentul 80;
- în tabelul emp1_*** salariaŃii care au salariul mai mic decât 6000 (care nu se regăsesc în tabelul
emp0_***);
- în tabelul emp2_*** salariaŃii care au salariul cuprins între 6000 şi 10000 (care nu se regăsesc în
tabelele emp0_*** şi emp1_***);
- în tabelul emp3_*** salariaŃii care au salariul mai mare decât 10000 (care nu se regăsesc în tabelele
emp0_***, emp1_*** şi emp2_***).

Obs.

26
Clauza FIRST a comenzii INSERT determină inserarea corespunzătoare primei clauze WHEN a cărei
condiŃie este evaluată TRUE. Toate celelalte clauze WHEN sunt ignorate.
10. Sintaxa simplificată a comenzii DELETE
DELETE FROM nume_tabel
[WHERE conditie];
11. ŞtergeŃi toate înregistrările din tabelele emp_*** şi dept_***. InseraŃi în aceste tabele toate înregistrările
corespunzătoare din employees, respectiv departments. PermanentizaŃi tranzacŃia.

12. ŞtergeŃi angajaŃii care nu au comision. AnulaŃi modificările.

DELETE FROM emp_***


WHERE commission_pct IS NULL;
ROLLBACK;

13. EliminaŃi departamentele care nu au nici un angajat. AnulaŃi modificările.


14. EliminaŃi angajaŃii care nu aparŃin unui departament valid. AnulaŃi modificările.
15. Sintaxa simplificată a comenzii UPDATE:
UPDATE nume_tabel [alias]
SET col1 = expr1[, col2=expr2]
[WHERE conditie];
sau
UPDATE nume_tabel [alias]
SET (col1,col2,...) = (subcerere)
[WHERE conditie];
16. MăriŃi salariul tuturor angajaŃilor din tabelul emp_*** cu 5%. AnulaŃi modificările.

UPDATE emp_***
SET salary = salary * 1.05;
ROLLBACK;

17. SchimbaŃi jobul tuturor salariaŃilor din departamentul 80 care au comision în 'SA_REP'. AnulaŃi
modificările.

18. Să se modifice jobul şi departamentul angajatului având codul 114, astfel încât să fie la fel cu cele ale
angajatului având codul 205.
19. SchimbaŃi salariul şi comisionul celui mai prost plătit salariat din firmă, astfel încât să fie egale cu
salariul si comisionul directorului.
20. Pentru fiecare departament să se mărească salariul celor care au fost angajaŃi primii astfel încât să devină
media salariilor din companie.
21. Să se modifice valoarea emailului pentru angajaŃii care câştigă cel mai mult în departamentul în care
lucrează astfel încât acesta să devină iniŃiala numelui concatenată cu prenumele. Dacă nu are prenume
atunci în loc de acesta apare caracterul ‘.’. AnulaŃi modificările.

27
LABORATOR 6 SQL - LDD
Limbajul de definire a datelor (CREATE, ALTER, DROP)

Crearea tabelelor
CREATE TABLE [schema.]nume_tabel (
nume_coloana tip_de_date [DEFAULT1 expr], ...);
CREATE TABLE nume_tabel [(col1, col2...)]
AS subcerere;
1. Creaţi tabelul salariat_*** având următoarea structură:
Nume Caracteristici Tip
cod_ang NOT NULL NUMBER(4)
nume VARCHAR2(25)
prenume VARCHAR2(25)
functia VARCHAR2(20)
sef NUMBER(4)
Valoare implicită data
data_angajarii DATE
curentă
varsta NUMBER(2)
email CHAR(50)
salariu Valoare implicită 0 NUMBER(9,2)

CREATE TABLE salariat_*** (


cod_ang NUMBER(4) NOT NULL,
nume VARCHAR2(25),
prenume VARCHAR2(25),
functia VARCHAR2(20),
sef NUMBER(4),
data_angajarii DATE DEFAULT SYSDATE,
varsta NUMBER(2),
email CHAR(50),
salariu NUMBER(9,2) DEFAULT 0);
2. Afişaţi structura tabelului creat anterior.
3. Se dau următoarele valori:
COD _ANG NUME PRENUME FUNCTIA SEF DATA_ANG VARSTA EMAIL SALARIU
1 ..... ..... director null ........ 30 ..... 5500
2 ..... ..... functionar 1 ......... 25 ..... 0
3 ..... ...... economist 1 ......... 45 ..... 3000
4 ..... .... functionar 1 ......... 35 ...... 1000
4. Inseraţi în tabelul salariat_*** prima înregistrare din tabelul de mai sus fără să precizaţi lista de
coloane în comanda INSERT.
5. Inseraţi a doua înregistrare folosind o listă de coloane din care excludeţi data_angajarii şi salariul
care au valori implicite. Observaţi apoi rezultatul.
6. Inseraţi înregistrările 3 şi 4.
7. Creaţi tabelul functionar_*** care să conţină funcţionarii din tabelul salariat_***, având
următoarele coloane: codul, numele, salariul anual şi data angajării. Verificaţi cum a fost creat
tabelul şi ce date conţine.

1
Modificarea tabelelor

8. Adăugaţi o nouă coloană tabelului salariat_*** care să conţină data naşterii.

ALTER TABLE salariat_***


ADD (datan DATE);

9. Modificaţi dimensiunea coloanei nume la 30 si pe cea a salariului la 12 cu 3 zecimale.

ALTER TABLE salariat_***


MODIFY (nume VARCHAR2(30), salariu NUMBER(12,3));

10. Modificaţi tipul coloanei email la VARCHAR2.


11. Modificaţi valoarea implicită a coloanei data_angajarii la data sistemului + o zi.
12. Eliminaţi coloana varsta din tabelul salariat_***.

ALTER TABLE salariat_***


DROP COLUMN varsta;

Redenumirea şi eliminarea tabelelor

RENAME nume_tabel TO nume_nou;


DROP TABLE nume_tabel;
13. Redenumiţi tabelul functionar_*** cu funct_***.
14. Recreaţi tabelul functionar_*** utilizând tabelul funct_***..
15. Eliminaţi tabelul funct_***.

Constrângeri

Adăugarea constrângerilor la crearea tabelului (CREATE TABLE)


CREATE TABLE [schema.]nume_tabel (
nume_coloana tip_de_date [DEFAULT expr]
[constrangere_de_coloana], ...
..[constrangere la nivel de tabel])
16. Ştergeţi şi apoi creaţi din nou tabelul salariat_*** cu următoarea structură.
NUME TIP CONSTRÂNGERE
cod_ang NUMBER(4) Cheie primară
nume VARCHAR2(25) NOT NULL
prenume VARCHAR2(25)
data_nasterii DATE data_nasterii<data_angajarii
functia VARCHAR2(9) NOT NULL
Referă ca şi cheie externă cod_ang din acelaşi
sef NUMBER(4)
tabel
data_angajarii DATE
email VARCHAR2(20) unic
salariu NUMBER(12,3) >0
cod_dept NUMBER(4) Combinaţia NUME + PRENUME să fie unică
2
Observaţie:
Constrângerile de tip CHECK se pot implementa la nivel de coloană doar dacă nu referă o altă
coloană a tabelului.

DROP TABLE salariat_***;


CREATE TABLE salariat_*** (
cod_ang NUMBER(4) PRIMARY KEY,
nume VARCHAR2(25) NOT NULL,
prenume VARCHAR2(25),
data_nasterii DATE,
functia VARCHAR2(9) NOT NULL,
sef NUMBER(4) REFERENCES salariat_*** (cod_ang),
data_angajarii DATE DEFAULT SYSDATE,
email VARCHAR2(20) UNIQUE,
salariu NUMBER(9,2) CHECK (salariu > 0),
cod_dep NUMBER(4),
CONSTRAINT const_c_*** CHECK (data_angajarii > data_nasterii),
CONSTRAINT const_u_*** UNIQUE (nume,prenume,data_nasterii));

17. Ştergeţi tabelul salariat_***, iar apoi recreaţi-l implementând toate constrângerile la nivel de
tabel.
Observaţie: Constrângerea de tip NOT NULL se poate declara doar la nivel de coloană.

DROP TABLE salariat_***;


CREATE TABLE salariat_*** (
cod_ang NUMBER(4),
nume VARCHAR2(25) NOT NULL,
prenume VARCHAR2(25),
data_nasterii DATE,
functia VARCHAR2(9) NOT NULL,
sef NUMBER(4),
data_angajarii DATE DEFAULT SYSDATE,
email VARCHAR2(20),
salariu NUMBER(9,2),
cod_dep NUMBER(4),
CONSTRAINT ccp_*** PRIMARY KEY (cod_ang),
CONSTRAINT cce_*** FOREIGN KEY (sef) REFERENCES salariat_***
(cod_ang),
CONSTRAINT cu1_*** UNIQUE (email),
CONSTRAINT cc1_*** CHECK (data_angajarii > data_nasterii),
CONSTRAINT cc2_***CHECK (salariu > 0),
CONSTRAINT cu2_*** UNIQUE (nume,prenume,data_nasterii));
18. Creaţi tabelul departament_*** care să aibă următoarea structură.

NUME TIP CONSTRÂNGERI


COD_DEP NUMBER(4) Cheie primară
NUME VARCHAR2(20) Not null
ORAS VARCHAR2(25)

3
Adăugarea constrângerilor ulterior creării tabelului, eliminarea, activarea sau dezactivarea
constrângerilor (ALTER TABLE)

- adaugă constrângeri
ALTER TABLE nume_tabel
ADD [CONSTRAINT nume_constr] tip_constr (coloana);
- elimină constrângeri
ALTER TABLE nume_tabel
DROP [CONSTRAINT nume_constr] tip_constr (coloana);
- activare/dezactivare constrângere
ALTER TABLE nume_tabel
MODIFY CONSTRAINT nume_constr ENABLE|DISABLE;
sau
ALTER TABLE nume_tabel
ENABLE| DISABLE nume_constr;
19. Inseraţi o nouă înregistrare în salariat_*** de forma:
cod nume prenume data_n functia sef data_ang email salariu cod_dep
11-JUN-
2 N2 P2 economist 1 Sysdate E2 2000 10
1960
Ce observaţi? Introduceţi înregistrarea dar specificând valoarea NULL pentru coloana sef.
20. Încercaţi să adăugaţi o constrângere de cheie externă pe cod_dep din salariat_***. Ce
observaţi?

ALTER TABLE salariat_***


ADD CONSTRAINT cce2_*** FOREIGN KEY (cod_dep) REFERENCES
departament_*** (cod_dep);

21. Inseraţi o nouă înregistrare în departament_***. Apoi adăugaţi constrângerea de cheie externă
definită anterior.
cod_dep nume loc
10 Economic Bucuresti

22. Inseraţi noi înregistrări în salariat_***, respectiv în departament_***. Care trebuie să fie
ordinea de inserare?
cod nume prenume data_n functia sef data_ang email salariu cod_dep
11-JUN-
3 N3 P3 jurist 2 Sysdate E3 2500 20
1967

cod_dep nume loc


20 Juritic Constanta
23. Ştergeţi departamentul 20 din tabelul departament_***. Ce observaţi?
24. Ştergeţi constrângerea cce2_***. Recreaţi această constrângere adăugând opţiunea ON
DELETE CASCADE.
ALTER TABLE salariat_***
DROP CONSTRAINT cce2_***;
ALTER TABLE salariat_***
ADD CONSTRAINT cce2_*** FOREIGN KEY (cod_dep) REFERENCES
departament_*** (cod_dep) ON DELETE CASCADE;
25. Ştergeţi departamentul 20 din tabelul departament_***. Ce observaţi în tabelul salariat_***?
Anulaţi modificările.
4
26. Ştergeţi constrângerea cce2_***. Recreaţi această constrângere adăugând opţiunea ON
DELETE SET NULL.

ALTER TABLE salariat_***


DROP CONSTRAINT cce2_***;
ALTER TABLE salariat_***
ADD CONSTRAINT cce2_*** FOREIGN KEY (cod_dep) REFERENCES
departament_*** (cod_dep) ON DELETE SET NULL;

27. Încercaţi să ştergeţi departamentul 10 din tabelul departament_***. Ce observaţi?

Consultarea dicţionarului datelor

Informaţii despre tabelele create se găsesc în vizualizările :


 USER_TABLES – informaţii complete despre tabelele utilizatorului curent.
 ALL_TABLES – informaţii complete despre tabelele tuturor utilizatorilor.
 COLS – informaţii despre coloane.
 TAB – informaţii de bază despre tabelele existente în schema utilizatorului curent.

Informaţii despre constrângeri găsim în :


 USER_CONSTRAINTS – informaţii despre constrângerile definite de utilizatorul
curent;
 ALL_CONSTRAINTS – informaţii despre cosntrângerile definite de toţi utilizatorii.

5
LABORATOR 7 SQL - LDD
Vizualizări. Secvenţe. Indecşi. Sinonime.

 Definirea vizualizărilor

Vizualizările sunt tabele virtuale care sunt construite pe baza unor tabele sau vizualizări,
denumite tabele de bază. Ele nu conţin date ci sunt ca nişte imagini logice asupra datelor din tabelele
de bază. Sunt definite de o cerere SQL, de aceea mai sunt denumite şi cereri stocate.
Avantajele utilizării vizualizărilor:
- restricţionarea accesului la date;
- simplificarea unor cereri complexe;
- prezentarea de diferite imagini asupra datelor.
Vizualizările se pot fi simple sau complexe. Asupra vizualizărilor simple se pot realiza operaţii
LMD. Asupra vizualizărilor complexe nu sunt posibile operaţii LMD în toate cazurile decât dacă sunt
definiţi declanşatori de tip INSTEAD OF.
Caracteristici Simple Complexe
Număr de tabele de baza Un singur tabel Unul sau mai multe tabele
Conţine funcţii Nu Da
Conţine grupări de date Nu Da

Sintaxa simplificată a comenzii CREATE VIEW este:


CREATE [OR REPLACE] [FORCE | NOFORCE] VIEW nume_view [(alias, alias, ..)]
AS subcerere
[WITH CHECK OPTION [CONSTRAINT nume_constr]]
[WITH READ ONLY [CONSTRAINT nume_constr]];
- FORCE permite crearea vizualizarea înainte de a defini tabelele de bază;
- subcererea poate fi oricât de complexă dar nu poate conţine clauza ORDER BY;
- WITH CHECK OPTION permite inserarea şi modificarea prin intermediul vizualizării numai
a liniilor ce sunt accesibile vizualizării; dacă lipseşte numele constrângerii atunci sistemul
asociază un nume implicit de tip SYS_Cn acestei constrângeri;
- WITH READ ONLY asigură că prin intermediul vizualizării nu se pot executa operaţii LMD.
Nu se pot realiza operaţii LMD în vizualizări ce conţin:
- funcţii grup,
- clauza GROUP BY sau HAVING,
- cuvântul cheie DISTINCT,
- pseudocoloana ROWNUM,
- coloane definite de expresii,
- coloane NOT NULL din tabelul de bază, care nu sunt incluse în coloanele vizualizării.
- linii ce nu sunt accesibile vizualizării (în cazul utilizării clauzei WITH CHECK OPTION).
Nu se pot actualiza:
- coloane ale căror valori rezultă prin calcul sau definite cu ajutorul funcţiei DECODE,
- coloane care nu respectă constrângerile din tabelele de bază.
Pentru vizualizările bazate pe mai multe tabele, orice operaţie INSERT, UPDATE sau DELETE
poate modifica datele doar din unul din tabelele de bază. Acest tabel este cel protejat prin cheie (key
preserved).
Eliminarea unei vizualizări se face prin comanda DROP VIEW :
DROP VIEW nume_viz;
Observaţie:
Subcererile temporare caracterizate de un alias ce apar în comenzile SELECT, INSERT.
UPDATE, DELETE, MERGE se numesc vizualizai inline (de exemplu o subcerere utilizată
în clauza FROM a comenzii SELECT). Spre deosebire de vizualizările propriu-zise acestea
nu sunt considerate obiecte ale schemei şi sunt entităţi temporare.
Observaţie:
Reactualizarea tabelelor implică reactualizarea corespunzătoare a vizualizărilor.
Reactualizarea vizualizărilor nu implică întotdeauna reactualizarea tabelelor de bază.

1. Să se creeze vizualizarea v_emp_*** care să conţină codul şi numele salariaţilor din tabelul emp_***.
Să se afişeze conţinutul acesteia. Să se insereze o nouă înregistrare în această vizualizare. Ce
observaţi? Să se şteargă vizualizarea v_emp_***.

CREATE VIEW v_emp_*** (cod, nume)


AS
SELECT employee_id, last_name
FROM emp_***;

INSERT INTO v_emp_***


VALUES (400,’N1’);

DROP VIEW v_emp_***;

2. Să se creeze vizualizarea v_emp_*** care să conţină codul, numele, emailul, data angajării, salariul şi
codul jobului salariaţilor din tabelul emp_***. Să se analizeze structura şi conţinutul vizualizării. Să
se insereze o nouă înregistrare în această vizualizare. Să se verifice că noua înregistrare a fost inserată
şi în tabelul de bază.
Observaţie: Trebuie introduse neapărat în vizualizare coloanele care au constrângerea NOT NULL în
tabelul de bază (altfel, chiar dacă tipul vizualizării permite operaţii LMD, acestea nu vor fi posibile
din cauza nerespectării constrângerilor NOT NULL).

CREATE VIEW v_emp_***


AS
SELECT employee_id, last_name, email, hire_date, salary,job_id
FROM emp_***;

DESC v_emp_***

SELECT * FROM v_emp_***;

INSERT INTO v_emp_***


VALUES (400,’N1’,’E1’,SYSDATE,5000,’SA_REP’);

SELECT employee_id, last_name, email, hire_date, salary, job_id


FROM emp_***;
3. Să se mărească cu 1000 salariul angajatului având codul 400 din vizualizarea creată anterior. Ce efect
va avea această acţiune asupra tabelului de bază?
UPDATE v_emp_***
SET salary=salary+1000
WHERE employee_id = 400;
SELECT employee_id, last_name, salary
FROM emp_***
WHERE employee_id = 400;

4. Să se şteargă angajatul având codul 400 din vizualizarea creată anterior. Ce efect va avea această
acţiune asupra tabelului de bază?

DELETE FROM v_emp_***


WHERE employee_id = 400;

SELECT employee_id, last_name, salary


FROM emp_***
WHERE employee_id = 400;

5. a) Să se creeze vizualizarea v_emp_dept_*** care să conţină employee_id, last_name, hire_date,


job_id, department_id din tabelul emp_*** şi coloana department_name din tabelul dept_***.

CREATE VIEW v_emp_dept_*** AS


SELECT employee_id, last_name, email, hire_date, job_id,
e.department_id, department_name
FROM emp_*** e, dept_*** d
WHERE e.department_id =d.department_id;

b) Să încerce inserarea înregistrării (500, 'N2', 'E2',SYSDATE,’SA_REP’,30, 'Administrativ') în


vizualizarea creată anterior.

INSERT INTO v_emp_dept_***


VALUES (500, 'N2', 'E2',SYSDATE,’SA_REP’,30, 'Administrativ');

c) Care dintre coloanele vizualizării v_emp_dept_*** sunt actualizabile?

SELECT column_name, updatable


FROM user_updatable_columns
WHERE UPPER(table_name) = UPPER('v_emp_dept_***');

d) Adăugaţi tabelului emp_*** constrângerea de cheie externă care referă tabelul dept_***, apoi
verificaţi ce coloane din vizualizarea v_emp_dept_*** sunt actualizabile.

ALTER TABLE emp_***


ADD CONSTRAINT cp_emp_*** PRIMARY KEY (employee_id);

ALTER TABLE dept_***


ADD CONSTRAINT cp_dept1_*** PRIMARY KEY (department_id);

ALTER TABLE emp_***


ADD CONSTRAINT ce_emp1_*** FOREIGN KEY (department_id)
REFERENCES dept_***(department_id);

SELECT column_name, updatable


FROM user_updatable_columns
WHERE UPPER(table_name) = UPPER('v_emp_dept_***');

d) Recreaţi vizualizarea v_emp_dept_***, apoi verificaţi ce coloane sunt actualizabile.


DROP VIEW v_emp_dept_***;

CREATE VIEW v_emp_dept_*** AS


SELECT employee_id, last_name, email, hire_date, job_id,
e.department_id, department_name
FROM emp_*** e, dept_*** d
WHERE e.department_id =d.department_id;

SELECT column_name, updatable


FROM user_updatable_columns
WHERE UPPER(table_name) = UPPER('v_emp_dept_***');

f) Inseraţi o linie prin intermediul acestei vizualizări.


Obs. Tabelul ale cărui coloane sunt actualizabile este protejat prin cheie.

INSERT INTO v_emp_dept_*** (employee_id, last_name,email,hire_date,


job_id, department_id)
VALUES (500, 'N2', 'E2',SYSDATE,’SA_REP’,30);

g) Ce efect are o operaţie de ştergere prin intermediul vizualizării v_emp_dept_***? Comentaţi.

DELETE FROM v_emp_dept_***


WHERE employee_id = 500;

SELECT employee_id, last_name, hire_date, job_id, department_id


FROM emp_***
WHERE employee_id = 500;

SELECT department_id, department_name


FROM dept_***
WHERE department_id = 30;

6. Să se creeze vizualizarea v_dept_*** care să conţine codul şi numele departamentului, numărul de


angajaţi din departamentul respectiv şi suma alocată pentru plata salariilor. Această vizualizare
permite actualizări?

CREATE VIEW v_dept_*** (cod, nume, nr_angajati, val_salarii)


AS
SELECT e.department_id, department_name, COUNT(*) nr_angajati,
SUM(salary) val_salarii
FROM emp_*** e, dept_*** d
WHERE e.department_id = d.department_id
GROUP BY e.department_id, department_name;

7. a) Să se creeze vizualizarea v_emp30_*** care să conţină numele, emailul, data angajării, salariul,
codul jobului şi codul departamentului celor care lucrează în departamentul 30. În această
vizualizare nu se va permite modificarea sau inserarea liniilor ce nu sunt accesibile ei. Daţi un nume
constrângerii.
CREATE VIEW v_emp30_*** AS
SELECT employee_id, last_name, email, hire_date, salary, job_id,
department_id
FROM emp_***
WHERE department_id=30
WITH CHECK OPTION CONSTRAINT ck_option1_***;

b) Să se listeze structura şi conţinutul vizualizării v_emp30_***.

DESCRIBE v_emp30_***

SELECT * FROM v_emp30_***;

c) Să se încerce prin intermediul vizualizării inserarea unui angajat în departamentul 10 şi a unui


angajat în departamentul 30.

INSERT INTO v_emp30_***


VALUES (111, 'N1', 'E1',SYSDATE,1000,’SA_REP’,10);

INSERT INTO v_emp30_***


VALUES (11, 'N11', 'E11',SYSDATE,1000,’SA_REP’,30);

d) Să se încerce prin intermediul vizualizării modificarea departamentului unui angajat.

UPDATE v_emp30_***
SET department_id =20
WHERE employee_id = 11;
8. Să se creeze o vizualizare (v_dept_***) asupra tabelului dept_*** să nu permită efectuarea nici unei
operaţii LMD. Testaţi operaţiile de inserare, modificare şi ştergere asupra acestei vizualizări.

CREATE VIEW v_dept_*** AS


SELECT *
FROM dept_***
WITH READ ONLY;

9. Să se consulte informaţii despre vizualizarea v_dept_***. Folosiţi vizualizarea dicţionarului datelor


USER_VIEWS (coloanele VIEW_NAME şi TEXT).

Obs: Coloana TEXT este de tip LONG. În cazul selectării unei coloane de tip LONG trebuie utilizată
comanda SET LONG n pentru a seta numărul de caractere afişate.

SET LONG 200

SELECT view_name, text


FROM user_views
WHERE UPPER(view_name)=UPPER(’v_dept_***’);

 Definirea secvenţelor
O secvenţă este un obiect al bazei de date ce permite generarea de numere întregi unice cu scopul de a
fi folosiţi ca valori pentru cheia primară sau coloane numerice unice. Secvenţele sunt independente de
tabele.
Sintaxa comenzii CREATE SEQUENCE este:
CREATE SEQUENCE nume_secvenţă
[INCREMENT BY n]
[START WITH valoare_start]
[ {MAXVALUE valoare_maximă | NOMAXVALUE} ]
[ {MINVALUE valoare_minimă | NOMINVALUE} ]
[ {CYCLE | NOCYCLE} ]
[ {CACHE n | NOCACHE} ];
- INCREMENT BY specifică diferenţa dintre valorile succesive ale secvenţei (valoare implicită 1).
- START WITH specifică primul număr care va fi generat de secvenţă (valoare implicită 1).
- MAXVALUE, MINVALUE precizează valoarea maximă, respectiv minimă pe care o poate genera
secvenţa. Opţiunile NOMAXVALUE, NOMINVALUE sunt implicite. NOMAXVALUE specifică
valoarea maximă de 1027 pentru o secvenţă crescătoare şi -1 pentru o secvenţă descrescătoare.
NOMINVALUE specifică valoarea minimă 1 pentru o secvenţă crescătoare şi -1026 pentru o secvenţă
descrescătoare.
- CYCLE şi NOCYCLE specifică dacă secvenţa continuă să genereze numere după obţinerea valorii
maxime sau minime. NOCYCLE este opţiunea implicită.
- CACHE n precizează numărul de valori pe care server-ul Oracle le prealocă şi le păstrează în
memorie. În mod implicit, acest număr de valori este 20. Opţiunea CACHE pemite accesul mai rapid la
valorile secvenţei care sunt păstrate în memorie. Aceste valori sunt generate la prima referinţă asupra
secvenţei. Fiecare valoare din secvenţă se furnizează din secvenţa memorată. După utilizarea ultimei
valori prealocate secvenţei, următoarea solicitare a unei valori determină încărcarea unui alt set de
numere în memorie. Pentru a nu fi prealocate şi reţinute în memorie astfel de valori, se utilizează
opţiunea NOCACHE.
Pseudocoloanele NEXTVAL şi CURRVAL permit utilizarea secvenţelor.
- nume_secv.NEXTVAL returnează următoarea valoare a secvenţei, o valoare unică la fiecare
referire. Trebuie aplicată cel puţin o dată înainte de a folosi CURRVAL;
- nume_secv.CURRVAL returnează valoarea curentă a secvenţei.
Pseudocoloanele NEXTVAL şi CURRVAL se pot utiliza în:
- lista SELECT a comenzilor ce nu fac parte din subcereri;
- lista SELECT a unei cereri ce apare într-un INSERT;
- clauza VALUES a comenzii INSERT;
- clauza SET a comenzii UPDATE.
Pseudocoloanele NEXTVAL şi CURRVAL nu se pot utiliza:
- în lista SELECT a unei vizualizări;
- într-o comandă SELECT ce conţine DISTINCT, GROUP BY, HAVING sau ORDER BY;
- într-o subcerere în comenzile SELECT, UPDATE, DELETE;
- în clauza DEFAULT a comenzilor CREATE TABLE sau ALTER TABLE.
Ştergerea secvenţelor se realizează cu ajutorul comenzii DROP SEQUENCE.
DROP SEQUENCE nume_secv;
10. Să se creeze o secvenţă care are pasul de incrementare 10 şi începe de la 10, are ca valoare maximă
10000 şi nu ciclează.
CREATE SEQUENCE sec_***
INCREMENT BY 10
START WITH 10
MAXVALUE 10000
NOCYCLE;
11. Să se modifice toate liniile din tabelul emp_***, regenerând codul angajaţilor astfel încât să utilizeze
secvenţa sec_emp***. Să se anuleze modificările.

UPDATE emp_***
SET employee_id = sec_emp***.NEXTVAL;
ROLLBACK;
12. Să se introducă un nou salariat în tabelul emp_*** folosindu-se pentru codul salariatului secvenţa
creată.
INSERT INTO emp_*** (employee_id,last_name,email,hire_date,job_id)
VALUES(sec_emp***.NEXTVAL,'x','x',sysdate,'x');

13. Să se afişeze valoarea curentă a secvenţei.


SELECT sec_***.CURRVAL valoare
FROM DUAL;
Exerciţiu
a) Creaţi o secvenţă pentru generarea codurilor de departamente, seq_dept_***. Secvenţa va începe
de la 200, va creşte cu 10 la fiecare pas şi va avea valoarea maximă 20000, nu va cicla.
b) Să se selecteze informaţii despre secvenţele utilizatorului curent (nume, valoare minimă,
maximă, de incrementare, ultimul număr generat). Se va utiliza vizualizarea user_sequences.
c) Să se insereze o înregistrare nouă în DEPT_*** utilizând secvenţa creată.
d) Să se selecteze valoarea curentă a secvenţei.
e) Să se şteargă secvenţa.

 Definirea indecşilor
Un index este un obiect al unei scheme utilizator care este utilizat de server-ul Oracle pentru a mări
performanţele unui anumit tip de cereri asupra unui tabel.
Indecşii:
- evită scanarea completă a unui tabel la efectuarea unei cereri;
- reduc operaţiile de citire/scriere de pe disc utilizând o cale mai rapidă de acces la date şi anume
pointeri la liniile tabelului care corespund unor anumite valori ale unei chei (coloane);
- sunt independenţi de tabelele pe care le indexează, în sensul că dacă sunt şterşi nu afectează
conţinutul tabelelor sau comportamentul altor indecşi;
- sunt menţinuţi şi utilizaţi automat de către server-ul Oracle;
- sunt şterşi odată cu eliminarea tabelului asociat.
Indecşii pot fi creaţi :
- automat: la definirea unei constrângeri PRIMARY KEY sau UNIQUE;
- manual: cu ajutorul comenzii CREATE INDEX.
Se creează un index atunci când:
- o coloană conţine un domeniu mare de valori;
- o coloană conţine un număr mare de valori null;
- una sau mai multe coloane sunt folosite des în clauza WHERE sau în condiţii de join în programele
de aplicaţii;
- tabelul este mare şi de obicei cererile obţin mai puţin de 2%-4% din liniile tabelului;
- tabelul nu este modificat frecvent.
Sintaxa comenzii CREATE INDEX:
CREATE [UNIQUE] INDEX nume_index
ON tabel (coloana1 [, coloana2…]);
Modificarea unui index se face prin comanda ALTER INDEX.
Eliminarea unui index se face prin comanda: DROP INDEX nume_index;

14. Să se creeze un index neunic, emp_last_name_idx_***, asupra coloanei last_name din tabelul
emp_***.
15. Să se creeze indecşi unici asupra codului angajatului (employee_id) şi asupra combinaţiei last_name,
first_name, hire_date.
16. Creaţi un index neunic asupra coloanei department_id din emp_*** pentru a eficientiza joinurile
dintre acest tabel şi dept_***.
 Definirea sinonimelor
Pentru a simplifica accesul la obiecte se pot asocia sinonime acestora.
Crearea unui sinonim este utilă pentru a evita referirea unui obiect ce aparţine altui utilizator
prefixându-l cu numele utilizatorului şi pentru a scurta numele unor obiecte cu numele prea lung.
Comanda pentru crearea sinonimelor este:
CREATE [PUBLIC] SYNONYM nume_sinonim
FOR obiect;
Eliminarea sinonimelor se face prin comanda DROP SYNONYM nume_sinonim;
17. Creaţi un sinonim public se_*** pentru tabelul emp_***.
18. Creaţi un sinonim pentru vizualizarea v_dept_***.
19. Utilizând sinonimele create anterior, afişaţi informaţii depre salariţi şi despre departamente.

 Informaţii din dicţionarul datelor

Tipuri de vizualizări ale dicţionarului datelor:


ALL_* - obiecte accesibile utilizatorului curent,
DBA_* - obiecte accesibile numai administratorului,
USER_* - obiecte ale utilizatorului curent
1. Tabelele utilizatorului curent:

SELECT table_name
FROM user_tables
ORDER BY table_name;
2. Definiţiile şi numele constrângerilor:

SELECT constraint_name, constraint_type, search_condition, table_name


FROM user_constraints
WHERE table_name IN ('EMPLOYEES', 'DEPARTAMENTS’)
ORDER BY table_name, constraint_name;
3. Coloanele asociate unei constrângeri:

SELECT constraint_name, column_name


FROM user_cons_columns
WHERE table_name IN ('EMPLOYEES', 'DEPARTAMENTS’)
ORDER BY table_name, constraint_name;
4. Informaţii despre vizualizări:

SELECT view_name
FROM user_views;
5. Informaţii referitoare la secvenţe:

SELECT sequence_name, min_value, max_value, increment_by, last_number


FROM user_sequences;
6. Informaţii referitoare la indecşi:
SELECT index_name, table_name
FROM user_indexes;

SELECT index_name
FROM user_ind_columns;

SELECT a.index_name, a.column_name,a.column_position poz, b.uniqueness


FROM user_indexes b, user_ind_columns a
WHERE a.index_name = b.index_name
AND a.table_name = 'EMP_***';
7. Informaţii referitoare la sinonime:

SELECT synonym_name, table_owner, table_name


FROM user_synonyms;
LABORATOR 8 - SQL
SQL 1999 (SQL3). Operatori pe mulţimi

SQL 1999 (SQL3)


1. Comparaţie între sintaxa Oracle şi SQL 1999. Pentru join, sistemul Oracle9i oferă şi o sintaxă specifică, în
conformitate cu standardul SQL3 (SQL:1999).

Oracle SQL 1999


Equijoin Natural Join sau Inner Join
Outer Join Outer Join
Selfjoin Join ON
Non equijoin Join USING/ ON
Produs cartezian Cross Join

Sintaxa corespunzătoare standardului SQL3 este următoarea:

SELECT tabel_1.nume_coloană, tabel_2.nume_coloană


FROM tabel_1
[CROSS JOIN tabel_2]
| [NATURAL JOIN tabel_2]
| [JOIN tabel_2 USING (nume_coloană) ]
| [JOIN tabel_2 ON (tabel_1.nume_coloană = tabel_2.nume_coloană) ]
| [LEFT | RIGHT | FULL OUTER JOIN tabel_2
ON (tabel_1.nume_coloană = tabel_2.nume_coloană) ];

2. Tipuri de join compilante cu SQL 1999, implementate de Oracle9i:


a) Cross Join. Realizează produsul cartezian a tabelor locations şi countries.
SELECT city, l.country_id, country_name
FROM locations l
CROSS JOIN countries;
SELECT city, l.country_id, country_name
FROM locations l, countries;
b) Natural Join. Se bazează pe coloanele cu acelaşi nume din cele două tabele şi selectează liniile care au
aceleaşi valori pentru aceste coloane. Dacă tipurile de date ale coloanelor cu nume identice sunt diferite,
va fi returnată o eroare. Coloanele comune nu trebuie calificate cu aliasuri. Oracle9i oferă posibilitatea
completării automate a operaţiei de join. Coloanele având acelaşi nume în cele două tabele trebuie să nu
fie precedate de numele sau alias-ul tabelului corespunzător.
SELECT last_name, job_id, job_title
FROM employees
NATURAL JOIN jobs;
SELECT last_name, e.job_id, job_title
FROM employees e, jobs j
WHERE e.job_id=j.job_id;
c) Using/On Join.
JOIN tabl2 USING nume_coloană – Dacă avem coloane cu acelaşi nume, dar cu tipurile de date
diferite, atunci în loc de NATURAL JOIN se utilizează JOIN cu clauza USING. Similar cu
NATURAL JOIN, coloana de legătură nu trebuie calificată cu alias oriunde în cadrul cererii. Acest
tip de JOIN se mai foloseşte atunci când cele două tabele au mai multe coloane cu nume comun, dar
legătura dintre ele trebuie făcută doar utilizând câteva dintre acestea.
JOIN tab2 ON tab1.nume_coloană = tab2.nume_coloană – Dacă se doreşte particularizarea,
specificarea şi/sau evidenţierea clară a condiţiei de join se utilizează clauza ON. Aceasta separă
condiţia de JOIN de alte condiţii, făcând codul mai lizibil. Într-o înlănţuire de JOIN-uri acestea sunt
evaluate de la stânga la dreapta.
SELECT last_name, department_name, location_id
FROM employees JOIN departments
USING (department_id);
SELECT last_name, department_name, location_id
FROM employees e JOIN departments d
ON (e.department_id = d.department_id);
SELECT last_name, department_name, location_id
FROM employees e, departments d
WHERE e.department_id=d.department_id;
d) Outerjoin cu sau fără condiţii suplimentare (complet, la stânga sau la dreapta).
INNER vs. OUTER JOIN
Join-ul a două tabele ce returnează doar liniile ce corespund condiţiei de JOIN se numeşte INNER
JOIN.
Join-ul a două tabele ce returnează atât liniile ce corespund condiţiei de JOIN cât şi cele care nu
îndeplinesc această condiţie se numeşte OUTER JOIN. Liniile dintr-un tabel care nu au
corespondent în celălalt tabel sunt completate cu valori NULL.
LEFT/RIGHT/FULL OUTER JOIN tab_1 ON (tab1.nume_coloană = tab2.nume_coloană)
efectuează outer join la stânga, dreapta, respectiv în ambele părţi pe baza condiţiei exprimate în
clauza ON.
Un join care returnează rezultatele unui inner join, dar şi cele ale outer join-urilor la stânga şi la
dreapta se numeşte full outer join.
SELECT last_name, department_name, location_id
FROM employees e
LEFT OUTER JOIN departments d
ON (e.department_id = d.department_id);
SELECT last_name, department_name, location_id
FROM employees e
RIGHT OUTER JOIN departments d
ON (e.department_id = d.department_id);
SELECT last_name, department_name, location_id
FROM employees e
FULL OUTER JOIN departments d
ON (e.department_id = d.department_id);
Exerciţiu
Alegeţi 10 cereri care utilizează join-uri din laboratorul 3 şi rezolvaţi aceste cereri cu SQL standard.

Operatori pe mulţimi

Operatorii pe mulţimi combină rezultatele obţinute din două sau mai multe interogări. Cererile care
conţin operatori pe mulţimi se numesc cereri compuse.
Există patru operatori pe mulţimi: UNION, UNION ALL, INTERSECT şi MINUS.
Operatorul UNION returnează toate liniile selectate de două cereri, eliminând duplicatele. Acest
operator nu ignoră valorile null şi are precedenţă mai mică decât operatorul IN.
Operatorul UNION ALL returnează toate liniile selectate de două cereri, fără a elimina duplicatele.
Precizările făcute asupra operatorului UNION sunt valabile şi în cazul operatorului UNION ALL. În
cererile asupra cărora se aplică UNION ALL nu poate fi utilizat cuvântul cheie DISTINCT.
Operatorul INTERSECT returnează toate liniile comune cererilor asupra cărora se aplică. Acest
operator nu ignoră valorile null.
Operatorul MINUS determină liniile returnate de prima cerere care nu apar în rezultatul celei de-a doua
cereri. Pentru ca operatorul MINUS să funcţioneze, este necesar ca toate coloanele din clauza WHERE
să se afle şi în clauza SELECT.

4. Să se creeze o cerere prin care să se afişeze numărul total de angajaţi şi, din acest total, numărul celor care
au fost angajaţi în 1997.
SELECT COUNT(*)|| ' nr_total ' numar
FROM employees
UNION
SELECT COUNT(*)|| ' nr_1980 ' numar_1997
FROM employees
WHERE TO_CHAR(hire_date,'YYYY')=1997;
5. Utilizând operatorul UNION, să se listeze codul salariaţilor, codul şi numele departamentelor.
SELECT employee_id, department_id, TO_CHAR(NULL) nume
FROM employees
UNION
SELECT TO_NUMBER(NULL), department_id, department_name
FROM departments;
6. Să se afişeze reuniunea cu duplicate, respectiv fără duplicate a următoarelor mulţimi:
- codul, codul jobului şi codul departamentului salariaţilor din tabelul employees;
- codul, codul jobului şi codul departamentului salariaţilor din tabelul job_history.
Ordonaţi rezultatul după codul angajatului.
Observaţie: Clauza ORDER BY trebuie să apară la final.
7. Să se afişeze numele departamentelor şi numele angajaţilor. Se vor afişa şi departamenntele în care nu
lucrează nimeni, respectiv şi angajaţii care nu lucrează în nici un departament (full outer join).
Utilizaţi operatorul UNION. Ce rezultat s-ar obţine dacă am utiliza operatorul UNION ALL?
8. Să se obţină codul, codul jobului şi salariul pentru toţi angajaţii, incluzând:
- angajaţii curenţi;
- angajaţii care au avut şi alte joburi; pentru aceştia valoarea salariului va fi null.
Ordonaţi rezultatul după codul salariatului.
9. Să se obţină, folosind operatorul INTERSECT, angajaţii care au salariul < 3000 şi al căror nume conţine
litera a pe poziţia 3.
SELECT employee_id, last_name
FROM employees
WHERE salary<3000
INTERSECT
SELECT employee_id, last_name
FROM employees
WHERE UPPER(last_name) LIKE '__A%';
10. Să se obţină codul, codul jobului şi codul departamentului angajaţilor care în trecut au mai lucrat pe acelaşi
job şi în acelaşi departament ca în prezent. Utilizaţi operatorul INTERSECT.
11. Modificaţi cererea anterioară astfel încât să obţineţi numele angajaţilor care îndeplinesc condiţia impusă.
12. Să se afişeze codurile departamentelor care nu au angajaţi, implementând operatorul MINUS.
SELECT department_id
FROM departments
MINUS
SELECT DISTINCT department_id
FROM employees;

13. Să se obţină codurile angajaţilor care nu au avut joburi anterioare.


a. Utilizaţi operatorul MINUS.
b. Utilizaţi operatorul NOT IN.
14. Să se obţină codul, codul jobului şi codul departamentului angajaţilor care în trecut au lucrat pe alte joburi
sau în alte departamente faţă de prezent. Utilizaţi operatorul MINUS.

15. a. Să se obţină codurile locaţiilor în care nu există departamente. Utilizaţi operatorul MINUS.
b. Daţi o altă metodă de rezolvare.
Limbajul de definire a datelor (DDL). CREATE, ALTER, DROP
Definirea tabelelor
CREATE TABLE [schema.]nume_tabel (
nume_coloana tip_de_date [DEFAULT1 expr], ...);

CREATE TABLE nume_tabel [(col1, col2...)]


AS subcerere;
1. CreaŃi tabelul salariat_*** având următoarea structură:
Nume Caracteristici Tip
cod_ang NOT NULL NUMBER(4)
nume VARCHAR2(25)
prenume VARCHAR2(25)
functia VARCHAR2(20)
sef NUMBER(4)
Valoare implicită data
data_angajarii DATE
curentă
varsta NUMBER(2)
email CHAR(50)
salariu Valoare implicită 0 NUMBER(9,2)

CREATE TABLE salariat_*** (


cod_ang NUMBER(4) NOT NULL,
nume VARCHAR2(25),
prenume VARCHAR2(25),
functia VARCHAR2(20),
sef NUMBER(4),
data_angajarii DATE DEFAULT SYSDATE,
varsta NUMBER(2),
email CHAR(50),
salariu NUMBER(9,2) DEFAULT 0);
2. AfişaŃi structura tabelului creat anterior.
3. Se dau următoarele valori:
COD _ANG NUME PRENUME FUNCTIA SEF DATA_ANG VARSTA EMAIL SALARIU
1 ..... ..... director null ........ 30 ..... 5500
2 ..... ..... functionar 1 ......... 25 ..... 0
3 ..... ...... economist 1 ......... 45 ..... 3000
4 ..... .... functionar 1 ......... 35 ...... 1000
4. InseraŃi în tabelul salariat_*** prima înregistrare din tabelul de mai sus fără să precizaŃi lista de coloane
în comanda INSERT.
5. InseraŃi a doua înregistrare folosind o listă de coloane din care excludeŃi data_angajarii şi salariul care au
valori implicite. ObservaŃi apoi rezultatul.
6. InseraŃi înregistrările 3 şi 4.
7. CreaŃi tabelul functionar_*** care să conŃină funcŃionarii din tabelul salariat_***, având următoarele
coloane: codul, numele, salariul anual şi data angajării. VerificaŃi cum a fost creat tabelul şi ce date
conŃine.

1
Modificarea tabelelor

8. AdăugaŃi o nouă coloană tabelului salariat_*** care să conŃină data naşterii.

ALTER TABLE salariat_***


ADD (datan DATE);

9. ModificaŃi dimensiunea coloanei nume la 30 si pe cea a salariului la 12 cu 3 zecimale.

ALTER TABLE salariat_***


MODIFY (nume VARCHAR2(30), salariu NUMBER(12,3));

10. ModificaŃi tipul coloanei email la VARCHAR2.


11. ModificaŃi valoarea implicită a coloanei data_angajarii la data sistemului + o zi.
12. EliminaŃi coloana varsta din tabelul salariat_***.

ALTER TABLE salariat_***


DROP COLUMN varsta;

Redenumirea şi eliminarea tabelelor

RENAME nume_tabel TO nume_nou;


DROP TABLE nume_tabel;
13. RedenumiŃi tabelul functionar_*** cu funct_***.
14. RecreaŃi tabelul functionar_*** utilizând tabelul funct_***..
15. EliminaŃi tabelul funct_***.

Constrângeri

Adăugarea constrângerilor la crearea tabelului (CREATE TABLE)


CREATE TABLE [schema.]nume_tabel (
nume_coloana tip_de_date [DEFAULT expr]
[constrangere_de_coloana], ...
..[constrangere la nivel de tabel])
16. ŞtergeŃi şi apoi creaŃi din nou tabelul salariat_*** cu următoarea structură.
NUME TIP CONSTRÂNGERE
cod_ang NUMBER(4) Cheie primară
nume VARCHAR2(25) NOT NULL
prenume VARCHAR2(25)
data_nasterii DATE data_nasterii<data_angajarii
functia VARCHAR2(9) NOT NULL
Referă ca şi cheie externă cod_ang din acelaşi
sef NUMBER(4)
tabel
data_angajarii DATE
email VARCHAR2(20) unic
salariu NUMBER(12,3) >0
cod_dept NUMBER(4) CombinaŃia NUME + PRENUME să fie unică

2
ObservaŃie:
Constrângerile de tip CHECK se pot implementa la nivel de coloană doar dacă nu referă o altă coloană a
tabelului.

DROP TABLE salariat_***;


CREATE TABLE salariat_*** (
cod_ang NUMBER(4) PRIMARY KEY,
nume VARCHAR2(25) NOT NULL,
prenume VARCHAR2(25),
data_nasterii DATE,
functia VARCHAR2(9) NOT NULL,
sef NUMBER(4) REFERENCES salariat_*** (cod_ang),
data_angajarii DATE DEFAULT SYSDATE,
email VARCHAR2(20) UNIQUE,
salariu NUMBER(9,2) CHECK (salariu > 0),
cod_dep NUMBER(4),
CONSTRAINT const_c_*** CHECK (data_angajarii > data_nasterii),
CONSTRAINT const_u_*** UNIQUE (nume,prenume,data_nasterii));
17. ŞtergeŃi tabelul salariat_***, iar apoi recreaŃi-l implementând toate constrângerile la nivel de tabel.
ObservaŃie: Constrângerea de tip NOT NULL se poate declara doar la nivel de coloană.

DROP TABLE salariat_***;


CREATE TABLE salariat_*** (
cod_ang NUMBER(4),
nume VARCHAR2(25) NOT NULL,
prenume VARCHAR2(25),
data_nasterii DATE,
functia VARCHAR2(9) NOT NULL,
sef NUMBER(4),
data_angajarii DATE DEFAULT SYSDATE,
email VARCHAR2(20),
salariu NUMBER(9,2),
cod_dep NUMBER(4),
CONSTRAINT ccp_*** PRIMARY KEY (cod_ang),
CONSTRAINT cce_*** FOREIGN KEY (sef) REFERENCES salariat_*** (cod_ang),
CONSTRAINT cu1_*** UNIQUE (email),
CONSTRAINT cc1_*** CHECK (data_angajarii > data_nasterii),
CONSTRAINT cc2_***CHECK (salariu > 0),
CONSTRAINT cu2_*** UNIQUE (nume,prenume,data_nasterii));
18. CreaŃi tabelul departament_*** care să aibă următoarea structură.

NUME TIP CONSTRÂNGERI


COD_DEP NUMBER(4) Cheie primară
NUME VARCHAR2(20) Not null
ORAS VARCHAR2(25)

Adăugarea constrângerilor ulterior creării tabelului, eliminarea, activarea sau dezactivarea


constrângerilor (ALTER TABLE)

- adaugă constrângeri
ALTER TABLE nume_tabel
ADD [CONSTRAINT nume_constr] tip_constr (coloana);
- elimină constrângeri
ALTER TABLE nume_tabel
3
DROP [CONSTRAINT nume_constr] tip_constr (coloana);
- activare/dezactivare constrângere
ALTER TABLE nume_tabel
MODIFY CONSTRAINT nume_constr ENABLE|DISABLE;
sau
ALTER TABLE nume_tabel
ENABLE| DISABLE nume_constr;
19. InseraŃi o nouă înregistrare în salariat_*** de forma:
cod_dep
cod nume prenume data_n functia sef data_ang email salariu
11-JUN-
2 N2 P2 economist 1 Sysdate E2 2000 10
1960
Ce observaŃi? IntroduceŃi înregistrarea dar specificând valoarea NULL pentru coloana sef.
20. ÎncercaŃi să adăugaŃi o constrângere de cheie externă pe cod_dep din salariat_***. Ce observaŃi?

ALTER TABLE salariat_***


ADD CONSTRAINT cce2_*** FOREIGN KEY (cod_dep) REFERENCES departament_***
(cod_dep);

21. InseraŃi o nouă înregistrare în departament_***. Apoi adăugaŃi constrângerea de cheie externă definită
anterior.

cod_dep nume loc


10 Economic Bucuresti

22. InseraŃi noi înregistrări în salariat_***, respectiv în departament_***. Care trebuie să fie ordinea de
inserare?
cod_dep
cod nume prenume data_n functia sef data_ang email salariu
11-JUN- 20
3 N3 P3 jurist 2 Sysdate E3 2500
1967

cod_dep nume loc


20 Juritic Constanta

23. ŞtergeŃi departamentul 20 din tabelul departament_***. Ce observaŃi?


24. ŞtergeŃi constrângerea cce2_***. RecreaŃi această constrângere adăugând opŃiunea ON DELETE
CASCADE.
25. ŞtergeŃi departamentul 20 din tabelul departament_***. Ce observaŃi în tabelul salariat_***? AnulaŃi
modificările.
26. ŞtergeŃi constrângerea cce2_***. RecreaŃi această constrângere adăugând opŃiunea ON DELETE SET
NULL.
27. ÎncercaŃi să ştergeŃi departamentul 10 din tabelul departament_***. Ce observaŃi?

Consultarea dicŃionarului datelor

InformaŃii despre tabelele create se găsesc în vizualizările :


• USER_TABLES – informaŃii complete despre tabelele utilizatorului curent.
4
• ALL_TABLES – informaŃii complete despre tabelele tuturor utilizatorilor.
• COLS – informaŃii despre coloane.
• TAB – informaŃii de bază despre tabelele existente în schema utilizatorului curent.

InformaŃii despre constrângeri găsim în :


• USER_CONSTRAINTS – informaŃii despre constrângerile definite de utilizatorul curent;
• ALL_CONSTRAINTS – informaŃii despre cosntrângerile definite de toŃi utilizatorii.

Definirea vizualizărilor
Sintaxa simplificată a comenzii CREATE VIEW este:
CREATE [OR REPLACE] [FORCE | NOFORCE] VIEW nume_view [(alias, alias, ..)]
AS subcerere
[WITH CHECK OPTION [CONSTRAINT nume_constr]]
[WITH READ ONLY [CONSTRAINT nume_constr]];
- FORCE permite crearea vizualizarea înainte de a defini tabelele de bază;
- subcererea poate fi oricât de complexă dar nu poate conŃine clauza ORDER BY;
- WITH CHECK OPTION permite inserarea şi modificarea prin intermediul vizualizării numai a
liniilor ce sunt accesibile vizualizării; dacă lipseşte numele constrângerii atunci sistemul asociază
un nume implicit de tip SYS_Cn acestei constrângeri;
- WITH READ ONLY asigură că prin intermediul vizualizării nu se pot executa operaŃii LMD.
Eliminarea unei vizualizări se face prin comanda DROP VIEW :
DROP VIEW nume_viz;

1. Să se creeze vizualizarea v_emp_*** care să conŃină codul şi numele salariaŃilor din tabelul emp_***. Să
se afişeze conŃinutul acesteia. Să se insereze o nouă înregistrare în această vizualizare. Ce observaŃi? Să
se şteargă vizualizarea v_emp_***.

CREATE VIEW v_emp_*** (cod, nume)


AS
SELECT employee_id, last_name
FROM emp_***;

INSERT INTO v_emp_***


VALUES (400,’N1’);

DROP VIEW v_emp_***;

2. Să se creeze vizualizarea v_emp_*** care să conŃină codul, numele, emailul, data angajării, salariul şi
codul jobului salariaŃilor din tabelul emp_***. Să se analizeze structura şi conŃinutul vizualizării. Să se
insereze o nouă înregistrare în această vizualizare. Să se verifice că noua înregistrare a fost inserată şi în
tabelul de bază.

CREATE VIEW v_emp_***


AS
SELECT employee_id, last_name, email, hire_date, salary,job_id
FROM emp_***;

DESC v_emp_***

SELECT * FROM v_emp_***;

5
INSERT INTO v_emp_***
VALUES (400,’N1’,’E1’,SYSDATE,5000,’SA_REP’);

SELECT employee_id, last_name, email, hire_date, salary, job_id


FROM emp_***;
3. Să se mărească cu 1000 salariul angajatului având codul 400 din vizualizarea creată anterior. Ce efect va
avea această acŃiune asupra tabelului de bază?
4. Să se şteargă angajatul având codul 400 din vizualizarea creată anterior. Ce efect va avea această acŃiune
asupra tabelului de bază?

5. a) Să se creeze vizualizarea v_emp_dept_*** care să conŃină employee_id, last_name, hire_date,


job_id, department_id din tabelul emp_*** şi coloana department_name din tabelul dept_***.
b) Să încerce inserarea înregistrării (500, 'N2', 'E2',SYSDATE,’SA_REP’,30, 'Administrativ') în
vizualizarea creată anterior.
c) Care dintre coloanele vizualizării v_emp_dept_*** sunt actualizabile?

SELECT column_name, updatable


FROM user_updatable_columns
WHERE UPPER(table_name) = UPPER('v_emp_dept_***');

d) AdăugaŃi tabelului emp_*** constrângerea de cheie externă care referă tabelul dept_***, apoi
verificaŃi ce coloane din vizualizarea v_emp_dept_*** sunt actualizabile.
e) RecreaŃi vizualizarea v_emp_dept_***, apoi verificaŃi ce coloane sunt actualizabile.
f) InseraŃi o linie prin intermediul acestei vizualizări.
Obs. Tabelul ale cărui coloane sunt actualizabile este protejat prin cheie.
g) Ce efect are o operaŃie de ştergere prin intermediul vizualizării v_emp_dept_***? ComentaŃi.

6. Să se creeze vizualizarea v_dept_*** care să conŃine codul şi numele departamentului, numărul de


angajaŃi din departamentul respectiv şi suma alocată pentru plata salariilor. Această vizualizare permite
actualizări?

CREATE VIEW v_dept_*** (cod, nume, nr_angajati, val_salarii)


AS
SELECT e.department_id, department_name, COUNT(*) nr_angajati,
SUM(salary) val_salarii
FROM emp_*** e, dept_*** d
WHERE e.department_id = d.department_id
GROUP BY e.department_id, department_name;

7. a) Să se creeze vizualizarea v_emp30_*** care să conŃină numele, emailul, data angajării, salariul,
codul jobului şi codul departamentului celor care lucrează în departamentul 30. În această
vizualizare nu se va permite modificarea sau inserarea liniilor ce nu sunt accesibile ei. DaŃi un nume
constrângerii.

CREATE VIEW v_emp30_*** AS


SELECT employee_id, last_name, email, hire_date, salary, job_id,
department_id
FROM emp_***
WHERE department_id=30
WITH CHECK OPTION CONSTRAINT ck_option1_***;

b) Să se listeze structura şi conŃinutul vizualizării v_emp30_***.

6
c) Să se încerce prin intermediul vizualizării inserarea unui angajat în departamentul 10 şi a unui angajat
în departamentul 30.
d) Să se încerce prin intermediul vizualizării modificarea departamentului unui angajat.

UPDATE v_emp30_***
SET department_id =20
WHERE employee_id = 11;
8. Să se creeze o vizualizare (v_dept_***) asupra tabelului dept_*** să nu permită efectuarea nici unei
operaŃii LMD. TestaŃi operaŃiile de inserare, modificare şi ştergere asupra acestei vizualizări.

CREATE VIEW v_dept_*** AS


SELECT *
FROM dept_***
WITH READ ONLY;

9. Să se consulte informaŃii despre vizualizarea v_dept_***. FolosiŃi vizualizarea dicŃionarului datelor


USER_VIEWS (coloanele VIEW_NAME şi TEXT).

Obs: Coloana TEXT este de tip LONG. În cazul selectării unei coloane de tip LONG trebuie utilizată
comanda SET LONG n pentru a seta numărul de caractere afişate.

SET LONG 200

SELECT view_name, text


FROM user_views
WHERE UPPER(view_name)=UPPER(’v_dept_***’);

Definirea secvenŃelor
Sintaxa comenzii CREATE SEQUENCE este:
CREATE SEQUENCE nume_secvenŃă
[INCREMENT BY n]
[START WITH valoare_start]
[ {MAXVALUE valoare_maximă | NOMAXVALUE} ]
[ {MINVALUE valoare_minimă | NOMINVALUE} ]
[ {CYCLE | NOCYCLE} ]
[ {CACHE n | NOCACHE} ];
Ştergerea secvenŃelor se realizează cu ajutorul comenzii DROP SEQUENCE.
DROP SEQUENCE nume_secv;
10. Să se creeze o secvenŃă care are pasul de incrementare 10 şi începe de la 10, are ca valoare maximă
10000 şi nu ciclează.

CREATE SEQUENCE sec_***


INCREMENT BY 10
START WITH 10
MAXVALUE 10000
NOCYCLE;
11. Să se modifice toate liniile din tabelul emp_***, regenerând codul angajaŃilor astfel încât să utilizeze
secvenŃa sec_emp***. Să se anuleze modificările.

7
UPDATE emp_***
SET employee_id = sec_emp***.NEXTVAL;
ROLLBACK;

12. Să se introducă un nou salariat în tabelul emp_*** folosindu-se pentru codul salariatului secvenŃa creată.
13. Să se afişeze valoarea curentă a secvenŃei.

SELECT sec_***.CURRVAL valoare


FROM DUAL;

ExerciŃiu
a) CreaŃi o secvenŃă pentru generarea codurilor de departamente, seq_dept_***. SecvenŃa va începe de
la 200, va creşte cu 10 la fiecare pas şi va avea valoarea maximă 20000, nu va cicla.
b) Să se selecteze informaŃii despre secvenŃele utilizatorului curent (nume, valoare minimă, maximă,
de incrementare, ultimul număr generat). Se va utiliza vizualizarea user_sequences.
c) Să se insereze o înregistrare nouă în DEPT_*** utilizând secvenŃa creată.
d) Să se selecteze valoarea curentă a secvenŃei.
e) Să se şteargă secvenŃa.

Definirea indecşilor
Sintaxa comenzii CREATE INDEX:
CREATE [UNIQUE] INDEX nume_index
ON tabel (coloana1 [, coloana2…]);
Modificarea unui index se face prin comanda ALTER INDEX.
Eliminarea unui index se face prin comanda: DROP INDEX nume_index;

14. Să se creeze un index neunic, emp_last_name_idx_***, asupra coloanei last_name din tabelul
emp_***.
15. Să se creeze indecşi unici asupra codului angajatului (employee_id) şi asupra combinaŃiei last_name,
first_name, hire_date.
16. CreaŃi un index neunic asupra coloanei department_id din emp_*** pentru a eficientiza joinurile
dintre acest tabel şi dept_***.

Definirea sinonimelor
Comanda pentru crearea sinonimelor este:
CREATE [PUBLIC] SYNONYM nume_sinonim
FOR obiect;
Eliminarea sinonimelor se face prin comanda DROP SYNONYM nume_sinonim;
17. CreaŃi un sinonim public se_*** pentru tabelul emp_***.
18. CreaŃi un sinonim pentru vizualizarea v_dept_***.
19. Utilizând sinonimele create anterior, afişaŃi informaŃii depre salariŃi şi despre departamente.

8
Limbajul de interogare al datelor (DQL). SELECT
CERERI MONOTABEL

1. AnalizaŃi sintaxa simplificată a comenzii SELECT. Care dintre clauze sunt obligatorii?
SELECT { [ {DISTINCT | UNIQUE} | ALL] lista_campuri | *}
FROM [nume_schemă.]nume_obiect ]
[, [nume_schemă.]nume_obiect …]
[WHERE condiŃie_clauza_where]
[GROUP BY expresie [, expresie …]
[HAVING condiŃie_clauza_having] ]
[ORDER BY {expresie | poziŃie} [, {expresie | poziŃie} …] ]

2. Să se listeze structura tabelelor din schema HR (EMPLOYEES, DEPARTMENTS, JOB_HISTORY,


JOBS, LOCATIONS, COUNTRIES, REGIONS), observând tipurile de date ale coloanelor.
Obs: Se va utiliza comanda SQL*Plus
DESCRIBE nume_tabel
3. Să se listeze conŃinutul tabelelor din schema considerată, afişând valorile tuturor câmpurilor.
Obs: Se va utiliza comanda SQL
SELECT * FROM nume_tabel;
4. Să se obŃină încă o dată rezultatul cererii precedente, fără a rescrie cererea.
Obs: Ultima comandă SQL lansată de către client este păstrată în buffer-ul SQL.
Pentru rularea acesteia se utilizează “/” sau RUN.
5. ListaŃi structura tabelului EMPLOYEES şi apoi daŃi comanda RUN (sau “/”). Ce observaŃi? Comenzile
SQL*Plus sunt păstrate în buffer?
DESC employees
RUN
6. Să se afişeze codul angajatului, numele, codul job-ului, data angajării. SalvaŃi instrucŃiunea SQL într-un
fişier numit p1_14.sql.
Obs: Pentru salvarea ultimei comenzi SQL se utilizează comanda SAVE. Precizarea extensiei „.sql” a
fişierului nu este obligatorie.
SELECT employee_id, last_name, job_id, hire_date
FROM employees;
SAVE z:\…\ p1_14.sql
7. ReexecutaŃi cererea folosind fişierul p1_14.sql.
START z:\…\ p1_14.sql
sau
@ z:\…\ p1_14.sql
8. EditaŃi fişierul p1_14.sql, adăugând coloanelor câte un alias (cod, nume, cod job, data angajarii).
EDIT z:\…\ p1_14.sql
9. Să se listeze, cu şi fără duplicate, codurile job-urilor din tabelul EMPLOYEES.
Obs. DISTINCT = UNIQUE
10. Să se afişeze numele concatenat cu prenumele, separate prin spaŃiu. EtichetaŃi coloana “Nume si
prenume”.

9
Obs: Operatorul de concatenare este “||”. Şirurile de caractere se specifică între apostrofuri (NU
ghilimele, caz în care ar fi interpretate ca alias-uri).
SELECT last_name|| ' ' || first_name " Nume si prenume "
FROM employees;
11. Să se listeze numele şi salariul angajaŃilor care câştigă mai mult de 10000 $.
SELECT last_name, salary
FROM employees
WHERE salary > 10000;
12. Să se modifice cererea anterioară astfel încât să afişeze numele şi salariul pentru toŃi angajaŃii al căror
salariu este cuprins între 5000$ şi10000$.
Obs: Pentru testarea apartenenŃei la un domeniu de valori se poate utiliza operatorul
[NOT] BETWEEN valoare1 AND valoare2
SELECT last_name, salary
FROM employees
WHERE salary BETWEEN 5000 AND 10000;
13. Să se creeze o cerere pentru a afişa numele angajatului şi numărul departamentului pentru angajatul 104.
14. Să se afişeze numele şi salariul pentru toŃi angajaŃii din departamentele 10 sau 30, în ordine alfabetică a
numelor.
Obs: ApartenenŃa la o mulŃime finită de valori se poate testa prin intermediul operatorului IN, urmat de
lista valorilor între paranteze şi separate prin virgule:
expresie IN (valoare_1, valoare_2, …, valoare_n)
15. Să listeze numele şi salariile angajaŃilor care câştigă mai mult de 10000 $ şi lucrează în departamentul
10 sau 30. Se vor eticheta coloanele drept Angajat si Salariu lunar.
16. Care este data curentă?
Obs: Pseudocoloana care returnează data curentă este SYSDATE. Pentru completarea sintaxei obligatorii
a comenzii SELECT, se utilizează tabelul DUAL:
SELECT SYSDATE
FROM dual;
Datele calendaristice pot fi formatate cu ajutorul funcŃiei TO_CHAR(data, format), unde formatul poate
fi alcătuit dintr-o combinaŃie a următoarelor elemente:
Element SemnificaŃie
D Numărul zilei din săptămână (duminică=1;
luni=2; …sâmbătă=6).
DD Numărul zilei din lună.
DDD Numărul zilei din an.
DY Numele zilei din săptămână, printr-o
abreviere de 3 litere (MON, THU etc.)
DAY Numele zilei din săptămână, scris în
întregime.
MM Numărul lunii din an.
MON Numele lunii din an, printr-o abreviere de 3
litere (JAN, FEB etc.)
MONTH Numele lunii din an, scris în întregime.
Y Ultima cifră din an
YY, YYY, YYYY Ultimele 2, 3, respectiv 4 cifre din an.
YEAR Anul, scris în litere (ex: two thousand
four).
HH12, HH24 Orele din zi, între 0-12, respectiv 0-24.
10
MI Minutele din oră.
SS Secundele din minut.
SSSSS Secundele trecute de la miezul nopŃii.

17. Să se afişeze numele şi data angajării pentru fiecare salariat care a fost angajat în 1987. Se cer 2 soluŃii:
una în care se lucrează cu formatul implicit al datei şi alta prin care se formatează data.
Varianta1:
SELECT first_name, last_name, hire_date
FROM employees
WHERE hire_date LIKE (‘%87’);
Varianta 2:
SELECT first_name, last_name, hire_date
FROM employees
WHERE TO_CHAR(hire_date, ‘YYYY’)=’1987’;
Sunt obligatorii ghilimelele de la şirul ‘1987’? Ce observaŃi?
18. Să se afişeze numele şi job-ul pentru toŃi angajaŃii care nu au manager.
SELECT last_name, job_id
FROM employees
WHERE manager_id IS NULL;
19. Să se afişeze numele, salariul şi comisionul pentru toŃi salariaŃii care câştigă comisioane. Să se sorteze
datele în ordine descrescătoare a salariilor, iar pentru cei care au acelaşi salariu în ordine crescătoare a
comisioanelor.
SELECT last_name, salary, commission_pct
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC, commission_pct ASC;
20. Să se listeze numele tuturor angajaŃilor care au a treia litera din nume 'a'.
Obs: Pentru a forma măştile de caractere utilizate împreună cu operatorul LIKE cu scopul de a compara
şirurile de caractere, se utilizează:
% - reprezentând orice şir de caractere, inclusiv şirul vid;
_ (underscore) – reprezentând un singur caracter.
SELECT DISTINCT last_name
FROM employees
WHERE last_name LIKE '__a%';

21. Folosind data curentă să se afişeze următoarele informaŃii:


- numele zilei, numărul zilei din săptămână, numărul zilei din luna, respectiv numărul zilei din an;
- numărul lunii din an, numele lunii cu abreviere la 3 caractere, respectiv numele complet al lunii;
- ora curentă (ora, minute, secunde).
22. Să se listeze numele departamentelor care funcŃionează în locaŃia având codul 1700 şi al căror manager
este cunoscut.
23. Să se afişeze codurile departamentelor în care lucrează salariaŃi.
24. Să se afişeze numele şi prenumele salariaŃilor angajaŃi în luna mai 1987.
25. Să se listeze codurile angajaŃilor care au avut şi alte joburi faŃă de cel prezent. Să se ordoneze rezultatul
descrescător după codul angajatului.
26. Să se afişeze numele şi data angajării pentru cei care lucrează în departamentul 80 şi au fost angajaŃi în
luna martie a anului 1997.
27. Să se afişeze numele joburilor care permit un salariu cuprins între 8300$ şi 14000$.
11
28. Care este grila de salarizare pentru un salariu de 10000$?
29. Să se listeze numele tuturor angajaŃilor care au 2 litere 'L' în nume şi lucrează în departamentul 30 sau
managerul lor este 123.
30. Să se afişeze numele, job-ul şi salariul pentru toŃi salariaŃii al căror job conŃine şirul 'CLERK' sau 'REP'
şi salariul nu este egal cu 1000, 2000 sau 3000 $.
31. Să se afişeze numele, salariul şi comisionul pentru toŃi angajaŃii al căror salariu este mai mare decât de 5
ori valoarea comisionului (salary*commission_pct*5).

FUNCłII SQL (single-row)

Principalele funcŃii SQL pot fi clasificate în următoarele categorii:


• FuncŃii single-row
• FuncŃii multiple-row (funcŃii agregat)
FuncŃiile single-row returnează câte o linie rezultat pentru fiecare linie a tabelului sau vizualizării
interogate. Aceste funcŃii pot apărea în listele SELECT, clauzele WHERE, START WITH, CONNECT BY
şi HAVING.

1. AnalizaŃi următoarele funcŃii pentru prelucrarea şirurilor de caractere:


FuncŃie SemnificaŃie Exemplu
Converteşte un şir de caractere
LOWER (expresie) LOWER ('AbCdE') = 'abcde'
la minuscule.
Converteşte un şir de caractere
UPPER (expresie) UPPER ('AbCdE') = 'ABCDE'
la majuscule.
Converteşte un şir de caractere
la un şir care începe cu
INITCAP (expresie) INITCAP ('AbCdE') = 'Abcde'
majusculă şi continuă cu
minuscule.
Extrage din expresia de tip şir
de caractere, n caractere
începând cu poziŃia m. Dacă
SUBSTR ('AbCdE', 2, 2) = 'bC'
lipseşte argumentul n, atunci
SUBSTR ('AbCdE', 2) = 'bCdE'
SUBSTR (expresie, m[, n]) extrage toate caracterele până la
SUBSTR ('AbCdE', -3,2) = 'Cd'
sfârşitul şirului. Dacă m este
SUBSTR ('AbCdE', -3) = 'CdE'
negativ numărătoarea poziŃiilor
începe de la sfârşitul şirului de
caractere spre început.
Returnează numărul de
LENGTH (expresie) LENGTH ('AbCdE') = 5
caractere al expresiei.
Returnează poziŃia la care se
găseşte a n-a ocurentă a
INSTR (LOWER('AbC aBcDe'), 'ab', 5, 2)
expresiei 'expr1' în cadrul
=0
INSTR (expresie, expr1[, m][, n]) expresiei 'expresie', căutarea
INSTR (LOWER('AbCdE aBcDe'), 'ab', 5)
începând de la poziŃia m. Daca
=7
m sau n lipsesc, valorile
implicite sunt 1 pentru ambele.
Reversul funcŃiilor LPAD,
RPAD. Trunchează expresia
RTRIM ('abcdeXXXX', 'X')
LTRIM (expresie[, expr1]) sau caracter la stânga sau la dreapta
= 'abcde'
RTRIM (expresie[, expr1]) prin eliminarea succesivă a
LTRIM (' abcde') = 'abcde'
caracterelor din expresia expr1.
Implicit, daca lipseşte, expr1
12
este ' ' un spaŃiu.
TRIM (LEADING 'X' FROM
Permite eliminarea caracterelor 'XXXabcdeXXX') = 'abcdeXXX'
TRIM (LEADING | TRAILING | specificate (caractere_trim) de TRIM (TRAILING 'X' FROM
BOTH caractere_trim FROM la începutul (leading) , sfârşitul 'XXXabcdeXXX') = 'XXXabcde'
expresie) (trailing) sau din ambele părŃi,
TRIM ( BOTH 'X' FROM
dintr-o expresie caracter data. 'XXXabcdeXXX') = 'abcde'

TRIM (' abcde ') = 'abcde'

2. Să se afişeze pentru fiecare angajat din departamentul 20 un şir de caractere de forma "FuncŃia
salariatului {prenume} {nume} este {cod functie}". Să se afişeze prenumele cu iniŃiala litera mare, iar
numele cu litere mari (Stephen KING), iar codul funcŃiei să se afişeze cu litere mici.
3. Să se afişeze pentru angajatul cu numele 'HIGGINS' codul, numele şi codul departamentului. Cum se
scrie condiŃia din WHERE astfel încât să existe siguranŃa ca angajatul 'HIGGINS' va fi găsit oricum ar fi
fost introdus numele acestuia? Căutarea trebuie să nu fie case-sensitive, iar eventualele blank-uri care
preced sau urmează numelui trebuie ignorate.
UPPER(TRIM(last_name))='HIGGINS';
4. Să se afişeze pentru toŃi angajaŃii al căror nume se termină în 'n', codul, numele, lungimea numelui şi
poziŃia din nume în care apare prima data litera 'a'. AsociaŃi aliasuri coloanelor returnate de cerere.
SELECT employee_id, last_name, LENGTH(last_name), INSTR(UPPER(last_name), 'A')
FROM employees
WHERE SUBSTR(last_name,-1)='n';
5. AnalizaŃi următoarele funcŃii aritmetice:
FuncŃie SemnificaŃie Exemplu
ROUND(1.6) = 2
Returnează valoarea rotunjită a expresiei
ROUND(1.4) = 1
până la n zecimale. Daca n este negativ sunt
ROUND (expresie [, n]) ROUND (1234.56,1) = 1234.6
rotunjite cifre din stânga virgulei. Valoarea
ROUND (1230.56, -2) = 1200
implicită pentru n este 0.
ROUND (1260.56, -2) = 1300

MOD (11, 4) = MOD (11, -4) = 3


MOD (m,n) Returnează restul împărŃirii lui m la n.
MOD(-11, 4) = MOD (-11, -4) = -3

6. Să se afişeze detalii despre salariaŃii care au lucrat un număr întreg de săptămâni până la data curentă.
MOD(ROUND(SYSDATE – hire_date), 7)=0;
7. Să se afişeze numele, salariul şi numărul de mii al salariului rotunjit la 2 zecimale pentru cei care nu au
salariul divizibil cu 1000.
8. AnalizaŃi următoarele operaŃii pe expresii de tip dată calendaristică:
Tipul de date al
OperaŃie Descriere
rezultatului
date -/+ number Date Scade/Adaugă un număr de zile dintr-o / la o dată.
date1 - date2 Number Întoarce numărul de zile dintre două date calendaristice.
date +/-
Date Scade/Adaugă un număr de ore la o / dintr-o dată calendaristică.
number/24
9. Să se afişeze data (luna, ziua, ora, minutul si secunda) de peste 10 zile.
13
SYSDATE+10
10. Să se afişeze numărul de zile rămase până la sfârşitul anului.
ROUND(TO_DATE(’31-DEC-2009’)-SYSDATE)
11. a. Să se afişeze data de peste 12 ore.
SYSDATE+12/24
b. Să se afişeze data de peste 5 minute.
SYSDATE+1/288
12. AnalizaŃi următoarele funcŃii pentru prelucrarea datelor calendaristice:
FuncŃie SemnificaŃie Exemplu
SYSDATE Întoarce data şi timpul curent
Returnează numărul de luni dintre
data date1 şi data date2. Rezultatul
MONTHS_BETWEEN poate fi pozitiv sau negativ după cum ROUND(MONTHS_BETWEEN
(date1, date2) date1 este mai recentă sau nu faŃă de (SYSDATE + 31, SYSDATE)) = 1
date2. Zecimalele reprezintă parŃi
dintr-o luna!
Adaugă n luni la o data specificată. MONTHS_BETWEEN
ADD_MONTHS (date, n) Valoarea n trebuie să fie întreagă (ADD_MONTHS(SYSDATE, 3),
(pozitivă sau negativă). SYSDATE) = 3

NEXT_DAY('15-dec-2006','Monday')
Returnează data corespunzătoare
= '18-dec-2006'
NEXT_DAY (date, char) primei zile a săptămânii specificate
NEXT_DAY ('15-dec-2006',1)
(char) care urmează după date.
= '18-dec-2006'

13. Să se afişeze numele angajatului, data angajării şi data negocierii salariului, care a avut loc în prima zi de
Luni, după 6 luni de serviciu. EtichetaŃi această coloană “Negociere”.
NEXT_DAY(ADD_MONTHS(hire_date, 6), ‘Monday’)
14. Pentru fiecare angajat să se afişeze numele şi numărul de luni de la data angajării. EtichetaŃi coloana
“Luni lucrate”. Să se ordoneze rezultatul după numărul de luni lucrate. Se va rotunji numărul de luni la
cel mai apropiat număr întreg.
SELECT last_name, ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)) “Luni lucrate”
FROM employees
ORDER BY MONTHS_BETWEEN(SYSDATE, hire_date);

SELECT last_name, ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)) “Luni lucrate”


FROM employees
ORDER BY “Luni lucrate”;

SELECT last_name, ROUND(MONTHS_BETWEEN(SYSDATE, hire_date)) “Luni lucrate”


FROM employees
ORDER BY 2;
15. AnalizaŃi următoarele funcŃii de conversie:
Obs. Conversiile implicite asigurate de server-ul Oracle sunt:
• de la VARCHAR2 sau CHAR la NUMBER;
• de la VARCHAR2 sau CHAR la DATE;
• de la NUMBER la VARCHAR2 sau CHAR;
• de la DATE la VARCHAR2 sau CHAR.
14
SELECT last_name
FROM employees
WHERE TO_CHAR(hire_date,'yyyy')=1994;
SELECT last_name
FROM employees
WHERE hire_date='07-JUN-1994';
SELECT employee_id||' '||last_name||' '||hire_date
FROM employees
WHERE department_id=10;
Conversiile explicite se realizează cu ajutorul funcŃiilor de tip TO_{tip}
FuncŃie SemnificaŃie Exemplu
Converteşte o valoare de tip numeric sau dată TO_CHAR('3') = ' 3'
calendaristică, la un şir de caractere conform cu TO_CHAR(-12) = '-12'
TO_CHAR
formatul specificat sau cu setările naŃionale TO_CHAR(sysdate, 'DDMMYYYY')
(expr_number_sau
specificate (NLS - National Language Support).
_date[, format][, = ' 09122004'
Daca formatul sau parametrii lipsesc se
nlsparameters]) TO_CHAR (sysdate + 365 * 57,
utilizează formatul şi parametrii impliciŃi.
Formatul este case sensitive. 'ddmmyyyy') = ' 25112061'
TO_NUMBER Converteşte o valoare de tip şir de caractere la o
(expr_char[, valoare numerică conform cu formatul TO_NUMBER ('-12.22', 'S99.99')
format][, specificat. Dacă formatul sau parametrii lipsesc = -12.22
nlsparameters]) se utilizează formatul şi parametrii impliciŃi.
Converteşte o valoare de tip şir de caractere la o
TO_DATE
valoare de tip dată calendaristică în
(expr_char[, TO_DATE ('15-feb-2006','dd-mon-
conformitate cu formatul specificat. Dacă
format][, yyyy')
formatul sau parametrii lipsesc se utilizează
nlsparameters])
formatul şi parametrii impliciŃi.
16. Să se afişeze numele şi prenumele pentru toŃi angajaŃii care s-au angajat în luna mai.
17. AnalizaŃi următoarele funcŃii SQL:
FuncŃie SemnificaŃie Exemplu
Returnează expr1 dacă aceasta nu este NVL(NULL, 1) = 1
NULL, expr2 în caz contrar. Cele 2 expresii NVL(2, 1) = 2
NVL (expr1, expr2) trebuie să aibă acelaşi tip sau expr2 să NVL('c', 1) = 'c' -- face conversie
permită conversia implicită la tipul NVL(1, 'c') -- eroare
expresiei expr1. --nu face conversie
Dacă expr1 este nenulă atunci returnează NVL2 (1, 2, 3) = 2
NVL2 (expr1, expr2, expr3)
expr2, altfel Returnează expr3 NVL2 (NULL, 2, 3) = 3
18. Să se afişeze numele angajaŃilor şi comisionul. Dacă un angajat nu câştigă comision, să se scrie “Fara
comision”. EtichetaŃi coloana “Comision”.
NVL(TO_CHAR(commission_pct), ‘Fara comision’)
19. Să se listeze numele, salariul şi comisionul tuturor angajaŃilor al căror venit lunar depăşeşte 10000$.
salary * NVL(commission_pct, 0) venit_lunar

20. AnalizaŃi expresia CASE şi funcŃia DECODE:

FuncŃie/Expresie SemnificaŃie Exemplu


CASE expr WHEN expr_bool1 În funcŃie de valoarea unei expresii
THEN return_expr1 returnează valoarea primei perechi
15
[WHEN expr_bool2 THEN WHEN .. THEN care se potriveşte sau
return_expr2 dacă nu se potriveşte nici una expresia
... din ELSE. Nu se poate specifica NULL
WHEN expr_booln THEN pentru toate expresiile de returnat.
return_exprn ] (return_expri). Toate expresiile trebuie
[ELSE return_expr] sa aibă acelaşi tip de date
END
DECODE (expr, expr_cautare1, Decodifică valoarea expresiei. Dacă
expr_rezultat1, valoarea expresiei este expr_cautarei
DECODE (1, 1, 2, 3) = 2
[expr_cautare2, expr_rezultat2, atunci e returnată expr_rezultati. Dacă
DECODE (2, 1, 2, 3) = 3
.. nu se potriveşte nici o expresie de
DECODE (3, 1, 2, 3) = 3
expr_cautaren, expr_rezultatn, ] căutare atunci e returnat
[rezultat_implicit]) rezultat_implicit.
21. Să se afişeze numele, codul funcŃiei, salariul şi o coloana care să arate salariul după mărire. Se ştie că
pentru IT_PROG are loc o mărire de 10%, pentru ST_CLERK 15%, iar pentru SA_REP o mărire de
20%. Pentru ceilalŃi angajaŃi nu se acordă mărire. Să se denumească coloana "Salariu revizuit".
SELECT last_name, job_id, salary,
DECODE(job_id,
‘IT_PROG’, salary*1.1,
’ST_CLERK’, salary*1.15,
‘SA_REP’, salary*1.2,
salary ) “salariu revizuit”
FROM employees;

SELECT last_name, job_id, salary,


CASE job_id WHEN ‘IT_PROG’ THEN salary* 1.1
WHEN ’ST_CLERK’ THEN salary*1.15
WHEN ‘SA_REP’ THEN salary*1.2
ELSE salary
END “salariu revizuit”
FROM employees;

SELECT last_name, job_id, salary,


CASE WHEN job_id= ‘IT_PROG’ THEN salary* 1.1
WHEN job_id=’ST_CLERK’ THEN salary*1.15
WHEN job_id =‘SA_REP’ THEN salary*1.2
ELSE salary
END “salariu revizuit”
FROM employees;
22. Să se afişeze numele salariatului şi codul departamentului în care acesta lucrează. Dacă există salariaŃi
care nu au un cod de departament asociat, atunci pe coloana id_depratment să se afişeze:
textul “fara departament”;
valoarea zero.
23. a. Să se afişeze numele angajaŃilor care nu au manager.
b. Să se afişeze numele angajaŃilor şi codul managerilor lor. Pentru angajaŃii care nu au manager să apară
textul “nu are sef”.
24. Să se afişeze numele salariatului şi:
• venitul anual dacă are comision;
• salariul dacă nu are comision.
Se va utiliza funcŃia NVL2.
25. Să se afişeze numele salariatului, salariul şi salariul revizuit astfel:

16
- dacă lucrează de mai mult de 200 de luni atunci salariul va fi mărit cu 20%;
- dacă lucrează de mai mult de 150 de luni, dar mai puŃin de 200 de luni, atunci salariul va fi mărit cu
15%;
- dacă lucrează de mai mult de 100 de luni, dar mai puŃin de 150 de luni, atunci salariul va fi mărit cu
10%;
- altfel, salariul va fi mărit cu 5%.

CERERI MULTITABEL, SUBCERERI

Tipuri de join:
• equijoin (se mai numeşte inner join sau simple join) - compunerea a două tabele diferite după o
condiŃie ce conŃine operatorul de egalitate.
SELECT last_name, department_name, location_id, e.department_id
FROM employees e, departments d
WHERE e.department_id = d.department_id;
Obs: Numele sau alias-urile tabelelor sunt obligatorii în dreptul coloanelor care au acelaşi nume
în mai multe tabele.
• nonequijoin - compunerea a două relaŃii tabele după o condiŃie oarecare, ce NU conŃine
operatorul de egalitate.
SELECT last_name, salary, grade_level
FROM employees, job_grades
WHERE salary BETWEEN lowest_sal AND highest_sal;
• outerjoin - compunerea externă a două tabele diferite completând una dintre relaŃii cu valori
NULL acolo unde nu există în aceasta nici un tuplu ce îndeplineşte condiŃia de corelare. RelaŃia
completată cu valori NULL este cea în dreptul căreia apare “(+)”. Operatorul (+) poate fi plasat
în orice parte a condiŃiei de join, dar nu în ambele părŃi. Full outer join = Left outer join UNION
Right outer join.
SELECT last_name, department_name,location_id
FROM employees e, departments d
WHERE e.department_id(+) = d.department_id;
• selfjoin - compunerea externă a unui tabel cu el însuşi după o condiŃie dată.
SELECT sef.last_name, angajat.last_name
FROM employees sef, employees angajat
WHERE sef.employee_id = angajat.manager_id
ORDER BY sef.last_name;
1. Pentru fiecare angajat să se afişeze numele, codul şi numele departamentului.
SELECT last_name, e.department_id, department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id;
2. Să se afişeze numele angajatului, numele departamentului pentru toŃi angajaŃii care câştigă comision.
3. Să se listeze numele job-urile care există în departamentul 30.
SELECT DISTINCT job_title
FROM employees e, jobs j
WHERE e.job_id = j.job_id
AND department_id = 30;
4. Să se afişeze numele, job-ul şi numele departamentului pentru toŃi angajaŃii care lucrează în Seattle.
5. Să se afişeze numele, salariul, data angajării şi numele departamentului pentru toŃi programatorii care
lucrează în America.
region_name = ‘Americas’
17
job_title = ‘Programmer’
6. Să se afişeze numele salariaŃilor şi numele departamentelor în care lucrează. Se vor afişa şi salariaŃii care
nu lucrează într-un departament (right outher join).
SELECT last_name, department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id(+);
7. Să se afişeze numele departamentelor şi numele salariaŃilor care lucrează în ele. Se vor afişa şi
departamentele care nu au salariaŃi (left outher join).
8. Să se afişeze numele, job-ul, numele departamentului, salariul şi grila de salarizare pentru toŃi angajaŃii.
9. Să se afişeze codul angajatului şi numele acestuia, împreună cu numele şi codul şefului său direct. Se vor
eticheta coloanele Ang#, Angajat, Mgr#, Manager. Să se salveze instrucŃiunea într-un fişier numit
p3_9.sql.
SELECT a.employee_id “Ang#”, a.last_name “Angajat”, b.employee_id “Mgr#”, b.last_name
“Manager”
FROM employees a, employees b
WHERE a.manager_id = b. employee_id;
10. Să se modifice p3_9.sql pentru a afişa toŃi salariaŃii, inclusiv pe cei care nu au şef.
11. Să se afişeze numele salariatului şi data angajării împreună cu numele şi data angajării şefului direct
pentru salariaŃii care au fost angajaŃi înaintea şefilor lor. Se vor eticheta coloanele Angajat, Data_ang,
Manager si Data_mgr.
12. Pentru fiecare angajat din departamentele 20 şi 30 să afişeze numele, codul departamentului şi toŃi
colegii săi (salariaŃii care lucrează în acelaşi departament cu el). Se vor eticheta coloanele corespunzător.
13. Să se afişeze numele şi data angajării pentru salariaŃii care au fost angajaŃi după Fay.
SELECT last_name, hire_date
FROM employees
WHERE hire_date > (SELECT hire_date
FROM employees
WHERE last_name = ‘Fay’);
sau
SELECT a.last_name, a.hire_date
FROM employees a, employees b
WHERE UPPER(b.last_name)=’FAY’ AND a.hire_date>b.hire_date;

14. ScrieŃi o cerere pentru a afişa numele şi salariul pentru toŃi colegii (din acelaşi departament) lui Fay. Se
va exclude Fay.
15. Să se afişeze codul departamentului, codul şi numele angajaŃilor care lucrează în acelaşi departament cu
cel puŃin un angajat al cărui nume conŃine litera “T”. Să se ordoneze după codul departamentului.
SELECT employee_id, last_name, department_id
FROM employees
WHERE department_id IN (SELECT DISTINCT department_id
FROM employees
WHERE UPPER(last_name) LIKE ‘%T%’)
ORDER BY department_id;
16. Să se afişeze numele şi salariul angajaŃilor conduşi direct de Steven King.
SELECT last_name, salary
FROM employees
WHERE manager_id = (SELECT employee_id
FROM employees
18
WHERE UPPER(last_name) ='KING'
AND UPPER(first_name) ='STEVEN' );
17. Să se afişeze numele şi job-ul tuturor angajaŃilor din departamentul ‘Sales’.

18. Să se afişeze numele angajaŃilor, numărul departamentului şi job-ul tuturor salariaŃilor al căror
departament este localizat în Seattle.
SELECT last_name, job_id, department_id
FROM employees
WHERE department_id IN (SELECT department_id
FROM departments
WHERE location_id = (SELECT location_id
FROM locations
WHERE city = ‘Seattle’));
RezolvaŃi această problemă utilizând join-uri.
19. Să se afle dacă există angajaŃi care nu lucrează în departamentul ‘Sales’ şi al căror salariu şi comision
coincid cu salariul şi comisionul unui angajat din departamentul ‘Sales’.
SELECT last_name, salary, commission_pct, department_id
FROM employees
WHERE (salary, commission_pct) IN (SELECT salary, commission_pct
FROM employees e, departments d
WHERE e.department_id = d.department_id
AND department_name = ‘Sales’)
AND department_id <> (SELECT department_id
FROM departments
WHERE department_name = ‘Sales’);
20. ScrieŃi o cerere pentru a afişa numele, numele departamentului şi salariul angajaŃilor care nu câştigă
comision, dar al căror manager coincide cu managerul unui angajat care câştigă comision.
21. ScrieŃi o cerere pentru a afişa angajaŃii care câştigă mai mult decât oricare funcŃionar. SortaŃi rezultatele
după salariu, în ordine descrescătoare.
SELECT last_name, salary, job_id
FROM employees
WHERE salary > (SELECT MAX(salary)
FROM employees
WHERE job_id LIKE '%CLERK')
ORDER BY salary DESC;
22. Să se afişeze codul, numele şi salariul tuturor angajaŃilor care câştigă mai mult decât salariul mediu.
23. Să se afişeze pentru fiecare salariat angajat în luna martie numele său, data angajării şi numele jobului.
24. Să se afişeze pentru fiecare salariat al cărui câştig total lunar este mai mare decât 12000 numele său,
câştigul total lunar şi numele departamentului în care lucrează.
25. Să se afişeze pentru fiecare angajat codul său şi numele joburilor sale anterioare, precum şi intervalul de
timp în care a lucrat pe jobul respectiv.
26. Să se modifice cererea de la punctul 25 astfel încât să se afişeze şi numele angajatului, respectiv codul
jobului său curent.
27. Să se modifice cererea de la punctul 26 astfel încât să se afişeze şi numele jobului său curent.
28. Să se afişeze salariaŃii care au acelaşi manager ca şi angajatul având codul 140.
29. Să se afişeze numele departamentelor care funcŃionează în America.

19
FuncŃii multiple-row (grup). Gruparea datelor.

Aceste tipuri de funcŃii pot fi utilizate pentru a returna informaŃia corespunzătoare fiecăruia dintre
grupurile obŃinute în urma divizării liniilor tabelului cu ajutorul clauzei GROUP BY.
Pot apărea în clauzele SELECT, ORDER BY şi HAVING. Server-ul Oracle aplică aceste funcŃii
fiecărui grup de linii şi returnează un singur rezultat pentru fiecare mulŃime.
Exemple de funcŃii grup: AVG, SUM, MAX, MIN, COUNT etc.
Tipurile de date ale argumentelor funcŃiilor grup pot fi CHAR, VARCHAR2, NUMBER sau DATE.
FuncŃiile AVG şi SUM, operează numai asupra valorilor numerice. FuncŃiile MAX şi MIN pot opera asupra
valorilor numerice, caracter sau dată calendaristică.
Toate funcŃiile grup, cu excepŃia lui COUNT(*), ignoră valorile null. COUNT(expresie)
returnează numărul de linii pentru care expresia dată nu are valoarea null. FuncŃia COUNT returnează
un număr mai mare sau egal cu zero şi nu întoarce niciodată valoarea null.
Când este utilizată clauza GROUP BY, server-ul sortează implicit mulŃimea rezultată în
ordinea crescătoare a valorilor coloanelor după care se realizează gruparea.
AbsenŃa clauzei GROUP BY conduce la aplicarea funcŃiei grup pe mulŃimea tuturor liniilor
tabelului.
În clauza GROUP BY se trec obligatoriu toate coloanele prezente în clauza SELECT, care nu
sunt argument al funcŃiilor grup.

1. Să se afişeze cel mai mare salariu, cel mai mic salariu, suma şi media salariilor tuturor angajatilor.
EtichetaŃi coloanele Maxim, Minim, Suma, respectiv Media. Să se rotunjească rezultatele.
SELECT MIN(salary) min, MAX(salary) max, SUM(salary) suma, ROUND(AVG(salary)) media
FROM employees;
2. Utilizând funcŃia grup COUNT să se determine:
a. numărul total de angajaŃi;
b. numărul de angajaŃi care au manager;
c. numărul de manageri.
3. Să se afişeze diferenŃa dintre cel mai mare şi cel mai mic salariu. EtichetaŃi coloana “Diferenta”.
4. Să se listeze numărul de angajaŃi din departamentul având codul 50.
5. CaŃi angajaŃi din departamentul 80 câştigă comision?
6. Să se selecteze valoarea medie şi suma salariilor pentru toŃi angajaŃii care sunt reprezentanŃi de vânzări
(SA_MAN, SA_REP).
7. Să se selecteze data angajării primei persoane care a fost angajată de companie.
8. Să se afişeze numărul de angajaŃi pentru fiecare job.
SELECT job_id, COUNT(employee_id) nr_angajati
FROM employees
GROUP BY job_id;
9. Să se afişeze minimul, maximul, suma şi media salariilor pentru fiecare departament.
10. Să se afişeze codul departamentului şi media salariilor pentru fiecare job din cadrul acestuia.
SELECT department_id, job_id, AVG(salary)
FROM employees
GROUP BY department_id, job_id;
11. a. Să se afişeze codul departamentelor pentru care salariul minim depăşeşte 5000$.
SELECT department_id, MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary)>5000;
20
b. Să se modifice cererea anterioară astfel încât să se afişeze şi oraşul în care funcŃionează aceste
departamente.
12. Să se obŃină codul departamentelor şi numărul de angajaŃi al acestora pentru departamentele care au cel
puŃin 10 angajaŃi.
13. Să se obŃină codul departamentelor şi suma salariilor angajaŃilor care lucrează în acestea, în ordine
descrescătoare după sumă. Se consideră angajaŃii care au comision şi departamentele care au mai mult
de 5 angajaŃi.
14. Să se obŃină job-ul pentru care salariul mediu este minim.
SELECT job_id
FROM employees
GROUP BY job_id
HAVING AVG(salary) = (SELECT MIN(AVG(salary))
FROM employees
GROUP BY job_id);
15. Să se afişeze cel mai mare dintre salariile medii pe departamente.
16. a. Să se afişeze codul, numele departamentului şi suma salariilor pe departamente.
SELECT d.department_id, department_name,a.suma
FROM departments d, (SELECT department_id ,SUM(salary) suma
FROM employees
GROUP BY department_id) a
WHERE d.department_id =a.department_id;

b. DaŃi o altă metodă de rezolvare a acestei probleme.

17. a. ScrieŃi o cerere pentru a afişa numele departamentului, numărul de angajaŃi şi salariul mediu pentru
angajaŃii din acel departament. Coloanele vor fi etichetate Departament, Nr. angajati, Salariu Mediu.
SELECT department_name “Departament”,
(SELECT COUNT(employee_id)
FROM employees
WHERE department_id = d.department_id ) ” Nr. angajati”,
(SELECT AVG(salary)
FROM employees
WHERE department_id = d.department_id) ”Salariu mediu”
FROM departments d;
b. DaŃi o altă metodă de rezolvare pentru problema anterioară.

18. Să se creeze o cerere prin care să se afişeze numărul total de angajaŃi şi, din acest total, numărul celor
care au fost angajaŃi în 1997, 1998, 1999 şi 2000. Datele vor fi afişate în forma următoare:
Total 1997 1998 1999 2000
--------------------------------------------------------------
50 10 5 25 1

SUM(DECODE(TO_CHAR(hire_date,'yyyy'),1997,1,0))

21
Operatorii ROLLUP şi CUBE

Clauza GROUP BY permite gruparea liniilor selectate după valorile expresiilor precizate în
aceasta. Pentru fiecare grup, va fi returnată o singură linie de informaŃie. Clauza GROUP BY poate
produce grupări superagregat utilizând extensiile CUBE sau ROLLUP.
ROLLUP grupează liniile selectate pe baza valorilor primelor n, n - 1, …, 0 expresii din
specificaŃia GROUP BY şi returnează o singură linie pentru fiecare grup. ROLLUP creează grupări
prin deplasarea într-o singură direcŃie, de la dreapta la stânga, de-a lungul listei de coloane
specificate în clauza GROUP BY. Apoi, se aplică funcŃia agregat acestor grupări. Dacă sunt
specificate n expresii în operatorul ROLLUP, numărul de grupări generate va fi n + 1. Liniile care se
bazează pe valoarea primelor n expresii se numesc linii obişnuite, iar celelalte se numesc linii
superagregat.
GROUP BY ROLLUP (expr_1, expr_2, …, expr_n) generează n+1 tipuri de linii,
corespunzătoare următoarelor grupări:
• GROUP BY (expr_1, expr_2, …, expr_n-1, expr_n)
• GROUP BY (expr_1, expr_2, …, expr_n-1)
• …
• GROUP BY (expr_1, expr_2)
• GROUP BY (expr_1)
• GROUP BY () – corespunzător absenŃei clauzei GROUP BY şi deci, calculului funcŃiilor
grup din cerere pentru întreg tabelul.

CUBE grupează liniile selectate pe baza valorilor tuturor combinaŃiilor posibile ale expresiilor
specificate şi returnează câte o linie totalizatoare pentru fiecare grup. Acest operator este folosit pentru a
produce mulŃimi de rezultate care sunt utilizate în rapoarte. În vreme ce ROLLUP produce subtotalurile
doar pentru o parte dintre combinaŃiile posibile, operatorul CUBE produce subtotaluri pentru toate
combinaŃiile posibile de grupări specificate în clauza GROUP BY, precum şi un total general.
Dacă există n coloane sau expresii în clauza GROUP BY, vor exista 2n combinaŃii posibile
superagregat.
19. Să se afişeze codurile departamentelor în care lucrează cel puŃin un angajat, iar pentru fiecare dintre
acestea şi pentru fiecare manager care lucrează în departamentul respectiv să se afişeze numărul de
salariaŃi. De asemenea, să se afişeze numărul de salariaŃi pentru fiecare departament indiferent de
manager şi numărul total de angajaŃi din companie.

SELECT department_id, manager_id, COUNT(employee_id)


FROM employees
WHERE manager_id IS NOT NULL AND department_id IS NOT NULL
GROUP BY ROLLUP (department_id, manager_id);

department_id manager_id COUNT(employee_id)


---------------------------------------------------------------------
10 7782 1
10 7839 1
10 2
-----------------------------------------------------------------------
20 7566 2
20 7788 1
20 7839 1
20 7902 1
20 5
-----------------------------------------------------------------------
30 7698 5
30 7839 1
30 6

22
-----------------------------------------------------------------------
13
20. Să se afişeze codurile departamentelor în care lucrează cel puŃin un angajat, iar pentru fiecare dintre
acestea şi pentru fiecare manager care lucrează în departamentul respectiv să se afişeze numărul de
salariaŃi. De asemenea, să se afişeze numărul de salariaŃi pentru fiecare departament indiferent de
manager, numărul de angajaŃi subordonaŃi unui manager indiferent de departament şi numărul total de
angajaŃi din companie.

SELECT department_id, manager_id, COUNT(employee_id)


FROM employees
WHERE manager_id IS NOT NULL AND department_id IS NOT NULL
GROUP BY CUBE (department_id, manager_id);

department_id manager_id COUNT(employee_id)


---------------------------------------------------------------------
10 7782 1
10 7839 1
10 2
-----------------------------------------------------------------------
20 7566 2
20 7788 1
20 7839 1
20 7902 1
20 5
-----------------------------------------------------------------------
30 7698 5
30 7839 1
30 6
-----------------------------------------------------------------------
7566 2
7698 5
7782 1
7788 1
7839 3
7902 1
----------------------------------------------------------------------
13
21. Pentru fiecare departament, job, respectiv an al angajării să se afişeze numărul de salariaŃi. De asemenea
se va afişa numărul de angajaŃi:
- pentru fiecare departament şi job, indiferent de anul angajării;
- pentru fiecare departament, indiferent de job şi de anul angajării;
- la nivel de companie.
22. Să se afişeze suma alocată pentru plata salariilor pe joburi (codul jobului), în cadrul departamentului
(codul departamentului). De asemenea, să se afişeze valoarea totală necesară pentru plata salariilor la
nivel de departament, valoarea totală necesară pentru plata salariilor la nivel de job, indiferent de
departament şi valoarea totală necesară pentru plata salariilor la nivel de companie.
23. FuncŃia GROUPING(expresie) întoarce:
- valoarea 0, dacă expresia a fost utilizată pentru calculul valorii agregat
- valoarea 1, dacă expresia nu a fost utilizată.
24. Să se afişeze numele departamentelor, titlurile job-urilor şi valoarea medie a salariilor, pentru:
- fiecare departament şi, în cadrul său pentru fiecare job;
- fiecare departament (indiferent de job);
- întreg tabelul.
23
De asemenea, să se afişeze şi o coloană care indică intervenŃia coloanelor department_name şi job_title
în obŃinerea rezultatului.

25. ModificaŃi cererea anterioară astfel încât să se afişeze numele departamentelor, titlurile job-urilor şi
valoarea medie a salariilor, pentru:
- fiecare departament şi, în cadrul său pentru fiecare job;
- fiecare departament (indiferent de job);
- fiecare job(indiferent de departament);
- întreg tabelul.
Cum intervin coloanele în obŃinerea rezultatului?
Să se afişeze ’Dept’, dacă departamentul a intervenit în agregare şi ‘Job’, dacă job-ul a intervenit în
agregare.
DECODE(GROUPING(department_name), 0, ‘Dept’)
26. UtilizaŃi cererea de la punctul 20.
a. EliminaŃi clauza WHERE din această cerere. AnalizaŃi rezultatul obŃinut.
b. ModificaŃi cererea obŃinută astfel încât să se identifice dacă o valoare null din rezultat este stocată pe
una dintre coloanele manager_id sau department_id sau este produsă de operatorul CUBE.
27. Clauza GROUPING SETS. Permite obŃinerea numai a anumitor grupări superagregat. Acestea pot fi
precizate prin intermediul clauzei:

GROUP BY GROUPING SETS ((expr_11, expr_12, …, expr_1n), (expr_21, expr_22, …expr_2m),


…)
28. Să se afişeze numele departamentelor, numele job-urilor, codurile managerilor angajaŃilor, maximul şi
suma salariilor pentru:
- fiecare departament şi, în cadrul său, fiecare job;
- fiecare job şi, în cadrul său, pentru fiecare manager;
- întreg tabelul.

GROUPING SETS ((department_name, job_title), (job_title, e.manager_id), ());

Limbajul de control al datelor (DCL). COMMIT, SAVEPOINT, ROLLBACK.


• Comanda COMMIT permanentizează modificările care au fost realizate de tranzacŃia curentă (o
tranzacŃie este un set de comenzi LMD); comanda suprimă toate punctele intermediare definite în
tranzacŃie şi eliberează blocările tranzacŃiei.
ObservaŃie:
Sistemul realizează COMMIT implicit:
- la închiderea normală a unui client Oracle (de exemplu SQL*Plus),
- după fiecare comandă LDD (CREATE, ALTER, DROP).
• Comanda SAVEPOINT marchează un punct intermediar în procesarea tranzacŃiei. În acest mod este
posibilă împărŃirea tranzacŃiei în subtranzacŃii.
Comanda SAVEPOINT are sintaxa:
SAVEPOINT nume_pct_intermediar;
• Comanda ROLLBACK permite renunŃarea la modificările efectuate; aceasta determină încheierea
tranzacŃiei, anularea modificărilor asupra datelor şi restaurarea stării lor precedente.
Comanda ROLLBACK are sintaxa:
ROLLBACK [TO SAVEPOINT nume_punct_salvare];

24
ObservaŃii:
- sistemul realizează ROLLBACK implicit dacă se închide anormal (defecŃiune hardware sau software,
pană de curent etc.);
- nici o comanda LDD (CREATE, ALTER; DROP) nu poate fi anulată.
1. Ce efect are următoarea secvenŃă de instrucŃiuni?
CREATE TABLE dept_***
AS SELECT * FROM departmets;

SELECT *
FROM dept_***;

SAVEPOINT a;

DELETE FROM dept_***;

INSERT INTO dept_***


VALUES (300,’Economic’,100,1000);

INSERT INTO dept_***


VALUES (350,’Cercetare’,200,2000);

SAVEPOINT b;

INSERT INTO dept_***


VALUES (400,’Juritic’,150,3000);

SELECT COUNT(*)
FROM dept_***;

ROLLBACK TO b;

SELECT COUNT(*)
FROM dept_***;

ROLLBACK TO a;

INSERT INTO dept_***


VALUES (500,’Contabilitate’,175,1500);

COMMIT;

SELECT *
FROM dept_***;

Limbajul de prelucrare a datelor (DML). INSERT, UPDATE, DELETE.


1. Să se creeze tabele emp_*** şi dept_***, având aceeaşi structură şi date ca şi tabelele employees,
respectiv departments.
CREATE TABLE emp_*** AS
SELECT * FROM employees;
2. Să se selecteze toate înregistrările din cele două tabele create anterior.
3. ŞtergeŃi toate înregistrările din cele 2 tabele create anterior. SalvaŃi modificările.
25
DELETE FROM emp_***;
COMMIT;
4. Să se listeze structura tabelului employees şi să se compare cu structura tabelului emp_***. Ce observaŃi?
5. Sintaxa simplificată a comenzii INSERT
- pentru inserarea unei singure linii:
INSERT INTO nume_tabel [(col1,col2,...)]
VALUES (expresie1, expresie2, ...);
- pentru inserarea liniilor rezultat ale unei comenzi SELECT:
INSERT INTO nume_tabel [(col1,col2,...)]
comanda_SELECT;
6. Să se exemplifice câteva dintre erorile care pot să apară la inserare şi să se observe mesajul returnat de sistem.
- lipsa de valori pentru coloane NOT NULL (coloana department_name este definită NOT NULL)
INSERT INTO dept_*** (department_id, location_id)
VALUES (200, 2000);
- nepotrivirea listei de coloane cu cea de expresii
INSERT INTO dept_***
VALUES (200, 2000);
INSERT INTO dept_*** (department_id, department_name,location_id)
VALUES (200, 2000);
- nepotrivirea tipului de date
INSERT INTO dept_*** (department_id, location_id)
VALUES (‘D23’, 2000);
- valoare prea mare pentru coloană
INSERT INTO dept_*** (department_id, location_id)
VALUES (15000, 2000);
7. InseraŃi în tabelul emp_*** salariaŃii (din tabelul employees) al căror comision depăşeşte 25% din salariu.

8. CreaŃi tabele emp1_***, emp2_*** şi emp3_*** cu aceeaşi structură ca tabelul employees. InseraŃi,
utilizând o singură comandă INSERT, informaŃii din tabelul employees:
- în tabelul emp1_*** salariaŃii care au salariul mai mic decât 6000;
- în tabelul emp2_*** salariaŃii care au salariul cuprins între 6000 şi 10000;
- în tabelul emp3_*** salariaŃii care au salariul mai mare decât 10000.
VerificaŃi rezultatele, apoi ştergeŃi toate înregistrările din aceste tabele.

Obs. Clauza ALL a comenzii INSERT determină evaluarea tuturor condiŃiilor din clauzele WHEN. Pentru
cele a căror valoare este TRUE, se inserează înregistrarea specificată în opŃiunea INTO corespunzătoare.
9. Să se creeze tabelul emp0_*** cu aceeaşi structură ca tabelul employees. InseraŃi, utilizând o singură
comandă INSERT, informaŃii din tabelul employees:
- în tabelul emp0_*** salariaŃii care lucrează în departamentul 80;
- în tabelul emp1_*** salariaŃii care au salariul mai mic decât 6000 (care nu se regăsesc în tabelul
emp0_***);
- în tabelul emp2_*** salariaŃii care au salariul cuprins între 6000 şi 10000 (care nu se regăsesc în
tabelele emp0_*** şi emp1_***);
- în tabelul emp3_*** salariaŃii care au salariul mai mare decât 10000 (care nu se regăsesc în tabelele
emp0_***, emp1_*** şi emp2_***).

Obs.

26
Clauza FIRST a comenzii INSERT determină inserarea corespunzătoare primei clauze WHEN a cărei
condiŃie este evaluată TRUE. Toate celelalte clauze WHEN sunt ignorate.
10. Sintaxa simplificată a comenzii DELETE
DELETE FROM nume_tabel
[WHERE conditie];
11. ŞtergeŃi toate înregistrările din tabelele emp_*** şi dept_***. InseraŃi în aceste tabele toate înregistrările
corespunzătoare din employees, respectiv departments. PermanentizaŃi tranzacŃia.

12. ŞtergeŃi angajaŃii care nu au comision. AnulaŃi modificările.

DELETE FROM emp_***


WHERE commission_pct IS NULL;
ROLLBACK;

13. EliminaŃi departamentele care nu au nici un angajat. AnulaŃi modificările.


14. EliminaŃi angajaŃii care nu aparŃin unui departament valid. AnulaŃi modificările.
15. Sintaxa simplificată a comenzii UPDATE:
UPDATE nume_tabel [alias]
SET col1 = expr1[, col2=expr2]
[WHERE conditie];
sau
UPDATE nume_tabel [alias]
SET (col1,col2,...) = (subcerere)
[WHERE conditie];
16. MăriŃi salariul tuturor angajaŃilor din tabelul emp_*** cu 5%. AnulaŃi modificările.

UPDATE emp_***
SET salary = salary * 1.05;
ROLLBACK;

17. SchimbaŃi jobul tuturor salariaŃilor din departamentul 80 care au comision în 'SA_REP'. AnulaŃi
modificările.

18. Să se modifice jobul şi departamentul angajatului având codul 114, astfel încât să fie la fel cu cele ale
angajatului având codul 205.
19. SchimbaŃi salariul şi comisionul celui mai prost plătit salariat din firmă, astfel încât să fie egale cu
salariul si comisionul directorului.
20. Pentru fiecare departament să se mărească salariul celor care au fost angajaŃi primii astfel încât să devină
media salariilor din companie.
21. Să se modifice valoarea emailului pentru angajaŃii care câştigă cel mai mult în departamentul în care
lucrează astfel încât acesta să devină iniŃiala numelui concatenată cu prenumele. Dacă nu are prenume
atunci în loc de acesta apare caracterul ‘.’. AnulaŃi modificările.

27

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