Baze de date şi SQL la atestatul de informatică
CREATE TABLE, INSERT, SELECT cu WHERE, UPDATE şi DELETE — structura în patru paşi a subiectului I de la atestat, cu toate problemele rezolvate.
Subiectul I de la atestat e cel mai previzibil din toată proba. Toate cele 40 de variante din sesiunea Dolj 2026 au exact aceeaşi structură în patru paşi:
| Cerinţa | Ce se cere | Instrucţiunea |
|---|---|---|
| a) | creează structura tabelului | CREATE TABLE |
| b) | introdu înregistrările | INSERT INTO |
| c) | afişează ceva filtrat | SELECT ... WHERE |
| d) | modifică sau şterge | UPDATE / DELETE |
Odată ce ştii scheletul, rămâne doar traducerea corectă a cerinţei în condiţii.
a) Traducerea structurii
Enunțul dă tipurile într-o notaţie prescurtată. Traducerea e mecanică:
| Notaţia din enunţ | Ce înseamnă | Tip SQL |
|---|---|---|
(N,5) |
numeric, 5 cifre | INTEGER |
(N,6.2) |
numeric, 6 cifre, 2 zecimale | DECIMAL(6,2) |
(T,80) |
text, 80 de caractere | VARCHAR(80) |
(T,1) |
un singur caracter | CHAR(1) |
(D) |
dată calendaristică | DATE |
(Y/N) |
da / nu | CHAR(1) |
CREATE TABLE Carti (
Nr_inventar INTEGER,
Titlu VARCHAR(80),
Pret DECIMAL(6,2),
Situatia VARCHAR(40)
);
Când enunțul se contrazice cu datele, datele au dreptate. Unele subiecte declară un câmp ca numeric, dar pun valori text în el. Nu poţi introduce „Light" într-o coloană numerică — declari
VARCHARşi scrii un comentariu.
Ce se stochează ca text, deşi arată ca număr
Regula: dacă nu faci calcule cu el, e text.
- CNP, telefon, cod poştal — zerourile de la început s-ar pierde ca număr
- numere de cameră (
0301) — la fel, plus că din ele se extrag informaţii - numere de dosar (
4123/235/2011) — conţin bare oblice
b) Inserarea datelor
INSERT INTO Carti VALUES (18211, 'Ghid practic', '1/G33', 4.50, 'In biblioteca');
Patru reguli:
- valorile se dau în ordinea coloanelor din
CREATE TABLE - textul între apostrofuri (
'), numerele fără - zecimalele cu punct, nu cu virgulă — virgula separă coloanele
- datele în format
'AAAA-LL-ZZ':12.mai.1998devine'1998-05-12'
Formatul ISO al datelor nu e o toană: scrise aşa, datele se sortează corect şi se compară cu
<, >, BETWEEN ca numerele.
Pentru valori lipsă se scrie NULL, fără apostrofuri. NULL nu e zero şi nu e şir gol — înseamnă
„nu se ştie". Orice comparaţie cu NULL e nici adevărată, nici falsă, deci rândul nu trece de
WHERE. Ca să-l cauţi explicit, foloseşti IS NULL, niciodată = NULL.
c) Filtrarea cu WHERE
SELECT * FROM Carti WHERE Situatia = 'Imprumutata';
SELECT * ia toate coloanele. Dacă enunțul cere „afişaţi numele şi vechimea", listezi exact
acele coloane: SELECT Nume, Vechime.
Traducerea formulărilor
Aici se pierd cele mai multe puncte — nu la sintaxă, ci la citire:
| În enunț | În SQL |
|---|---|
| peste 2 / mai mult de 2 | > 2 |
| cel puţin 2 / mai mare sau egal | >= 2 |
| între 50 şi 100 inclusiv | BETWEEN 50 AND 100 |
intervalul [20, 50] |
BETWEEN 20 AND 50 |
| diferit de zero | <> 0 |
| anterioară datei de 20 | < '...-20' |
Operatori utili
WHERE Clasa = 11 AND Seria = 'B' -- ambele conditii
WHERE Nume LIKE 'C%' -- incepe cu C
WHERE LOWER(Descriere) LIKE '%poet%' -- contine cuvantul
WHERE Denumire IN ('Productie', 'Vanzari') -- una dintre valori
WHERE Data BETWEEN '2016-01-01' AND '2016-12-31' -- tot anul
= vs LIKE: = pentru valoarea întreagă, LIKE pentru o bucată dintr-un text. % înseamnă
„orice şir". LIKE 'A+%' ar prinde şi A++ — atenţie când clasele diferă printr-un singur caracter.
Ordonarea
ORDER BY Pret DESC -- descrescator (ASC e implicit)
ORDER BY Nume ASC, Prenume ASC -- al doilea criteriu, la egalitate
Ordinea clauzelor e fixă: SELECT → FROM → WHERE → GROUP BY → ORDER BY.
Agregare şi grupare
Când enunțul spune „pentru fiecare X", ai nevoie de GROUP BY X.
SELECT Gen, AVG(Durata) AS Durata_medie
FROM Filme
GROUP BY Gen;
| Funcţie | Ce face |
|---|---|
COUNT(*) |
câte rânduri |
COUNT(DISTINCT col) |
câte valori diferite |
SUM(x) |
suma |
AVG(x) |
media |
MIN(x) / MAX(x) |
extremele |
Regula de aur: în SELECT pot apărea doar coloanele după care grupezi şi funcţii de agregare.
SELECT Titlu, AVG(Durata) ... GROUP BY Gen n-are sens — într-un gen sunt mai multe titluri.
Funcţiile acceptă orice expresie: SUM(Pret_buc * Nr_buc) calculează întâi pe rând, apoi adună.
Nu e acelaşi lucru cu SUM(Pret_buc) * SUM(Nr_buc).
Fără GROUP BY, agregarea se face pe tot tabelul şi întoarce un singur număr.
Când ai nevoie de subinterogare
| Enunțul cere | Soluţia |
|---|---|
| doar valoarea extremă | SELECT MAX(Pret) FROM t; |
| rândul care conţine extrema | subinterogare |
DELETE FROM Stocuri WHERE Pret = (SELECT MAX(Pret) FROM Stocuri);
Motivul: MAX comprimă rândurile şi pierde informaţia despre care rând a produs valoarea. Şi
funcţiile de agregare nu se pot folosi direct în WHERE.
d) UPDATE şi DELETE
UPDATE Carti
SET Situatia = 'Pierduta'
WHERE Titlu = 'Coliba unchiului Tom';
DELETE FROM Autori
WHERE LOWER(Descriere) LIKE '%poet%';
WHERE lipsă = toate rândurile modificate sau şterse. Nu apare nicio eroare; pur şi simplu
pierzi datele. E cea mai gravă greşeală din toată categoria.
Excepţie: dacă enunțul nu restrânge grupul („măriţi salariul mecanicilor cu 2%"),
WHEREchiar nu se pune. Regula corectă e „puneWHEREcând enunțul restrânge", nu „puneWHEREîntotdeauna".
La SET, mai multe coloane se despart prin virgulă, nu prin AND:
SET Clasa = 11,
Seria = 'A'
DELETE şterge rânduri întregi — nu se specifică nicio coloană.
Calculul procentual
Partea dreaptă a lui SET poate folosi valoarea curentă a coloanei:
| Cerinţa | SQL |
|---|---|
| scade cu 7% | SET Pret = Pret * 0.93 |
| creşte cu 5% | SET Pret = Pret * 1.05 |
| scade 9 lei (fix) | SET Pret = Pret - 9 |
| creşte cu 1 | SET Nr = Nr + 1 |
| la jumătate | SET Credit = Credit / 2 |
| valoare fixă | SET Rating = 3 |
Scădere cu p% → * (1 - p/100); creştere → * (1 + p/100). Greşeala clasică e * 0.07 în loc
de * 0.93 — ar lăsa 7% din preţ.
Când coloana e întreagă, enunțul cere de obicei rotunjire: ROUND(Pret * 1.05).
Capcana împărţirii întregi
Compensare / 100 -- 5 / 100 = 0 (!!)
Compensare / 100.0 -- 5 / 100.0 = 0.05
Între doi întregi, împărţirea e întreagă. Rezultatul e 0, formula pare că nu face nimic, şi nu
apare nicio eroare. Scrie mereu 100.0.
Identificarea unui singur rând
La UPDATE şi DELETE, ţinteşte prin cheia unică — cod, număr matricol, CNP — nu prin nume:
WHERE Cod_parafa_medic = 9813 -- sigur
WHERE Prenume = 'Mircea' -- doi medici se numesc asa
Datele din subiecte sunt construite special cu omonime, ca să prindă exact această greşeală.
Greşeli care se repetă
UPDATE/DELETEfărăWHERE— cea mai costisitoare- Text fără apostrofuri —
WHERE Situatia = Imprumutatacaută o coloană cu acest nume - Zecimale cu virgulă —
4,50se citeşte ca două valori ==la comparaţie — în SQL egalitatea e cu un singur=- Data în format românesc —
'12.mai.1998'nu e recunoscută ANDîn loc de virgulă înSETGROUP BYuitat când enunțul cere „pentru fiecare"- Coloane care nu sunt în
GROUP BY = NULLîn loc deIS NULL- Împărţire întreagă la calcule procentuale
- Intervalul de lună terminat pe 30 în loc de 31
Unde se scriu
Instrucţiunile de mai sus sunt SQL standard. În Microsoft Access se lipesc în Create → Query Design → SQL View. Câteva echivalenţe:
| SQL standard | Access |
|---|---|
VARCHAR(80) |
TEXT(80) |
'1998-05-12' |
#05/12/1998# |
SUBSTR(x, 1, 2) |
Mid(x, 1, 2) |
CAST(x AS INTEGER) |
CInt(x) |
DATE(d, '+25 days') |
DateAdd("d", 25, d) |
LIMIT 1 |
SELECT TOP 1 |
Toate problemele din această categorie
Toate cele 40 de subiecte, cu rezolvări complete în SQL, verificate rulând efectiv instrucţiunile.
- Baze de date — tabelul AbonatiBaze de date
- Baze de date — tabelul AutoriBaze de date
- Baze de date — tabelul BileteBaze de date
- Baze de date — tabelul CamereBaze de date
- Baze de date — tabelul CartiBaze de date
- Baze de date — tabelul CititoriBaze de date
- Baze de date — tabelul ClaseBaze de date
- Baze de date — tabelul ClientiBaze de date
- Baze de date — tabelul ComenziBaze de date
- Baze de date — tabelul ConsultatiiBaze de date
- Baze de date — tabelul CursVBaze de date
- Baze de date — tabelul CursuriBaze de date
- Baze de date — tabelul Date_meteoBaze de date
- Baze de date — tabelul DepartamenteBaze de date
- Baze de date — tabelul DosareBaze de date
- Baze de date — tabelul EdituriBaze de date
- Baze de date — tabelul EleviBaze de date
- Baze de date — tabelul FilmeBaze de date
- Baze de date — tabelul FructeBaze de date
- Baze de date — tabelul FurnizoriBaze de date
- Baze de date — tabelul LocuinteBaze de date
- Baze de date — tabelul MasiniBaze de date
- Baze de date — tabelul MaterialeBaze de date
- Baze de date — tabelul MecaniciBaze de date
- Baze de date — tabelul Medicamente (compensare)Baze de date
- Baze de date — tabelul Medicamente (interval de preţ)Baze de date
- Baze de date — tabelul MediciBaze de date
- Baze de date — tabelul MelodiiBaze de date
- Baze de date — tabelul MeseBaze de date
- Baze de date — tabelul OraseBaze de date
- Baze de date — tabelul Ore_cursBaze de date
- Baze de date — tabelul PacientiBaze de date
- Baze de date — tabelul PieseBaze de date
- Baze de date — tabelul PoliteBaze de date
- Baze de date — tabelul PremiiBaze de date
- Baze de date — tabelul ProduseBaze de date
- Baze de date — tabelul ProfesoriBaze de date
- Baze de date — tabelul ReparatiiBaze de date
- Baze de date — tabelul StocuriBaze de date
- Baze de date — tabelul StudentiBaze de date