Documente Academic
Documente Profesional
Documente Cultură
1999-10-01
T/P
CC
T JL
Figura 1.5 Tabelele puncte şi eveniment, legate prin dată.
Este mult mai bine decât să încercaţi a deduce tipul punctajului „pe ghicite"; în schir obţinem tipul direct din
datele înregistrate în mod explicit în baza noastră de date. asemenea, este o opţiune de preferat faţă de
înregistrarea tipurilor de punctaje în tabe puncte, deoarece trebuie să înregistrăm fiecare tip o singură dată.
Totuşi, dacă sunteţi ca mine, când auziţi pentru prima dată de acest lucru (combir informaţiilor din mai multe
tabele), vă gândiţi: „Mda, bună idee, dar e multă muncă faci mereu toate căutările alea; nu complicăm lucrurile?"
într-un fel, aveţi dreptate. Folosirea a două liste cu înregistrări este mai complicată i folosirea unei singure liste.
Dar să mai privim o dată la catalog (vezi figura l .2). Nu aii deja două seturi de înregistrări? Ţineţi cont de
următoarele aspecte:
• Păstraţi evidenţa punctajelor folosind celulele din matricea tabelului puncte, ui fiecare celulă este indexată de
numele elevului şi dată (în jos pe lateral şi de-a lur părţii superioare a matricei). Acesta reprezintă un set de
înregistrări; este analog conţinutul tabelului punctaj.
• Cum procedaţi pentru a şti tipul de eveniment pe care îl reprezintă fiecare dată? i scris un T sau un C mic
deasupra fiecărei date! Astfel, păstraţi şi evidenţa asocierii îi dată si tipul punctajului, de-a lungul părţii de sus a
matricei. Acesta reprezintă doilea set de înregistrări; este analog cu conţinutul tabelului eveniment.
Capitolul 1 Introducere în MySQL şi SQL
53
i Cu alte cuvinte, chiar dacă nu conştientizaţi acest lucru, în realitate utilizaţi catalogul | într-un mod absolut
similar cu cel propus de mine, de păstrare a informaţiilor în două tabele. Singura diferenţă reală este că acele
două categorii de informaţii nu sunt separate în mod atât de explicit în catalog.
Pagina din catalog ilustrează într-un fel modul în care noi gândim informaţiile, precum şi dificultatea
determinării modului de a plasa informaţiile într-o bază de date: noi tindem să integrăm diferitele categorii de
informaţii şi să le interpretăm ca pe un întreg. Bazele de date procedează altfel, motiv pentru care par uneori
artificiale şi nenaturale. Tendinţa noastră naturală de a unifica informaţiile îngreunează uneori până si sesizarea
faptului ca avem mai multe tipuri de date, nu doar unul singur. Din această cauză, încercarea de a „gândi ca o
bază de date" în ceea ce priveşte modul de reprezentare a datelor poate fi o mare problemă.
O cerinţă impusă tabelului eveniment de macheta prezentată în figura 1.5 este aceea ca datele să fie unice,
deoarece data este folosită pentru a corela înregistrările din tabelele puncte si eveniment. Cu alte cuvinte, nu
puteţi da două chestionare în aceeaşi zi, respectiv un chestionar şi un test. Dacă o faceţi, veţi avea două seturi de
înregistrări în tabelul puncte si două înregistrări în tabelul eveniment, toate cu aceeaşi dată, si nu veţi mai putea
stabili o corespondenţă între înregistrările din cele două tabele.
Aceasta este o problemă care nu va apărea niciodată dacă într-o zi survine o singură examinare, dar putem
presupune în mod corect că acest lucru nu se va întâmpla? în aparenţă răspunsul este afirmativ; la urma urmelor,
nu sunteţi atât de sadic încât să daţi un chestionar şi un test în aceeaşi zi. Dar sper că mă veţi ierta dacă sunt
sceptic. Am auzit deseori oameni care spun despre datele lor: „Acest caz ciudat nu se va întâmpla niciodată."
Apoi, se dovedeşte că acel caz ciudat chiar se produce cu o anumită ocazie si de regulă este necesar să vă re-
proiectaţi tabelele pentru a remedia problema pe care a cauzat-o acel caz ciudat.
Este mai bine să anticipaţi posibilele probleme şi modul de a le rezolva. Deci, să presupunem că uneori sunteţi în
situaţia de a înregistra două seturi de punctaje pentru aceeaşi zi. Cum ne putem descurca? Aşa cum se va vedea,
nu este o problemă chiar atât de dificilă. Cu o mică modificare în modul de dispunere a datelor, apariţia mai
multor evenimente la o anumită dată nu va provoca necazuri:
l- Adăugaţi o coloană la tabelul eveniment si folosiţi-o pentru a-i atribui un număr unic fiecărei înregistrări din
tabel. Astfel, fiecare eveniment va avea propriul său număr de identificare, deci coloana va avea numele
eveniment_id. (Dacă pare ciudat, catalogul din figura 1.2 are deja această proprietate: identificatorul de
eveniment este similar numărului coloanei din matricea cu punctaje din catalog. Se poate ca numărul să nu ne
scris în mod explicit şi denumit „identificator de eveniment", dar este chiar un identificator de evenimente.)
2. Când inseraţi punctaje în tabelul puncte, înregistraţi identificatorul de eveniment în locul datei.
Rezultatul acestor modificări este prezentat în figura 1.6. Acum, coroboraţi tabelele puncte şi eveniment folosind
identificatorul de eveniment în locul datei şi utilizaţi tabelul eveniment pentru a determina nu numai tipul
fiecărui punctaj, dar si data la care a *ost obţinut acesta. De asemenea, elementul unic din tabelul eveniment nu
mai trebuie
54
Partea l Utilizarea generală a sistemului MySQL
să fie data, ci identificatorul de eveniment. Aceasta înseamnă că puteţi avea zece teste şi chestionare în aceeaşi
zi; puteţi păstra evidenţa lor corectă în scriptele dumneavoastră. (Indiscutabil, elevii dumneavoastră vor fi extrem
de încântaţi să afle acest lucru.) Din păcate, din punct de vedere al operatorului uman, macheta de tabel din
figura 1.6 pare mai puţin satisfăcătoare decât cele anterioare. Tabelul puncte este mai abstract, deoarece conţine
mai puţine coloane cu o semnificaţie evidentă. Macheta de tabel prezentată anterior în figura 1.4 era uşor de
examinat si de înţeles, deoarece tabelul puncte avea coloane atât pentru date, cât şi pentru tipurile de punctaj.
Tabelul puncte actual, prezentat în figura 1.6, nu are coloane pentru nici una din aceste informaţii, ceea ce pare
serios deplasat faţă de normal. Cine vrea să citească un tabel puncte care conţine „identificatoare de eveniment"?
Pentru noi, această informaţie nu are o semnificaţie prea importantă.
Tabelul puncte
Tabelul eveniment
Nume eveniment id Puncte
Billy 5 15
Missy :5 ' 14
Johnny 556666 17 19
Jenny 67 73
Billy 82 79
Missy
Johnny
Jenny
eveniment id Data Tip
123 1999-09- C C
4 03 1999- T C
09-06
1999-09-
09 1999-
09-15
5 1999-09- C
23
6 1999-10- T
01
Figura 1.6 Tabelele puncte şi eveniment, legate după identificatorul de eveniment.
Acum aţi ajuns la o răscruce. Sunteţi interesat de posibilitatea de a putea păstra evidenţa electronică a
rezultatelor şcolare şi de a nu trebui să efectuaţi tot felul de calcule manuale obositoare atunci când atribuiţi
notele. Dar, după ce vă gândiţi la modul în care veţi reprezenta informaţiile despre punctaje într-o bază de date,
sunteţi descurajat de aspectul abstract şi discontinuu al informaţiei reprezentate în acel mod.
Natural, aceasta duce la o întrebare: „N-ar fi bine să nu folosesc deloc o bază de date? Poate că MySQL nu este
pentru mine." Aşa cum puteţi intui, voi răspunde negativ la această întrebare, deoarece altfel cartea de faţă ar
ajunge rapid la final. Dar, atunci când vă gândiţi cum să faceţi un lucru anume, este bine să aveţi în vedere
diferite alternative şi să vă întrebaţi dacă n-ar fi mai bine să folosiţi un sistem de baze de date, cum este SQL, sau
altceva, cum ar fi un program de calcul tabelar:
• Catalogul are rânduri şi coloane, ca şi o foaie de calcul tabelar. De aceea, o foaie de calcul tabelar şi un catalog
sunt foarte asemănătoare atât vizual, cât şi conceptual.
• Intr-o foaie de calcul tabelar se pot efectua calcule, deci puteţi însuma punctajele fiecărui elev folosind un
câmp de calcul. Este puţin mai greu să acordaţi o pondere diferită chestionarelor si testelor, dar nu imposibil.
Pe de altă parte, dacă doriţi să examinaţi numai o parte a datelor dumneavoastră (numai chestionarele sau numai
testele, de exemplu), să faceţi comparaţii (între notele băieţilor şi cele ale fetelor) sau să afişaţi informaţii de
sumar într-un mod flexibil, datele problemei se schimbă. O foaie de calcul tabelar nu funcţionează atât de bine,
în timp ce sistemele de baze de date relaţionale execută cu uşurinţă aceste operaţii.
Capitolul 1 Introducere în MySQL şi SQL
55
LJn alt element care trebuie avut în vedere este aceea că natura abstractă şi discontinuă a datelor reprezentate
într-o bază de date relaţională nu este, la urma urmelor, o problemă atât de mare. Trebuie să vă gândiţi la acea
reprezentare atunci când configuraţi baza de date, astfel încât să nu vă dispuneţi datele într-un mod lipsit de sens
în raport cu felul în care doriţi să le utilizaţi. Totuşi, după ce aţi determinat modul de reprezentare, veţi conta pe
motorul de baze de date pentru adunarea şi reprezentarea datelor dumneavoastră într-un mod semnificativ. Nu
veţi mai privi datele ca pe o adunătură de componente fără legătură între ele.
De exemplu, când regăsiţi punctaje din tabelul puncte, nu doriţi să vedeţi identificatoare de eveniment; doriţi să
vedeţi date. Aceasta nu este o problemă. Baza de date va căuta datele din tabelul eveniment în funcţie de
identificatorul de eveniment si vi le va prezenta. De asemenea, puteţi don să vedeţi dacă punctajele au fost
obţinute la chestionare sau teste. Nici aceasta nu este o problemă. Baza de date va căuta tipurile de punctaje în
acelaşi mod: folosind identificatorul de eveniment. Nu uitaţi, acesta este „punctul forte" al unui sistem de baze
de date relaţionale ca MySQL: corelarea unui lucru cu altul pentru a extrage informaţii din mai multe surse si de
a vă prezenta datele pe care doriţi să le vedeţi. In cazul datelor noastre de evidenţă a rezultatelor şcolare, MySQL
este cel care adună informaţiile la un loc folosind identificatorii de eveniment, pentru ca dumneavoastră să nu
mai efectuaţi această operaţie.
Acum, pentru a da o mică idee „în avans" cu privire la modul în care cereţi sistemului MySQL să execute
această corelaţie între lucruri, să presupunem că doriţi să vedeţi punctajele obţinute la data de 23 septembrie
1999. Interogarea pentru extragerea punctajelor aferente unui eveniment produs la o anumită dată se prezintă
astfel:
SELECT puncte.nume, eveniment.data, puncte.puncte, eveniment.tip
FROM puncte, eveniment
WHERE eveniment.data = "1999-09-23"
AND puncte.eveniment_id = eveniment.eveniment_id
Cam de speriat, este? Această interogare regăseşte numele elevului, data, punctajul şi tipul punctajului prin
alăturarea (corelarea) înregistrărilor din tabelul puncte cu înregistrările din tabelul eveniment. Rezultatul arată
astfel:
Nume Data Puncte Tip
Billy 1999-09-2315 15 C
Missy 1999-09-2314 14 C
Johnny 1999-09-2317 17 C
Jenny 1999-09-2319 19 c
Formatul acestor informaţii vă „sună" cunoscut? Ar fi şi cazul; este acelaşi cu macheta tabelului prezentat în
figura 1.4! Nu trebuie să cunoaşteţi identificatorul de eveniment pentru a obţine acest rezultat. Specificaţi data
care vă interesează si lăsaţi sistemul MySQL să determine înregistrările din tabelul puncte care corespund datelor
respective. Dacă v-aţi întrebat în ce măsură din cauza aspectului abstract şi discontinuu se „pierde" ceva în ceea
ce priveşte preluarea informaţiilor dintr-o bază de date într-o formă semnificativă pentru noi, răspunsul este: nu
se pierde nimic.
56 Partea l Utilizarea generală a sistemului MySQL
Desigur, după ce aţi examinat această interogare, vă mai puteţi pune şi altă întrebare. Instrucţiunea pare cam
lungă şi complicată; nu pare o cantitate cam mare de muncă numai pentru regăsirea punctajelor obţinute la o
anumită dată? Ba da, este. Totuşi, există metode de a evita tastarea mai multor rânduri de instrucţiuni SQL de
fiecare dată când doriţi să emiteţi o interogare, în general, determinaţi o singură dată modul de a executa o
interogare ca aceea şi apoi o stocaţi, pentru a o putea repeta cu uşurinţă atunci când este necesar. Vom vedea
cum se procedează în secţiunea „Sugestii pentru interacţiunea cu MySQL".
în realitate, am cam exagerat punând pe tapet acea interogare. Si totuşi interogarea respectivă este, credeţi sau
nu, ceva mai simplă decât cea pe care o vom folosi efectiv pentru extragerea punctajelor. Motivul este că trebuie
să mai facem o modificare în structura tabelului, în loc de a înregistra numele elevului în tabelul puncte, vom
folosi un identificator de elev unic. (Adică vom folosi valoarea din coloana ID a catalogului în locul valorii din
coloana Nume.) Apoi vom crea un alt tabel, denumit elev, care conţine coloanele nume si elev_id (Figura 1.7).
Tabelul elev Tabelul puncte
Tabelul eveniment
Nume Sex elev_id elev_id eveniment_id Puncte
Billy M 1 1 5 15
Missy F 2 H 2 <ysJSjSiSB! 14 L
B
"
Johnny M 3 34567 556666 17 19
Jenny F 4 8 67 73
82 79
eveniment id
Data
1999-09-03 1999-09-06 1999-09-09 1999-09-16
1999-09-23
1999-10-01
Tip
CC
TC
Figura 1.7 Tabelele elev, puncte şi eveniment, legate prin identificatorul de elev şi identificatorul de eveniment.
De ce să facem această modificare? în primul rând pentru că pot fi doi elevi cu acelaşi nume. Utilizarea unui
număr unic de identificare a elevului vă ajută să faceţi diferenţa dintre punctajele elevilor. (Este exact acelaşi
lucru ca şi diferenţierea între punctajele obţinute la un test şi la un chestionar date în aceeaşi zi prin utilizarea în
locul datei a unui identificator unic de eveniment.)
După această modificare a machetei tabelului, interogarea pe care o vom folosi efectiv pentru extragerea
punctajelor aferente unei anumite date devine puţin mai complexă:
SELECT elev.nume, eveniment.data, puncte.puncte, eveniment.tip
FROM eveniment, puncte, elev
WHERE eveniment.data = "1999-09-23"
AND eveniment.eveniment_id = puncte.eveniment_id
AND puncte.elev_id = elev.elev_id
Dacă vă îngrijorează faptul că nu puteţi determina imediat semnificaţia acelei interogări, liniştiţi-vă. Majoritatea
oamenilor nu sunt în stare de aşa ceva. Vom revedea interogarea după ce vom continua parcurgerea acestui
manual, dar diferenţa între acum şi mai târziu este că mai târziu o veţi înţelege. Nu, nu mă ţin de bancuri.
Capitolul 1 Introducere în MySQL şi SQL
57
Din figura 1.7, veţi observa că am adăugat ceva la tabelul elev care nu exista în catalog: o coloană pentru sex.
Aceasta va permite efectuarea unor operaţii simple, precum numărarea băieţilor şi a fetelor din clasă, respectiv
mai complexe, precum compararea punctajelor băieţilor cu cele ale fetelor.
Suntem aproape gata cu tabelele pentru proiectul de evidenţă a rezultatelor şcolare. Mai avem nevoie de un
singur tabel, pentru înregistrarea absenţelor în vederea determinării prezenţei la curs. Conţinutul acestuia este
relativ simplu: un identificator numeric al elevului şi o dată (vezi figura 1.8). Fiecare rând din tabel arată că
elevul respectiv a fost absent la acea dată. La sfârşitul perioadei de notare, vom apela la caracteristicile de
numărare ale sistemului MySQL pentru a rezuma conţinutul tabelului, astfel încât să ştim de câte ori a fost
absent fiecare elev.
Tabelul absente
elev_id Data
"2 4 2 1999-09-02
1999-09-15
1999-09-20
Figura 1.8 Tabelul absente.
Suntem gata de a crea tabelele noastre de evidenţă a rezultatelor şcolare, acum când ştim cum vor arăta acestea.
Instrucţiunea CREATE TABLE pentru tabelul elev arată astfel: CREATE TABLE elev
nume VARCHAR(20) NOT NULL, sex ENUMCFVM1) NOT NULL,
elev_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ) Tastaţi această instrucţiune
în mysql sau executaţi următoarea comandă de interpreter:
% mysql samp_db < creat e_student.sql
Instrucţiunea CREATE TABLE creează un tabel denumit elev cu trei coloane: nume, sex şi elev_id.
nume este o coloană şir de lungime variabilă, care poate conţine maximum 20 de caractere. Această reprezentare
a numelor este mai simplă decât cea folosită pentru tabelele Ligii istorice, folosind o singură coloană în locul
unor coloane separate pentru nume şi prenume. Aceasta deoarece ştiu dinainte că nici un exemplu de interogare
privind evidenţa rezultatelor şcolare nu va trebui să efectueze vreo operaţie care s-ar preta mai bine la două
coloane. (Da, trişez. Recunosc.)
sex determină dacă un elev este băiat sau fata. Este o coloană de tip ENUM (enumerare), ceea ce înseamnă că
poate lua numai una din valorile enumerate explicit în specificaţia coloanei, în speţă M sau F, adică de sex
masculin sau feminin. ENUM este util când aveţi un set limitat de valori pe care le poate conţine o coloană. Am
fi putut folosi în schimb CHAR(1 ), dar ENUM specifică mai exact care pot fi valorile din coloană. Dacă emiteţi
o instrucţiune DESCRIBE numejtabel pentru un tabel care conţine o coloană ENUM, MySQL vă va indica exact
care sunt valorile posibile.
58
Partea l Utilizarea generală a sistemului MySQL
Apropo, valorile dintr-o coloană ENUM nu trebuie să fie neapărat compuse dintr-un singur caracter. Coloana tip
ar fi putut fi declarată astfel: ENUM (' feminin', ' masculin').
elev_id este o coloană de tip întreg care va conţine numere unice de identificare a elevilor, în mod normal, veţi
primi numerele de identificare a elevilor de la o sursă centrală, cum ar fi secretariatul scolii, dar noi vom folosi
valori inventate. Declaraţia coloanei elev_id are numeroase părţi, chiar dacă nu conţine decât numere:
• I NT arată că în coloană sunt incluse numere întregi (valori fără parte fracţionară)
• UNSIGNED interzice utilizarea numerelor negative.
• NOT NULL înseamnă că valoarea coloanei trebuie completată. (Nici un elev nu poate fi lipsit de un număr de
identificare.)
• AUTO_INCREMENT este un atribut special în MySQL si funcţionează astfel: dacă valoarea din coloana
elev_id lipseşte (sau este NULL), atunci când creaţi o nouă înregistrare în tabelul elev, MySQL generează
automat un număr unic cu o unitate mai mare decât valoarea maximă care se află în coloană la momentul
respectiv. Vom folosi acest fapt la încărcarea tabelului elev, specificând valori numai pentru coloanele nume şi
sex si permiţând sistemului MySQL să genereze automat valoarea coloanei elev_id.
• PRIMARY KEY arată că acea coloană este indexată pentru căutări rapide şi că fiecare valoare din coloană
trebuie să fie unică. Aceasta ne împiedică să folosim din greşeală acelaşi identificator de două ori, ceea ce este o
proprietate necesară a numerelor de identificare a elevilor. (Nu numai atât; MySQL impune ca fiecare coloană
AUTO_INCREMENT să aibă un indice unic.)
Dacă nu înţelegeţi toate chestiile astea despre AUTO_INCREMENT si PRIMARY_KEY, gândiţi-vă că ele
reprezintă o modalitate magică de a genera numere de identificare pentru fiecare elev. Nu contează valorile în
sine, atâta vreme cât sunt unice.
Notă: Dacă aveaţi de gând să luaţi identificatoarele numerice ale elevilor de la secretariat (fără să le generaţi
automat), puteţi declara coloana elev_id în acelaşi mod, cu deosebirea că veţi omite atributul
AUTO_INCREMENT.
Tabelul eveniment se prezintă astfel: CREATE TABLE eveniment
data DATE NOT NULL
tip ENUM('T','C1) NOT NULL,
eveniment_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
Tastaţi instrucţiunea de mai sus în mysql sau executaţi următoarea comandă de interpreter:
% mysql samp_db < create_event.sql
Toate coloanele sunt declarate ca NOT NULL, deoarece nu poate lipsi valoarea nici uneia dintre ele.
Coloana data conţine o valoare DATE standard MySQL, în format "AAAA-LL-ZZ" (cu anul scris primul).
tip reprezintă tipul punctajului. Ca si sex din tabelul elev, tip este o coloană de tip enumerare. Valorile permise
sunt T si C, reprezentând „test", respectiv „chestionar".
Capitolul 1 Introducere în MySQL şi SQL
59
eveniment_id este o coloană AUTO_INCREMENT, similară coloanei elev_id din tabelul elev. Utilizarea
opţiunii AUTO_INCREMENT ne permite să generăm cu uşurinţă valori unice ale identificatorilor de eveniment.
Ca în cazul coloanei elev_id din tabelul elev, valorile particulare sunt mai puţin importante decât unicitatea lor.
Tabelul puncte arată astfel: CREATE TABEL puncte (
elev_id INT UNSIGNED NOT NULL, eveniment_id INT UNSIGNED NOT NULL, punctaj INT NOT
NULL, PRIMARY KEY (eveniment_id, elev_id) ) Tastaţi acea instrucţiune în mysql sau executaţi următoarea
comandă de interpreter:
% mysql samp_db < creat e_score.sql
puncte este o coloană INT (întreg). Cu alte cuvinte, se presupune că valorile punctajelor sunt întotdeauna întregi.
Dacă doriţi să permiteţi includerea unor punctaje precum 58 , 5, care conţin o pane fracţionară, veţi folosi unul
din tipurile de coloană cu virgulă mobilă, precum FLOAT sau DECIMAL.
Coloanele elev_id şi eveniment_id sunt întregi care reprezintă elevul şi evenimentul pentru care a fost acordat
fiecare punctaj. Folosindu-le pentru a stabili legături între tabelele elev si eveniment, vom putea determina
numele elevului si data evenimentului. De asemenea, am transformat combinaţia dintre cele două coloane într-o
cheie primară (PRIMARY KEY). Astfel, ne asigurăm că nu vom avea punctaje repetate pentru un elev la un test
sau chestionar dat. De asemenea, modificarea ulterioară a unui punctaj este mai simplă. De exemplu, când un
punctaj a fost introdus incorect, putem elimina înregistrarea veche atunci când inserăm înregistrările noi,
folosind instrucţiunea MySQL REPLACE. Nu este necesar să folosim o instrucţiune DELETE combinată cu
INSERT; MySQL execută automat această operaţie.
Reţineţi unicitatea combinaţiei între coloanele eveniment_id si elev_id. în tabelul puncte, nici o valoare nu este
intrinsec unică. Vor exista mai multe înregistrări de punctaje pentru fiecare valoare eveniment_id (câte una
pentru fiecare elev), respectiv mai multe înregistrări pentru fiecare valoare elev_id (câte una pentru fiecare
chestionar şi test).
Tabelul absente pentru determinarea prezenţei la curs se prezintă astfel: CREATE TABLE absente
elev_id INT UNSIGNED NOT NULL, data DATE NOT NULL, PRIMARY KEY (elev_id, data) )
Tastaţi instrucţiunea respectivă în mysql sau executaţi următoarea comandă de interpreter: % mysql sarap_db <
create_absence.sql
60
Partea l Utilizarea generală a sistemului MySQL
Coloanele elev_id şi data sunt ambele declarate NOT NULL pentru a interzice absenţa valorilor. Vom
transforma combinaţia dintre cele două coloane într-o cheie primară, astfel încât să nu creăm din greşeală
înregistrări dublate. La urma urmelor, nu-i frumos să pui absenţă unui elev de două ori într-o zi!
Adăugarea de noi înregistrări
în acest moment, baza noastră de date si tabelele au fost create, iar în secţiunea următoare, „Regăsirea
informaţiilor", veţi afla cum se pot extrage datele din baza de date. Mai întâi însă, trebuie să inserăm unele
înregistrări în tabele.
Există numeroase modalităţi de a adăuga date într-o bază de date. Puteţi insera manual înregistrări într-un tabel
emiţând instrucţiuni INSERT. De asemenea, puteţi adăuga înregistrări citindu-le dintr-un fişier, fie sub formă de
valori de date brute pe care le încărcaţi folosind instrucţiunea LOAD DATA sau utilitarul mysqlimport, fie în
formă de instrucţiuni INSERT redactate anterior, pe care le introduceţi în mysql.
Această secţiune ilustrează fiecare metodă de inserţie a înregistrărilor în tabelele dumneavoastră. Ce aveţi
dumneavoastră de făcut este să vă jucaţi cu diferitele metode, pentru a vedea cum funcţionează. Apoi, deplasaţi-
vă la sfârşitul secţiunii şi rulaţi comenzile pe care le găsiţi acolo pentru a şterge conţinutul tabelelor si pentru a le
reîncărca. Astfel, vă veţi asigura că tabelele conţin aceleaşi înregistrări la care am lucrat când am scris secţiunea
următoare, iar dumneavoastră veţi obţine aceleaşi rezultate.
Să începem să adăugăm rezultate folosind INSERT, o instrucţiune SQL pentru care specificaţi tabelul în care
doriţi să inseraţi un rând de date şi valorile care vor fi plasate în rândul respectiv.
Instrucţiunea INSERT are mai multe forme:
• Puteţi specifica valori pentru toate coloanele:
INSERT INTO nume_tabel VALUES(valoare1,valoare2,...) De exemplu:
mysql> INSERT INTO elev VALUES('Minai1,'M1,NULL);
mysql> INSERT INTO eveniment VALUESC11999-9-3","C",NULL); Cuvântul INTO este opţional începând
de la MySQL 3.22.5. (Acest lucru este valabil si pentru celelalte forme ale instrucţiunii INSERT.) Lista
VALUES trebuie să conţină o valoare pentru fiecare coloană din tabel, în ordinea în care sunt stocate coloanele
în tabel, (în mod normal, aceasta este ordinea în care au fost specificate coloanele la crearea tabelului. Folosiţi
DESCRIBE nume_tabel pentru a afla ordinea, dacă nu o cunoaşteţi cu exactitate.)
Puteţi încadra între ghilimele valorile de tip şir şi dată în MySQL, folosind ghilimele simple sau duble. Valorile
NULL din exemplul precedent sunt destinate coloanelor AUTO_INCREMENT din tabelele elev si eveniment.
(Inserţia unei „valori lipsă" determină generarea următorului număr elev_id sau eveniment_id.)
Versiunile MySQL începând de la 3.22.5 vă permit să inseraţi mai multe rânduri într-un tabel cu o singură
instrucţiune INSERT, prin specificarea mai multor liste de valori: INSERT INTO nume_tabel VALUES(...),
(...),...
Capitolul 1 Introducere în MySQL şi SQL
61
De exemplu: mysql> INSERT INTO elev VALUES('Ana1,'F1,NULL),("Mihai',"M",NULL);
Acest procedeu necesită introducerea de la tastatură a unei cantităţi mai reduse de text decât cea impusă de mai
multe instrucţiuni INSERT, iar execuţia sa de către server este mai eficientă.
• Puteţi denumi coloanele cărora doriţi să le atribuiţi valori, apoi enumeraţi valorile. Acest procedeu este util
când doriţi să creaţi o înregistrare pentru care numai câteva coloane trebuie configurate iniţial.
INSERT INTO nume_tabel (nume_col1 ,nume_col2,...) VALUES(valoare7,valoare2,...) De exemplu:
mysql> INSERT INTO membru (nume,prenume) VALUES('Tache','Ştefan'); De la MySQL 3.22.5, această
formă a instrucţiunii INSERT permite de asemenea mai multe liste cu valori:
mysql> INSERT INTO elev (nume,sex) VALUES('Ana1,'F'),("Mihai","M"); Coloanele care nu sunt specificate
în lista de coloane primesc o valoare prestabilită.
• De la MySQL 3.22.10, puteţi denumi coloanele şi valorile sub forma nume_coloana=valoare.
INSERT INTO nume_tabel SET nume_col1=valoare1, nume_co!2=valoare2,... De exemplu:
mysql> INSERT INTO membru SET nume='Tache',prenume='Ştefan'; Toate coloanele care nu sunt specificate
în clauza SET primesc o valoare prestabilită. Nu puteţi insera mai multe rânduri folosind această formă a
instrucţiunii INSERT.
O altă metodă pentru încărcarea înregistrărilor într-un tabel este citirea valorilor datelor direct dintr-un fişier.
Puteţi încărca înregistrări în acest mod cu ajutorul instrucţiunii LOAD DATA sau cu utilitarul mysqlimport.
Instrucţiunea LOAD DATA se comportă ca un încărcător de mare capacitate, care citeşte date dintr-un fişier.
Folosiţi această instrucţiune din interiorul programului mysql:
mysql> LOAD DATA LOCAL INFILE "membru.txt" INTO TABLE membru; Această instrucţiune citeşte
conţinutul fişierului de date membru. txt localizat în catalogul dumneavoastră curent din gazda programului
client şi o trimite serverului, pentru a fi încărcată în tabelul membru.
LOAD DATA LOCAL nu va avea efect dacă versiunea dumneavoastră de MySQL este anterioară versiunii
3.22.15, deoarece posibilitatea de citire a fişierelor de la client a fost adăugată la instrucţiunea LOAD DATA
începând de la versiunea respectivă. (Fără cuvântul cheie LOCAL, fişierul trebuie localizat pe gazda serverului si
dumneavoastră aveţi nevoie de un privilegiu de acces la server, de care majoritatea utilizatorilor MySQL nu
dispun.)
în mod prestabilit, instrucţiunea LOAD DATA presupune că valorile coloanelor sunt separate prin tabulator! şi
că rândurile se încheie cu un caracter linie nouă. De asemenea, presupune că valorile există în ordinea în care
coloanele sunt stocate în tabel. Este posibilă citirea fişierelor în alte formate sau specificarea unei alte ordini a
coloanelor. Vezi rubrica aferentă comenzii LOAD DATA din Anexa D, „Referinţă de sintaxă SQL", pentru mai
multe detalii.
62 Partea l Utilizarea generală a sistemului MySQL
Utilitarul mysqlimport este o interfaţă în linie de comandă pentru instrucţiunea LOAD DATA. Acest utilitar se
invocă din interpretor şi generează automat o instrucţiune LOAD DATA:
% mysqlimport --local sarap_db nembru.txt
mysqlimport generează automat o instrucţiune LOAD DATA, care determină încărcarea fişierului membru. txt
în tabelul membru. Acest lucru nu este posibil dacă programul dumneavoastră MySQL este anterior versiunii
3.22.15, deoarece opţiunea --local necesită LOAD DATA LOCAL. Ca si în cazul programului mysql, dacă
trebuie să specificaţi parametri de conexiune, indicaţi-i în linia de comandă, anterior numelui bazei de date.
mysqlimport extrage numele tabelului din numele fişierului de date. (Ca nume al tabelului, se folosesc toate
caracterele, până la primul punct din numele fişierului.) De exemplu, membru.txt va fi încărcat în tabelul
membru, iar presedinte.txt va fi încărcat în tabelul preşedinte. Dacă aveţi mai multe fişiere de încărcat într-un
singur tabel, alegeţi numele fişierelor cu atenţie; în caz contrar, mysqlimport nu va folosi numele corect de tabel.
Cu nume ca membrul .txt sau membru2.txt, mysqlimport va presupune că numele tabelelor sunt membrul,
respectiv membru2. Totuşi, puteţi folosi nume precum membru. 1 .txt şi membru.2.txt, respectiv membru.txt! şi
membru.txt2.
După ce aţi testat aceste metode de adăugare a înregistrărilor, trebuie să ştergeţi conţinutul tabelelor şi apoi să le
reîncărcaţi, astfel încât conţinutul lor să fie cel presupus în secţiunea următoare.
Din interpretorul dumneavoastră, executaţi următoarele comenzi:
% ntysql sarap_db < insert_president.sql
% mysql samp_db < insertjnember.sql
% mysql samp_db < insert_student.sql
% mysql samp_db < insert_score.sql
% mysql samp_db < insert_event.sql
% mysql samp_db < insert_absence.sql
Fiecare fişier conţine o instrucţiune DELETE pentru ştergerea tuturor înregistrărilor pe care le-aţi inserat în
tabel, urmată de un set de comenzi INSERT pentru reiniţializarea conţinutului tabelului. Dacă nu doriţi să scrieţi
fiecare din aceste comenzi în parte, încercaţi următoarea comandă:
% cat insert_*.sql | mysql samp_db
Regăsirea informaţiilor
Acum, tabelele noastre au fost create şi încărcate cu date, deci haideţi să vedem ce putem face cu acele date.
Instrucţiunea SELECT vă permite să regăsiţi si să afişaţi informaţii din tabelele dumneavoastră, într-o manieră
oricât de generală sau de specifică doriţi. Puteţi afişa întregul conţinut al unui tabel:
SELECT * FROM preşedinte Sau puteţi selecta o singură coloană a unui singur rând:
SELECT data_nastere FROM preşedinte WHERE nume = "Eisenhower"
Capitolul 1 Introducere în MySQL şi SQL
63
Instrucţiunea SELECT are numeroase clauze (părţi) pe care le combinaţi conform necesităţilor pentru a regăsi
informaţia care vă interesează. Fiecare dintre aceste clauze poate fi simplă sau complexă, deci instrucţiunile
SELECT în totalitatea lor pot fi simple sau complexe. Totuşi, puteţi sta liniştit; nu veţi găsi în această carte
interogări lungi de o pagină, pentru care aveţi nevoie de o oră pentru a le pricepe. (Când găsesc într-un material
interogări lungi de-un cot, în general le omit fără mustrări de conştiinţă si bănuiesc că si dumneavoastră
procedaţi la fel.)
Forma generală a instrucţiunii SELECT este:
SELECT ce se selectează
FROM tabel sau tabele
WHERE condiţii pe care trebuie sa le satisfacă datele
Nu uitaţi că SQL este un limbaj cu "format liber; deci, când vă scrieţi propriile dumneavoastră interogări
SELECT, nu este necesar să inseraţi salturile la linie nouă acolo unde le-am inserat eu.
Pentru a scrie o instrucţiune SELECT, specificaţi ce anume doriţi să regăsiţi şi apoi unele clauze opţionale.
Clauzele prezentate mai sus (FROM, WHERE) sunt cel mai frecvent folosite, deşi mai pot fi specificate si altele,
cum sunt GROUP BY, ORDER BY si LIMIT.
Clauza FROM este de regulă prezentă, dar acest lucru nu este necesar dacă nu selectaţi date din tabele. De
exemplu, interogarea următoare afişează pur si simplu valorile unor expresii care pot fi calculate fără referire la
vreun tabel, deci nu este necesară nici o clauză FROM:
mysql> SELECT 2+2, "Salut, lume', VERSION();
2+2 i Salut, lume j VERSION()
4 j Salut, lume i 3.23.0'-alpha-log
Când folosiţi o clauză FROM pentru a specifica un tabel de unde vor fi regăsite datele, forma cea mai „generică"
a instrucţiunii SELECT este aceea prin care se regăsesc toate datele. Pentru aceasta, folosiţi caracterul *, care
este o abreviere de la „toate coloanele". Interogarea următoare regăseşte toate rândurile din tabelul elev si le
afişează: mysql> SELECT * FROM elev;
nume sex elev_id
Megan F 1
Joseph M 2
Kyle M 3
Katie p 4
Coloanele sunt returnate în ordinea în care MySQL le-a stocat în tabel. Aceasta este ordinea în care coloanele
sunt afişate atunci când se emite o instrucţiune DESCRIBE elev. Punctele de suspensie de la sfârşitul exemplului
arată că interogarea returnează mai multe rânduri decât cele prezentate.
Puteţi denumi în mod explicit coloana sau coloanele pe care doriţi să le vedeţi. Pentru a selecta numai numele
elevilor, procedaţi astfel:
64 Partea l Utilizarea generală a sistemului MySQL
mysql> SELECT nume FROM elev;
nume
Megan Joseph Kyle Katie
Dacă precizaţi mai mult de o coloană, separaţi numele coloanelor prin virgule. Instrucţiunea următoare este
echivalentă cu SELECT * FROM elev, dar fiecare coloană este specificată în mod explicit:
mysql> SELECT nume,sex,elev_id FROM elev;
nume sex elev_id
Megan F 1
Joseph M 2
Kyle M 3
Katie F 4
Puteţi specifica dacă sortarea unei coloane se va face în ordine ascendentă sau descendentă folosind cuvintele
cheie ASG sau DESC după numele coloanelor din clauza ORDER BY. De exemplu, pentru a sorta numele
preşedinţilor în ordine inversă (descrescătoare), folosiţi DESC astfel:
mysql> SELECT nune, prenume FROM preşedinte -> ORDER BY nume DESC;
nume prenume
Wilson Wood row
Washington George
Van Buren Martin
Tyler John
Ordinea ascendentă este cea prestabilită dacă nu specificaţi ASC sau DESC pentru un nume de coloană într-o
clauză ORDER BY.
Dacă sortaţi o coloană care poate conţine valori NULL, toate valorile NULL apar la începutul coloanei, respectiv
la sfârşit, dacă sortaţi în ordine descendentă.
Rezultatele interogării pot fi sortate pe mai multe coloane, iar fiecare coloană poate fi sortată în ordine
ascendentă sau descendentă, independent de celelalte coloane. Interogarea următoare regăseşte rânduri din
tabelul preşedinte, le sortează în sens descendent în funcţie de statul de naştere şi după numele de familie în
cadrul fiecărui stat: mysql> SELECT nume, prenume, stat FROM preşedinte -> ORDER BY stat DESC, nume
ASC;
nume prenume stat
Arthur Chester A. VT
Coolidge Calvin VT
Harrison William H. VA
Jefferson Thomas VA
Madison James VA
Monroe James VA
Taylor Zachary VA
Tyler John VA
Washington George VA
Wilson Woodrow VA
Eisenhower Dwight D. TX
Johnson Lyndon B. TX
Această interogare formatează numele preşedinţilor sub forma unui singur şir, prin concatenarea numelui si a
prenumelui separate printr-un spaţiu şi formatează locul naşterii sub forma oraşelor şi statelor separate prin
virgulă.
Când folosiţi o expresie pentru a calcula valoarea unei coloane, expresia este folosită pentru antetul coloanei.
Aceasta poate duce la o coloană foarte lată, dacă expresia este lungă (aşa cum o arată interogarea precedentă).
Pentru a rezolva problema, capului de coloană i se poate atribui un nume, folosind construcţia AS nume.
Asemenea nume se numesc aliasuri de coloană. Rezultatul interogării anterioare poate deveni astfel mai
semnificativ:
mysql> SELECT CONCAT(prenume," ",nuBe), AS Nume,
-> CONCAT(oraş,', ",stat) As Loculnasterii
-> FROM preşedinte;
Nume i Loculnasterii
iwakefield, VA
George Washington John
JBraintree, MĂ
Adams Thomas Jefferson VA
iAlbemarle County, i Port
James Madison
Conway, VA
în cazul în care aliasul coloanei conţine spaţii, acestea vor trebui inserate între ghilimele: mysql> SELECT
CONCAT(prenume," *,nune), AS 'Nunele preşedintelui* -> CONCAT(oras,", ",stat) As "Locul naşterii" ->
FROM preşedinte;
72 Partea 1 Utilizarea generală a sistemului MySQL
Aceeaşi formă de interogare indică numărul de preşedinţi născuţi în fiecare stat: mysql> SELECT stat,
COUNT(*) FROM preşedinte GROUP BY stat;
i stat COUNT(*)
i AR 1
i CA 1
i GA 1
;IA 1
•ÎL 1
!KY 1
i MĂ 4
i MO 1
i NC 2
INE 1
;NH 1
IN j 1
iNY 4
i OH 7
i PA 1
jsc 1
;TX 2
i VA 8
iVT 2
Când numărau valorile în acest mod, clauza GROUP BY este necesară, deoarece indică programului MySQL
modul de grupare a valorilor înainte de a le număra. Dacă omiteţi această clauză, veţi primi un mesaj de eroare.
Utilizarea funcţiei COUNT(*) cu GROUP BY pentru numărarea valorilor are un număr de avantaje în raport cu
numărarea individuală a apariţiilor fiecărei valori distincte din coloană:
• Nu trebuie să ştiţi dinainte care sunt valorile prezente în coloana al cărei conţinut îl rezumaţi.
• Aveţi nevoie de o singură interogare, nu de mai multe interogări.
• Obţineţi toate rezultatele într-o singură interogare, deci puteţi sorta datele de ieşire.
Primele două avantaje sunt importante pentru exprimarea cu mai mare uşurinţă a interogărilor. Cel de-al treilea
avantaj este important deoarece vă permite o flexibilitate în afişarea rezultatelor.
Capitolul 1 Introducere în MySQL şi SQL
79
Când folosiţi o clauza GROUP BY, rezultatele sunt sortate în coloanele în funcţie de care realizaţi gruparea, dar
puteţi folosi ORDER BY pentru a sorta într-o altă ordine. De exemplu, dacă doriţi ca numerele de preşedinţi să
fie grupate în funcţie de statul de naştere, dar să fie şi sortate în ordinea descrescătoare a celor mai bine
reprezentate state, puteţi folosi o clauză ORDER BY după cum urmează:
mysql> SELECT stat, COUNT(*) AS număr FROM preşedinte -> GROUP BY stat ORDER BY număr DESC;
istat număr
!VA 8
;OH 7
ţ MĂ 4
;NY 4
INC 2
iVT O
;TX n
isc 1
;NH 1
i PA 1
;KY 1
!NJ 1
i IA 1
i MO 1
:CA 1
i NE 1
;GA 1
;IL 1
IAR 1
Când coloana după care doriţi să sortaţi este determinată prin calcul, puteţi da coloanei un alias şi puteţi face
referire la alias în clauza ORDER BY. Interogarea anterioară demonstrează acest fapt; coloana COUNT(*) este
denumită număr. O altă modalitate de a face referire la o asemenea coloană este prin poziţia sa în datele de
ieşire. Interogarea anterioară ar fi putut fi scrisă si astfel:
SELECT state, COUNT(*) FROM preşedinte
GROUP BY stat ORDER BY 2 DESC
Nu cred că desemnarea unei coloane prin poziţia acesteia este foarte sugestivă. Dacă adăugaţi, eliminaţi sau re-
ordonaţi coloanele cu datele de ieşire, trebuie să vă amintiţi să verificaţi clauza ORDER BY si să corectaţi
numărul coloanei, dacă acesta s-a modificat. Aliasurile nu ridică această problemă.
Dacă doriţi să folosiţi GROUP BY cu o coloană care conţine valori calculate, trebuie să faceţi referire la acea
coloană folosind un alias sau poziţia coloanei, aşa cum se procedează în cazul clauzei ORDER BY. Următoarea
interogare determină numărul preşedinţilor care s-au născut în fiecare lună a anului:
mysql> SELECT MONTH(data_nastere) as Luna,
-> MONTHNAME(datajiastere) AS Nume, COUNT(*) AS număr -> FROM preşedinte GROUP BY Nume
ORDER BY Luna;
80
Partea l Utilizarea generală a sistemului MySQL
Luna Nume număr
1 Ianuarie A
2 Februarie 4
3 Martie 4
4 Aprilie 4
5 Mai 2
/ţ Iunie 1
-Ţ Iulie 3
B August 4
9 Septembrie 1
10 Octombrie 6
11 Noiembrie 5
12 Decembrie 3
Folosind poziţiile coloanelor, interogarea poate fi scrisă astfel:
SELECT MONTH(data_nastere), MONTHNAME(data_nastere), COUNT(*) FROM preşedinte GROUP BY
2 ORDER BY 1;
COUNTQ poate fi combinată cu ORDER BY şi LIMIT pentru a găsi, de exemplu, cele mai
bine reprezentate patru state din tabelul preşedinte:
mysql> SELECT stat, COUNT(*) AS nuiar FROM preşedinte -> GROUP BY stat ORDER BY număr DESC
LIMIT 4;
i stat număr
i VA 8
i OH 7 4 4
i MĂ
JNY
Dacă nu doriţi să limitaţi datele de ieşire ale interogării folosind o clauză LIMIT, ci prin căutarea anumitor valori
ale funcţiei COUNT(), utilizaţi o clauză HAVING. Interogarea următoare vă va indica numărul de state
reprezentate prin doi sau mai mulţi preşedinţi: mysql> SELECT stat, COUNT(*) AS miiar FROM preşedinte
-> GROUP BY stat HAVING nunar > 1 ORDER BY număr DESC;
istat număr
i VA 8
;OH 7
;MA 4
•NY 4
;NC 2
iVT 2
•TX 2
în general, acesta este tipul de interogare care trebuie executat atunci când doriţi să găsiţi într-o coloană valori
care se repetă.
HAVING este similar cu WHERE, dar se aplică după ce au fost selectate rezultatele interogării si este folosit
pentru a reduce domeniul rezultatelor trimise efectiv de server clientului.
Există funcţii de sumar altele decât COUNT (). Funcţiile MIN (), MAX (), SUM () si AVG () sunt utile pentru
determinarea valorii minime, maxime, totale şi medii dintr-o coloană. Le puteţi chiar
Capitolul 1 Introducere în MySQL şi SQL
81
folosi pe toate simultan. Următoarea interogare prezintă diferite caracteristici numerice pentru fiecare chestionar
şi test pe care l-ati dat. De asemenea, indică numărul de punctaje incluse în calculul fiecărei valori. (Unii elevi au
fost absenţi şi, ca atare, nu au fost număraţi.) mysql> SELECT
-> eveniment_id,
-> MIN(puncte) AS minim,
-> MAX(puncte) AS maxim,
-> MAX(puncte)-MIN(puncte)+1 AS domeniu,
-> SUM(puncte) AS total,
-> AVG(puncte) AS medie,
-> COUNT(puncte) AS număr,
-> FROM puncte
-> GROUP BY eveniment_id;
eveniment_id minim maxim domeniu total medie număr
1 9 20 12 439 15.1379 29
2 8 19 12 425 14.1667 30
3 60 97 38 2425 78.2258 31
4 7 20 14 379 14.0370 27
5 8 20 13 383 14.1852 27
6 62 100 39 2325 80.1724 29
Aceste informaţii pot deveni mai semnificative dacă aţi şti, desigur, dacă valorile respective au fost obţinute la
punctaje sau teste. Totuşi, pentru a obţine acele informaţii, trebuie să consultăm şi tabelul eveniment; vom reveni
la această interogare în secţiunea „Regăsirea informaţiilor din mai multe tabele".
Funcţiile de sumar sunt distractive, deoarece sunt foarte puternice, dar vă puteţi lăsa foarte uşor „dus de val".
Priviţi această interogare: mysql> SELECT
-> stat AS Stat,
-> AVG((TO_DAYS(data_deces)-TO_DAYS(data_nastere))/365) AS Vârsta
-> FROM preşedinte WHERE data_deces IS NOT NULL
-> GROUP BY state ORDER BY Vârsta;
i stat ! vârsta j ; stat i vârsta i
iKY j 56. 20821 9! !TX î 71. 476712
j
;VT ; 58. 852055! :MA ! 72.
642009;
; NC ; 60. 141096! ;VA ! 72.
822945;
;OH ! 62. 8661 !PA '• 77.
45 : 158904;
;NH ! 64. 91 7808! !SC i 78.
284932!
;NY i 69. 342466! ;CA ; 81.
336986!
:NJ ! 71. 31 5068; !MO j 88. 6931
51 !
!IA ! 90.
254795!
Interogarea selectează preşedinţii care au decedat, îi grupează în funcţie de data naşterii, le calculează vârsta la
data decesului, calculează vârsta medie (per stat) şi apoi sortează rezultatele după vârsta medie. Cu aljte cuvinte,
interogarea determină, pentru preşedinţii care nu mai sunt în viaţă, vârsta medie la data decesului în funcţie de
statul de naştere.
82 Partea l Utilizarea generală a sistemului MySQL
Şi ce înseamnă asta? înseamnă numai că puteţi scrie interogarea. Evident, nu înseamnă că interogarea merită
scrisă. Nu toate operaţiile pe care le puteţi aplica unei baze de date sunt la fel de semnificative; totuşi, uneori
oamenii devin extrem de fericiţi când află ce anume pot face cu baza lor de date. Astfel se poate explica apariţia
unui număr tot mai mare de statistici din ce în ce mai ezoterice (şi mai lipsite de sens) cu ocazia evenimentelor
sportive televizate din Statele Unite în ultimii ani. Statisticienii sportivi îşi pot folosi bazele de date pentru a
determina tot ce doriţi să ştiţi despre o echipă, dar şi tot ce nu doriţi să ştiţi. Chiar este atât de important să ştiţi
care fundaş din Divizia A (mutatis mutandis pentru realitatea românească - N.T.) are la activ cele mai multe
intercepţii în careul de 16 metri atunci când echipa lui conduce cu mai mult de două goluri diferenţă, când până
la sfârşitul reprizei a doua sunt mai puţin de 15 minute şi antrenorul încă n-a făcut cele trei schimbări
regulamentare?
Regăsirea informaţiilor din mai multe tabele
Interogările pe care le-am scris până acum au extras date dintr-un singur tabel. Acum ajungem la partea
interesantă. Am mai spus că puterea unui SGBD relaţional rezidă în capacitatea sa de a corela un aspect cu altul,
deoarece acest fapt vă permite să combinaţi informaţii din mai multe tabele pentru a găsi răspunsul la întrebări la
care nu se poate răspunde consultând un singur tabel. Această secţiune vă prezintă modul de scriere a
interogărilor de regăsire a informaţiilor din mai multe tabele.
Când selectaţi informaţii din mai multe tabele, efectuaţi o operaţie denumită unire. Aceasta deoarece produceţi
un rezultat prin unirea informaţiilor dintr-un tabel cu informaţiile din celălalt tabel, operaţie care are loc prin
coroborarea înregistrărilor din mai multe tabele care conţin valori comune.
Să parcurgem un exemplu. Anterior, în secţiunea „Tabele pentru proiectul de evidenţă a rezultatelor şcolare", a
fost prezentată, fără nici o explicaţie, o interogare pentru regăsirea punctajelor de la chestionare sau teste date la
o anumită dată. Acum este momentul să dăm explicaţia. De fapt, interogarea implică o unire pe trei căi, deci o
vom descrie în două etape.
în prima etapă, construim o interogare pentru selectarea punctajelor obţinute la o anumită dată, după cum se
poate vedea mai jos: mysql> SELECT elev_id, data, puncte, tip
-> FROM eveniment, puncte
-> WHERE data = '1999-09-23'
-> AND eveniment.eveniraent_id = puncte.eveniment_id;
elev_id data puncte tip
1 1999-09-23 15 •c
f\ 1999-09-23 12 C
3 1999-09-23 11 C
5 1999-09-23 13 c
6 1999-09-23 18 c
Kyle Abby
3 5 10 17 Peter Will
20 Avery 1211
Notă: Deşi am furnizat un calificator în clauza GROUP BY, nu este absolut necesar pentru această interogare.
GROUP BY se referă la coloanele din lista de selecţie (din,primele două linii ale interogării). Aici există o
singură coloană denumită elev_id, deci MySQL cunoaşte coloana la care vă referiţi. Acest fapt este de asemenea
valabil pentru coloanele specificate în clauzele ORDER BY.
Datele de ieşire generate de interogare sunt acceptabile dacă dorim să ştim numai care sunt elevii cu absenţe.
Dar, dacă trimitem această listă la secretariatul şcolii, cei de acolo vor spune: „Dar ceilalţi elevi? Vrem o valoare
pentru fiecare elev." Aceasta este;o problemă uşor diferită, înseamnă că dorim să cunoaştem numărul absenţelor,
chiar şi pen-
Capitolul 1 Introducere în MySQL şi SQL
85
tru elevii care nu au avut nici o absenţă. Deoarece întrebarea este diferită, şi interogarea este diferită.
V ' .• ' " ••-. . ' "
"()..'
Pentru a răspunde la întrebare, putem folosi LEFT JOIN în loc de a asocia identificatorul de elev în clauza
WHERE. LEFT JOIN indică programului MySQL ;să genereze un rând de date de ieşire pentru fiecare rând
selectat din tabelul denumit primul în cadrul unirii (adică tabelul precizat în stânga cuvintelor cheie LEFT JOIN).
Precizând mai întâi tabelul elev, vom primi date de ieşire pentru fiecare elev, chiar şi pentru cei care nu sunt
reprezentaţi în tabelul absente. Interogarea se prezintă astfel: mysql> SELECT elev.elev_id, elev.nu»e,
-> COUNT(absente,date) A» absente
-> FROM elev LEFT JOIN absente
-> ON elev.elev_id = absente.elevJLd
-> GROUP BY elevielev£id; (
elev_id nume absente
.WAÎ.V.,.!-
:.:...
1 Megan 0
2 Joseph 0
3 Kyle 1
4 Katie 0
5 Abby 1
6 Nathan 0
7 Liesl 0
Anterior, în secţiunea „Generarea de sumare* am rulat a interogare care producea o caracterizare numerica a
datelor din tabelul puncte. Datele de ieşire ale acelei interogări afişau identificatorul de eveniment, dar nu
includeau data la care a fost obţinut punctajul sau tipul acestuia, deoarece atunci nu ştiam cum să unim tabelul
puncte cu tabelul eveniment, pentru .a obţine datele şi tipurile punctajelor. Acum ştim. Interogarea următoare
este similară celei pe care am executat-o anterior, dar prezintă data şi tipul punctajelor, nu numai identificatorul
numeric de eveniment:
mysql> SELECT :
, -> eveninent.data,eyeniseat.tip .
-> MIN(puncte.puncte) AS «inia, -> MAX(puncte.puncte) AS MxiM,
-> MAX(puncte.puncte)-MIN(puncte.puncte)+1 AS doieniu, -> SQM(puncte.puncte) AS total,
,
-> AVG(puncte.puncte) AS «edie,, -> COUNT(puncte.puncte) AS nimar, -> FROM puncte, eveniment
:j,',; .
-> «HERE puncte.eveni«ent_id = eveni*ent.eveniHent_14 • -> GROUP BY evenUent.data;
86
Partea l Utilizarea generală a.sistemului MySQL
data tip minim maxim domeniu total medie număr
1999-09-03 c 9 20 12 439 15.1379 29
1999-09-06 C 8 19 12 425 14.1667 30
1999-09-09 T 60 97 38 2425 78.2258 31
1999-09-16 r* 7 20 14 379 14.0370 27
1999-09-23 c Q 20 13 383 14.1852 27
1999-10-01 Ţ 62 100 39 2325 80.1724 29
Puteţi folosi funcţii precum COUNT () sau AVG() pentru a genera un sumar pe coloane multiple, chiar dacă
aceste coloane provin de la tabele diferite. Interogarea următoare determină numărul de punctaje si punctajul
mediu pentru fiecare combinaţie între data evenimentului şi sexul elevului:
mysql> SELECT eveniment.data, elev.sex,
-> COUNT(puncte) AS număr, AVG(puncte) AS medie
-> FROM eveniment, puncte, elev
-> WHERE eveniment.eveninent_id = puncte.elev_id
-> AND puncte.elev_id = elev.elev_id
-> GROUP BY eveniment.data, elev.sex;
data sex număr medie
1999-09-03 F 14 14.6429
1999-09-03 M 15 15.6000
1999-09-06 F 14 14.7143
1999-09-06 M 16 13.6875
1999-09-09 F 15 77.4000
1999-09-09 M 16 79.0000
1999-09-16 F 13 15.3077
1999-09-16 M 14 12.8571
1999-09-23 F 12 14.0833
1999-09-23 M 15 14.2667
1999-10-01 F 14 77.7857
1999-10-01 M 15 82.4000
Putem folosi o interogare similară pentru a efectua una dintre sarcinile aferente proiectului de evidenţă a
rezultatelor şcolare: calculul punctajului total al fiecărui elev la sfârşitul semestrului. Interogarea se prezintă
astfel:
SELECT elev.elev_id, elev.nume
SUM(puncte.puncte) AS total, COUNT(puncte.puncte) AS n
FROM eveniment, puncte, elev
WHERE eveniment.eveniment_id = puncte.eveniment_id
AND puncte.elev_id = elev.elev_id
GROUP BY puncte.elev_id
ORDER BY total
Nu există nici o cerinţă conform căreia o unire trebuie efectuată folosindu-se două tabele diferite. La început
poate părea ciudat, dar puteţi uni un tabel cu el însuşi. De exemplu, puteţi determina dacă au existat preşedinţi
născuţi în acelaşi oraş comparând locul naşterii fiecărui preşedinte cu locul naşterii tuturor celorlalţi preşedinţi:
Capitolul 1 Introducere în MySQL şi SQL
87
mysql> SELECT pl.nume, p1.prenume, pl.oras, pl.stat -> FROM preşedinte AS p1, preşedinte AS p2 ->
WHERE pl.oras = p2.oras AND pl.stat = p2.stat -> AND (pl.nume != p2.nume OR p1.prenume !=
p2.prenume) -> ORDER stat, oraş, nume;
nume prenume oraş stat
Adams John Quincy John Braintree MĂ
Adams Braintree MĂ
Există două aspecte ciudate ale acestei interogări:
• Trebuie să facem referire la două instanţe ale aceluiaşi tabel, deci vom crea aliasuri ale tabelului (p1, p2) şi le
vom folosi pentru a explicita referirile la coloanele tabelului.
• înregistrarea fiecărui preşedinte este echivalentă cu ea însăşi, dar nu dorim să vedem acest lucru în datele de
ieşire. Cea de-a doua linie a clauzei WHERE interzice corespondenţa unei înregistrări cu ea însăşi, asigurând
faptul că înregistrările comparate aparţin unor preşedinţi diferiţi.
O interogare similară găseşte preşedinţii care s-au născut în aceeaşi zi. Datele de naştere nu pot fi comparate
direct, deoarece astfel s-ar „pierde" preşedinţii care s-au născut în ani diferiţi, în schimb, vom folosi MONTH ()
şi DAYOFMONTH() pentru a compara ziua şi luna datei de naştere:
mysql> SELECT pl.nume, p1.prenume, p1.data_nastere
-> FROM preşedinte AS p1, preşedinte AS p2
-> WHERE MONTH(p1.data_nastere) = MONTH(p2.data_nastere)
-> AND DAYOFMONTH(p1.data_nastere) =
-> DAYOFMONTH(p2.data_nastere)
-> AND (pl.nume != p2.nume OR p1.prenume != p2.prenume)
-> ORDER BY pl.nume;
nume prenume data_nastere
Harding Polk Warren G. 1865-11-02
James K. 1795-11-02
Folosind funcţia DAYOFYEAR () în locul combinaţiei între funcţiile MONTH () şi DAYOFMONTH () s-ar
obţine o interogare mai simplă, dar s-ar produce rezultate incorecte atunci când se compară datele din anii bisecţi
cu datele din anii cu 365 de zile.
Unirile efectuate până acum au combinat informaţii din tabele între care există o oarecare relaţie logică
semnificativă, dar numai dumneavoastră cunoaşteţi semnificaţia acelei relaţii. MySQL nu ştie (sau nu-i pasă)
dacă tabelele unite au sau nu vreo legătură unul cu altul. De exemplu, puteţi uni tabelul eveniment cu tabelul
preşedinte, pentru a afla dacă aţi dat sau nu chestionare sau teste de ziua de naştere a unui preşedinte: mysql>
SELECT preşedinte.nume, preşedinte.prenume,
-> preşedinte.data_nastere, eveniment.tip
-> FROM preşedinte, eveniment
-> WHERE MONTH(presedinte.data_nastere) =
88 Partea l Utilizarea generală a sistemului MySQL
-> MONTH(eveniment.data)
-> ANO DAYOFMONTH(preşedinte.data_nastere)
-> OAYOFMONTH(eveniment.data);
nume prenume data_nastere tip
Carter James E 1924-10-01 T
Se pare că aţi dat un test de ziua de naştere a unui preşedinte. Şi ce dacă? Aceasta înseamnă că MySQL va fi
foarte bucuros să scoată la iveală rezultate, indiferent dacă ele sunt logice sau nu. Numai pentru că folosiţi un
calculator nu înseamnă obligatoriu că rezultatele unei interogări sunt utile sau că merită osteneala. Din fericire
sau din păcate, nu suntem scutiţi de a şti ce trebuie să facem.
Ştergerea sau actualizarea înregistrărilor existente
Uneori, doriţi să vă descotorosiţi de înregistrări sau doriţi să le modificaţi conţinutul. Instrucţiunile DELETE şi
tlPDATE vă permit aceste operaţii.
Instrucţiunea DELETE are următoarea formă:
DELETE FROM tabel_nume WHERE înregistrări care trebuie şterse Clauza WHERE indică înregistrările care
trebuie şterse si este opţională dar, dacă o omiteţi, vor fi şterse toate înregistrările. Aceasta înseamnă că
instrucţiunea DELETE cea mai simplă este şi cea mai periculoasă:
DELETE FROM nume_tabel
Această interogare şterge în totalitate conţinutul tabelului. Fiţi atent la interogarea respectivă!
Pentru a şterge anumite înregistrări, folosiţi clauza WHERE pentru a selecta înregistrările care vă interesează.
Aceasta este similară clauzei WHERE dintr-o instrucţiune SELECT. De exemplu, pentru a şterge din tabelul
preşedinte pe toţi preşedinţii născuţi în statul Ohio, folosiţi această interogare:
mysql> DELETE FROM preşedinte WHERE stat="OH";
Query OK, 7 rows affected (0.00 sec)
Un dezavantaj al clauzei WHERE pentru instrucţiunea DELETE este acela că puteţi face referire numai la
coloanele tabelului din care ştergeţi înregistrările.
înainte de a emite o instrucţiune DELETE, deseori este bine să testaţi clauza WHERE folosind-o cu o
instrucţiune SELECT, pentru a vă asigura că veţi şterge înregistrările pe care doriţi să le ştergeţi (si numai acele
înregistrări). Să presupunem că doriţi să ştergeţi înregistrarea pentru Teddy Roosevelt. Ar fi bună următoarea
interogare?
DELETE FROM preşedinte WHERE nume="Roosevelt"
Da, în sensul că ar şterge înregistrarea pe care o aveţi în vedere. Nu, în sensul că ar şterge şi înregistrarea lui
Franklin Roosevelt. Cel mai bine este să verificaţi în prealabil clauza WHERE, astfel:
mysql> SELECT nume, prenume FROM preşedinte -> WHERE nume="Roosevelt";
Capitolul 1 Introducere în MySQL şi SQL
89
prenume nume
Roosevelt Theodore
Roosevelt Franklin D.
Din acest exemplu se deduce necesitatea de a fi mai concret: mysql> SELECT nume, prenume FROM
preşedinte
-> WHERE nurae=nRoosevelt" AND prenume="Theodore";
prenume nume
Roosevelt Theodore
Acum ştim care este clauza WHERE corectă pentru selectarea înregistrării dorite, deci interogarea DELETE
poate fi construită corect: mysql> DELETE FROM preşedinte
-> WHERE nume="Roosevelt" AND prenume="Theodore"; Cam multă muncă pentru a şterge o înregistrare, nu-
i aşa? Paza bună trece primejdia rea!
(Aceasta este una din situaţiile în care veţi dori să reduceţi la minimum numărul caracterelor introduse de la
tastatură, prin utilizarea tehnicilor de copiere si lipire, respectiv de editare a liniilor de intrare. Vezi secţiunea
„Sugestii pentru interacţiunea cu mysql" pentru mai multe informaţii.)
Pentru modificarea înregistrărilor existente, folosiţi UPDATE, care are următoarea formă:
UPDATE nume_tabel SET coloane de modificat
WHERE înregistrări de actualizat
Clauza WHERE se foloseşte ca în cazul instrucţiunii DELETE şi este opţională deci, dacă nu o specificaţi, v or fi
actualizate toate înregistrările din tabel. Interogarea următoare transformă numele tuturor elevilor dumneavoastră
în „George":
mysql> UPDATE elev SET nume="George°; Evident, trebuie să fiţi atent cu înregistrări de acest gen.
De obicei, veţi fi mai concret în ceea ce priveşte înregistrările pe care le actualizaţi. Să presupunem că aţi
adăugat recent un membru al Ligii istorice, dar aţi completat numai câteva coloane din rubrica sa:
mysql> INSERT membru (nume,prenume)
-> VALUES('lonescu'.'Vasile');
Apoi, vă daţi seama că aţi uitat să menţionaţi data plăţii cotizaţiei. Puteţi remedia acest neajuns după cum
urmează: mysql> UPDATE membru
-> SET data_expirare='2001-7-20'
-> WHERE nume='lonescu' AND prenume='Vasile'
Puteţi actualiza mai multe coloane simultan. Instrucţiunea următoare actualizează adresa poştală şi de e-mail a
lui Vasile: mysql> UPDATE membru
-> SET email ='ivasiledyahoo.corn',
-> strada='123 Str. Lunga',oras='Nicăieri',
90 Partea l Utilizarea generală a sistemului MySQL
-> stat='NY',cod_postal='01003l
-> WHERE nume='lonescu' AND prenume='Vasile';
De asemenea, puteţi „anula" valoarea unei coloane atribuindu-i acesteia valoarea NULL (presupunând că acea
coloană acceptă valori NULL). Dacă la un anumit moment în viitor Vasile se decide să plătească acea cotizaţie
mare care îi permite să devină membru „pe viaţă", îi puteţi modifica înregistrarea în mod adecvat atribuind datei
de expirare valoarea NULL (în sensul că „nu expiră niciodată"): mysql> UPDATE membru
-> SET data_expirare=NULL
-> WHERE nume='lonescu1 AND prenune='Vasile';
în cazul instrucţiunii UPDATE, ca şi pentru DELETE, este bine să testaţi o clauză WHERE folosind o
instrucţiune SELECT, pentru a vă asigura că alegeţi înregistrările corecte pentru actualizare, în cazul în care
criteriile dumneavoastră de selecţie sunt prea înguste sau prea largi, veţi actualiza prea puţine, respectiv prea
multe înregistrări.
Dacă aţi încercat interogările din această secţiune, veţi fi şters şi modificat înregistrări din tabelele bazei de date
samp_db. înainte de a trece la secţiunea următoare, trebuie să anulaţi aceste modificări. Faceţi aceasta prin
reîncărcarea tabelelor folosind instrucţiunile de la sfârşitul secţiunii „Adăugarea de noi înregistrări".
Modificarea structurii tabelelor
Vă mai amintiţi când am creat tabelul membru al Ligii istorice fără o coloană a identificatorului numeric al
membrilor, ca pretext pentru a folosi instrucţiunea ALTER TABLE? Este momentul să folosim această
instrucţiune. Cu ajutorul instrucţiunii ALTER TABLE, puteţi redenumi tabele, adăuga sau şterge coloane,
modifica tipuri de coloane şi altele. Singurul exemplu pe care îl voi prezenta aici este modalitatea de a adăuga o
coloană nouă. Vezi capitolul 3 pentru mai multe detalii despre posibilităţile instrucţiunii ALTER TABLE.
Considerentul principal la adăugarea unei coloane cu identificatorul numeric al membrilor la tabelul membru
este acela că valorile trebuie să fie unice, pentru a se evita confuzia între rubrici. Aici este utilă o coloană
AUTO_INCREMENT, deoarece putem permite programului MySQL să genereze automat numere unice atunci
când adăugăm membri noi. într-o instrucţiune CREATE TABLE, specificaţia pentru o asemenea coloană ar arăta
astfel:
membru_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY Pentru instrucţiunea
ALTER TABLE, sintaxa este similară. Rulaţi această interogare pentru a adăuga coloana:
mysql> ALTER TABLE membru
-> ADD membru_id INT UNSIGNED NOT NULL AUTO_INCREMENT -> PRIMARY KEY;
Acum, când avem o nouă coloană pentru memorarea identificatorilor numerici ai membrilor, cum repartizăm
numere înregistrărilor existente din tabelul membru? Simplu; MySQL s-a ocupat deja de asta! Când adăugaţi o
coloană la un tabel, MySQL iniţializează valorile coloanelor cu valoarea prestabilită, în cazul unei coloane
AUTO_INCREMENT, aceasta determină generarea unui nou număr din secvenţă pentru fiecare rând.
Capitolul 1 Introducere în MySQL şi SQL
91
Sugestii pentru interacţiunea cu mysql
Această secţiune discută despre modul de a interacţiona cu programul client mysql într-o manieră mai eficientă
şi cu mai puţin text introdus de la tastatură. Este descris un mod mai simplu de conectare la server şi metode de
introducere a interogărilor fără a o tasta pe fiecare de la început până la sfârşit.
Simplificarea procesului de conexiune
Probabil că trebuie să specificaţi parametri de conexiune precum numele gazdei, numele de utilizator sau parola
atunci când invocaţi mysql. Pentru simpla rulare a unui program trebuie introdusă o cantitate serioasă de text,
lucru care devine foarte repede obositor. Există numeroase metode pentru simplificarea procesului de conectare,
prin reducerea la minimum a textului introdus de la tastatură:
• Utilizaţi un fişier cu opţiuni pentru stocarea parametrilor de conectare.
• Repetaţi comenzile beneficiind de caracteristicile interpretorului dumneavoastră de memorare a comenzilor
(istoric).
• Definiţi o scurtătură pentru linia de comandă mysql folosind un alias sau un script de interpretor.
Utilizarea unui fişier cu opţiuni
începând de la versiunea 3.22, MySQL vă permite să stocaţi parametri de conexiune într-un fişier cu opţiuni.
Apoi, nu mai trebuie să tastaţi parametri de fiecare dată când rulaţi mysql; aceştia sunt folosiţi ca şi cum i-aţi fi
introdus în linia de comandă. Parametrii sunt de asemenea folosiţi şi de către alte programe client MySQL,
precum mysqlimport. Aceasta înseamnă că un fişier cu opţiuni reduce cantitatea de text introdus atunci când
folosiţi şi aceste programe.
Pentru a folosi fişierul cu opţiuni în vederea specificării parametrilor de conexiune, creaţi un fişier denumit
-/.my.cnf (adică un fişier denumit .my.cnf în catalogul dumneavoastră de bază). Un fişier cu opţiuni este un fişier
cu text simplu, deci îl puteţi crea folosind orice editor de texte. Conţinutul fişierului se prezintă astfel:
[client]
host=gaz(/a_serverului
user=numele_dumneavoastră
password=parola_dumneavoastră
Linia [client] indică începutul grupului cu opţiuni pentru programul client; toate liniile care urmează după
aceasta sunt linii citite de către programele client MySQL pentru a obţine valorile opţiunilor, până la sfârşitul
fişierului sau până la începutul unui grup de parametri diferit, înlocuiţi gazda_serverului, numele_dumneavoastra
si parola_dum-neavoastra cu numele gazdei, numele de utilizator şi parola pe care le specificaţi când vă conectaţi
la server. Pentru mine, fişierul . my. cnf se prezintă astfel
[client]
host=pit-viper.snake.net
user=paul
password=secret
92 Partea l Utilizarea generală a sistemului MySQL
Singura linie obligatorie este [client]. Liniile care definesc valorile parametrilor sunt opţionale; le puteţi specifica
numai pe cele dorite. De exemplu, dacă numele dumneavoastră de utilizator MySQL este acelaşi cu numele
UNIX de deschidere a sesiunii de lucru, atunci nu trebuie să includeţi o linie user.
După ce aţi creat fişierul .my .cnf, stabiliţi pentru modul său de acces o valoare restrictivă, pentru a vă asigura că
nimeni altcineva nu poate să-1 citească:
% chmod 600 .ray,cnf
Sub Windows, conţinutul fişierului cu opţiuni este acelaşi, deşi numele este diferit (C: \my.cnf), iar
dumneavoastră nu invocaţi comanda chmod.
Deoarece fişierele cu opţiuni au fost adăugate la MySQL decât începând cu versiunea 3.22, nu le puteţi folosi cu
versiunile anterioare, în particular, nu puteţi folosi un fişier cu opţiuni sub Windows la clienţii incluşi în
distribuţia shareware a sistemului MySQL, deoarece aceea se bazează pe MySQL 3.21. Fişierele cu opţiuni sunt
compatibile cu versiunea Windows înregistrată a sistemului MySQL, sau vă puteţi procura programe client mai
recente, compatibile cu fişierele cu opţiuni, din situl Web MySQL.
Despre fişierele cu opţiuni se pot obţine mai multe informaţii în Anexa E, „Referinţă de program MySQL".
Utilizarea istoricului de comenzi al interpretorului
Interpretoare precum csh, tcsh si bash reţin comenzile dumneavoastră într-o listă istoric şi vă permit sa repetaţi
comenzi din lista respectivă. Dacă folosiţi un asemenea interpreter, lista dumneavoastră istoric vă poate ajuta să
evitaţi tastarea unor întregi comenzi. De exemplu, daca aţi invocat programul mysql recent, îl puteţi reexecuta
după cum urmează:
% l my
Caracterul ! indică interpretorului să caute în istoricul de comenzi pentru a găsi cea mai recentă comandă care
începe cu my şi să o re-execute ca şi cum dumneavoastră aţi fi rein-trodus-o de la tastatură. De asemenea, unele
interpretoare vă permit să vă deplasaţi în sus si în jos în lista istoric folosind tastele cu săgeţi orientate în sus,
respectiv în jos (sau poate combinaţiile de taste Ctrl-P şi Ctrl-N). Puteţi selecta în acest mod comanda dorită şi
apoi apăsaţi pe Enter pentru a o executa, tcsh şi bash au această caracteristică, posibil existentă şi la alte
interpretoare. Examinaţi documentaţia interpretorului dumneavoastră pentru a afla mai multe detalii despre
utilizarea listei istoric.
Utilizarea aliasurilor şi a scripturilor de interpreter
Dacă interpretorul dumneavoastră permite atribuirea unui alias, puteţi configura scurtături de comenzi care vă
permit să invocaţi o comandă lungă prin tastarea unui nume scurt. De exemplu, în csh sau tcsh, puteţi folosi
comanda alias pentru a configura un alias denumit samp_db, cum este acesta:
alias samp_db 'mysql -h pit-viper.snake.net -u -paul -p samp_db' Sintaxa pentru bash este uşor diferită:
alias samp_db='mysql -h pit-viper.snake.net -u -paul -p samp_db' Definirea unui alias face aceste două
comenzi echivalente:
samp_db
mysql -h pit-viper.snake.net -u -paul -p samp_db
Capitolul 1 Introducere în MySQL şi SQL 93
Evident, prima comandă este mai uşor de introdus decât a doua. Pentru ca aliasul să aibă efect de fiecare dată
când deschideţi sesiunea de lucru, plasaţi comanda alias într-unul din fişierele de pornire ale interpretorului
dumneavoastră (de exemplu .cshrc pentru csh sau .bash_profile pentru bash).
Un alt tip de scurtătură este crearea unui script de interpreter care execută mysql automat, cu opţiunile adecvate,
în UNIX, un fişier script echivalent cu aliasul samp_db prezentat anterior arată astfel:
#! /bin/sh
exec mysql -h pit-viper.snake.net -u -paul -p samp_db
Dacă denumesc scriptul samp_db şi îl transform într-un executabil (folosind comanda chmod +x samp_db), pot
tasta samp_db pentru a rula mysql şi pentru a mă conecta la baza mea de date.
Sub Windows, în aceleaşi scopuri se poate folosi un fişier batch, având extensia .bat si conţinând comenzi
executabile fără intervenţia utilizatorului. Denumiţi fişierul samp_db.bat şi inseraţi în fişier o linie ca aceasta:
mysql -h pit-viper.snake.net -u -paul -p samp_db
Acest fişier batch poate fi rulat fie tastând samp_db la promptul dintr-o consolă DOS, fie executând dublu clic pe
pictograma sa din Windows.
Dacă obţineţi acces la mai multe baze de date sau vă conectaţi la mai multe gazde, puteţi defini mai multe
aliasuri sau scripturi, fiecare din acestea invocând mysql cu opţiuni diferite.
Emiterea de interogări cu o cantitate mai mică de text introdus de la tastatură
mysql este un program extrem de util pentru interacţiunea cu baza dumneavoastră de date, dar interfaţa sa este
ideală pentru interogări scurte, de un rând. mysql nu este interesat dacă o interogare se extinde pe mai multe
rânduri, desigur, dar nu este prea amuzant să tastezi interogări lungi. Introducerea unei interogări, chiar şi a uneia
scurte, nu este nici ea prea distractivă, dacă descoperi că trebuie să o tastezi din nou datorită unei erori de
sintaxă.
Există numeroase tehnici pe care le puteţi utiliza pentru evitarea unor introduceri şi reintroduceri inutile de text:
• Folosiţi caracteristica de editare a liniei de intrare din mysql.
• Utilizaţi copierea şi lipirea.
• Rulaţi mysql în mod batch.
• Evitaţi să introduceţi instrucţiuni INSERT, prin utilizarea datelor existente pentru crearea de noi înregistrări.
Utilizarea editorului liniilor de intrare din mysql
în mysql este încorporată biblioteca GNU Readline, care permite editarea liniilor de intrare. Puteţi manipula linia
pe care o introduceţi la un moment dat sau puteţi relua liniile introduse anterior şi le puteţi re-introduce, fie ca
atare, fie după o modificare ulterioară. Acest lucru este convenabil când introduceţi o linie si observaţi o greşeală
de
94
Partea l Utilizarea generală a sistemului MySQL
introducere a textului; puteţi reveni în interiorul liniei pentru a corecta problema înainte de a apăsa pe tasta Enter.
Dacă introduceţi o interogare care conţine o greşeală, puteţi relua interogarea şi o puteţi edita pentru a remedia
problema, după care o retrimiteţi. (Acest lucru este cel mai uşor de făcut dacă tastaţi întreaga interogare pe o
singură linie.)
Unele dintre secvenţele de editare pe care le veţi găsi utile sunt prezentate în tabelul 1.4, dar există multe
comenzi de editare a liniei de intrare în afara celor prezentate în tabel. Puteţi găsi o versiune electronică a
manualului Readline prin consultarea motorului dumneavoastră preferat de căutare în Internet. Manualul este de
asemenea inclus în distribuţia Readline, disponibilă în situl Web al proiectului GNU, la adresa http: /
/www.gnu .org/.
Tabelul 1.4 Comenzi mysql de editare a liniei de intrare
Secvenţă de taste
Săgeată sus, Ctrl-P
Săgeată jos, Ctrl-N
Săgeată stânga, Ctrl-B
Săgeată dreapta, Ctrt-F
Esc Ctrl-B
Esc Ctrl-F
Ctrl-A
Ctrl-E
Ctrl-D
Delete
Esc D
Esc Backspace
Ctrt-K
Ctrl-
Semnificaţie
Reluarea liniei anterioare Reluarea liniei ulterioare Deplasarea cursorului la stânga (înapoi) Deplasarea
cursorului la dreapta (înainte) Deplasare înapoi cu un cuvânt Deplasare înainte cu un cuvânt Deplasarea
cursorului la începutul liniei Deplasarea cursorului la sfârşitul liniei Ştergerea caracterului aflat sub cursor
Ştergerea caracterului din stânga cursorului Ştergerea cuvântului
Ştergerea cuvântului din stânga cursorului Şterge tot, de la cursor la sfârşitul liniei Anulează ultima modificare;
se poate repeta
Exemplul următor descrie o utilizare simplă pentru editarea liniei de intrare. Să presupunem că aţi introdus
această interogare în timp ce utilizaţi mysql:
mysql> SHOW COLUMNS FROM persedinte;
Dacă observaţi că în loc de „preşedinte" aţi scris „perşedinte", înainte de a apăsa pe tasta Enter, apăsaţi tasta
săgeată stânga sau combinaţia de taste Ctrl-B de câteva ori pentru a deplasa cursorul la stânga, până când ajunge
la litera s. Apoi apăsaţi de două ori pe tasta Delete pentru a şterge caracterele e r, tastaţi re pentru a remedia
eroarea şi apăsaţi pe tasta Enter pentru a emite interogarea. Dacă apăsaţi pe tasta Enter înainte de a sesiza
eroarea, nu este nici o problemă. După ce mysql îşi afişează mesajul de eroare, apăsaţi pe tasta săgeată sus sau pe
combinaţia de taste Ctrl-P pentru a relua linia, după care o editaţi aşa cum am arătat mai sus.
Editarea liniilor de intrare nu este disponibilă în versiunea Windows a programului mysql, dar vă puteţi procura
distribuţia client gratuită cygwin_32 din situl Web MySQL. Programul mysqlc din acea distribuţie este similar
cu mysql, dar înţelege comenzile de editare a liniei de intrare.
Capitolul 1 Introducere în MySQL şi SQL
95
Utilizarea copierii şi a lipirii pentru emiterea de interogări
Dacă lucraţi într-un mediu cu ferestre, puteţi salva textul interogărilor care vi se par utile într-un fişier şi puteţi
folosi operaţiile de copiere şi lipire pentru a emite cu uşurinţă aceste interogări. Pur si simplu respectaţi
următoarea procedură:
1. Invocaţi mysql într-o fereastră Telnet sau într-o fereastră de consolă DOS.
2. Deschideţi fişierul care conţine interogările dumneavoastră într-o fereastră document. (De exemplu, eu
folosesc BBEdit sub Mac OS, respectiv vi într-o fereastră xterm sub X Window System în UNIX.)
3. Pentru a executa o interogare stocată în fişierul dumneavoastră, selectaţi-o şi copiaţi-o. Apoi, comutaţi în
fereastra dumneavoastră Telnet sau în consola DOS şi lipiţi interogarea în MySQL.
Procedura pare cam stângace descrisă pe hârtie, dar este o modalitate rapidă de a introduce interogări rapid şi
fără a mai scrie nimic atunci când o folosiţi efectiv.
De asemenea, această tehnică vă permite să vă editaţi interogările în fereastra document, precum şi să construiţi
interogări noi prin copierea si lipirea componentelor interogărilor existente. De exemplu, dacă selectaţi frecvent
rânduri dintr-un anumit tabel, dar doriţi să vedeţi datele de ieşire sortate în diferite moduri, puteţi folosi o listă cu
diferite clauze ORDER BY în fereastra dumneavoastră document, după care o copiaţi şi o lipiţi pe aceea pe care
doriţi să o utilizaţi pentru orice interogare specifică.
Puteţi folosi copierea şi lipirea şi în cealaltă direcţie (de la Telnet către fişierul dumneavoastră cu interogări).
Când introduceţi linii în mysql, acestea sunt salvate într-un fişier denumit .mysqljiistory din catalogul
dumneavoastră de bază. Dacă introduceţi manual o interogare pe care doriţi să o salvaţi pentru a o consulta
ulterior, părăsiţi programul mysql, deschideţi . mysql_history într-un editor, apoi copiaţi si lipiţi interogarea din
fişierul .mysql_history în fişierul care conţine interogarea.
Rularea programului mysql în mod batch
Nu este necesară rularea interactivă a programului mysql; acest program poate citi un fişier în mod non-
interactiv (batch). Acest mod este util pentru interogări pe care le rulaţi periodic, deoarece nu doriţi, evident, să
reintroduceţi de la tastatură o asemenea interogare de fiecare dată când o rulaţi. Este mai uşor să inseraţi o dată
interogarea într-un fişier, după care să cereţi programului mysql să execute conţinutul fişierului conform
necesităţilor.
Să presupunem că averi o interogare care găseşte pe membrii Ligii istorice interesaţi de o anumită perioadă din
istoria Statelor Unite, căutând în coloana interese a tabelului membru. De exemplu, pentru a-i găsi pe membrii
interesaţi în perioada de criză a anilor '30, interogarea trebuie scrisă astfel (reţineţi caracterul punct si virgulă de
la sfârşitul instrucţiunii, pentru ca mysql să poată sesiza locul unde se încheie aceasta):
SELECT nume, prenume, email, interese FROM membru
WHERE interese LIKE "%criza%"
ORDER BY nume, prenume;
Plasaţi interogarea într-un fişier denumit interese. sql, apoi rulaţi interogarea furnizând fişierul programului
mysql, astfel:
% mysql samp_db < interese.sql
96 Partea l Utilizarea generală a sistemului MySQL
în mod prestabilit, mysql generează date de ieşire într-un format delimitat prin tabula-tori atunci când este rulat
în mod grup. Dacă doriţi să obţineţi acelaşi format tabular atunci când rulaţi mysql în mod interactiv, folosiţi
opţiunea -t:
% mysql -t samp_db < interese.sql Dacă doriţi să salvaţi datele de ieşire, redirecţionaţi-le spre un fişier:
% mysql -t samp_db < interese.sql > fisier_iesire
Pentru a folosi interogarea în scopul de a găsi membri interesaţi de personalitatea lui Thomas Jefferson, puteţi
edita fişierul cu interogarea pentru a înlocui criza cu Jefferson, după care rulaţi mysql din nou. Această metodă
funcţionează atâta timp cât nu folosiţi interogarea foarte frecvent. Dacă o folosiţi foarte frecvent, atunci este
necesară o altă metodă. O modalitate de a spori flexibilitatea interogării este de a o insera într-un script de
interpreter care preia un argument din linia de comandă a scriptului şi o foloseşte pentru a modifica textul
interogării. Astfel, interogarea devine parametrizată şi vă permite să specificaţi cuvântul cheie care arată
domeniul de interes atunci când rulaţi scriptul. Pentru a vedea modul de funcţionare al acestei metode, scrieţi un
mic script de interpreter, denumit interese.sh:
#1 /bin/sh
if [ $# -ne 1 J; then echo "Rog specificaţi un cuvânt cheie"; exit; fi
mysql -t samp_db «QUERY_INPUT
SELECT nume, prenume, email, interese FROM membru
WHERE interese LIKE "%$1%"
ORDER BY nume, prenume;
QUERY_INPUT
Cea de-a doua linie se asigură că există un cuvânt cheie în linia de comandă; afişează un scurt mesaj şi încheie
execuţia programului în caz contrar, întreg textul cuprins între «QUERY_INPUT şi linia QUERY_INPUT finală
se transformă în date de intrare pentru mysql. în cadrul textului interogării, interpretorul înlocuieşte referinţa la
$1 cu cuvântul cheie din linia de comandă. (In scripturile de interpreter, $1, $2... se referă la argumentele
comenzii.) Acest fapt determină interogarea să reflecte cuvântul cheie pe care îl specificaţi în linia de comandă
când rulaţi scriptul.
înainte de a putea rula scriptul, trebuie să-1 faceţi executabil:
% chmod +x interese.sh
Acum, nu mai trebuie să editaţi scriptul de fiecare dată când îl rulaţi. Pur si simplu indicaţi direct în linia de
comandă ce anume căutaţi:
% interese.sh criza
% interese.sh Jefferson
Crearea de noi înregistrări folosind date existente
Se pot adăuga noi înregistrări într-un tabel, rând cu rând, folosind instrucţiunea INSERT dar, după crearea
câtorva înregistrări prin tastarea manuală a instrucţiunilor INSERT, majoritatea dintre noi suntem convinşi că
trebuie să existe şi o metodă mai bună de a executa această operaţie. O alternativă este utilizarea unui fişier care
conţine numai valori de date si apoi încărcarea de înregistrări din acel fişier folosind instrucţiunea LOAD DATA
sau utilitarul my sql import.
Capitolul 1 Introducere în MySQL şi SQL
97
Deseori, creaţi fişierul de date folosind date care deja există într-un alt format. Informaţiile dumneavoastră pot fi
conţinută într-un program de calcul tabelar sau poate că se află într-o altă bază de date şi doriţi să le transferaţi
către MySQL. Pentru a nu complica această discuţie, voi presupune că datele dumneavoastră se află într-o foaie
de calcul tabelar din calculatorul dumneavoastră de birou.
Pentru a transfera datele din foaia de calcul din calculatorul dumneavoastră într-un fişier din contul
dumneavoastră UNIX, puteţi folosi tehnica de copiere şi lipire, alături de Telnet. Iată cum procedaţi:
1. Deschideţi o conexiune Telnet spre contul dumneavoastră UNIX. Sub Mac OS, puteţi folosi o aplicaţie
precum BetterTelnet sau NCSA Telnet. Sub Windows, puteţi folosi programul Telnet standard.
2. Deschideţi foaia de calcul tabelar, selectaţi blocul de date pe care doriţi să-1 transferaţi şi copiaţi-1.
3. în fereastra Telnet, tastaţi următoarea comandă pentru a începe capturarea datelor în fisieruldata.txt:
% cat > data.txt
Comanda cat aşteaptă datele de intrare.
4. Lipiţi datele pe care le-aţi copiat din foaia de calcul în fereastra Telnet. Programul cat crede că introduceţi
personal informaţiile si le scrie îndatoritor în fişierul data.txt.
5. După ce toate datele lipite au fost scrise în fişier, apăsaţi pe tasta Enter în cazul în care cursorul ajunge la
sfârşitul unei linii de date, nu la începutul unei linii noi. Apoi, apăsaţi pe combinaţia de taste Ctrl-D pentru a
indica „sfârşitul fişierului". Programul cat încetează să mai aştepte date de intrare şi închide fişierul.
în acest moment, aveţi un fişier data.txt care conţine blocul de date pe care 1-aţi selectat în foaia dumneavoastră
de calcul tabelar şi care este gata de a fi încărcat în baza dumneavoastră de date cu LOAD DATA sau
mysqlimport.
Copierea şi lipirea reprezintă o metodă rapidă si simplă de a transfera date într-un fişier UNIX, dar este mai
adecvată pentru seturi mai mici de date. Cantităţile mai mari de date pot depăşi limitele bufferului de copiere al
sistemului dumneavoastră, în asemenea cazuri, se recomandă salvarea foii de calcul tabelar în format text simplu
(delimitat prin tabulator!). Apoi, puteţi transfera fişierul din calculatorul dumneavoastră în contul UNIX folosind
FTP. Transferaţi fişierul în mod text (nu în mod binar sau imagine), astfel încât caracterele sfârşit de rând să fie
convertite în caractere UNIX de sfârşit de rând. (UNIX foloseşte caractere salt la linie nouă, Mac OS foloseşte
caractere retur de car, iar Windows foloseşte perechea de caractere retur de car-salt la linie nouă.) Puteţi indica
instrucţiunii LOAD DATA sau programului mysqlimport tipul caracterului sfârşit de rând pe care să-1 aştepte,
dar în UNIX este mai uşor de lucrat cu fişierul dacă acesta conţine caractere de salt la linie nouă.
După transferul fişierului, este bine să verificaţi dacă are sau nu rânduri vide la sfârşit. Dacă are, trebuie să le
ştergeţi, deoarece altfel se vor transforma în înregistrări vide sau deformate atunci când încărcaţi fişierul în baza
de date.
98 Partea l Utilizarea generală a sistemului MySQL
în fişierele salvate în format text simplu dintr-o foaie de calcul tabelar, valorile care conţin spaţii pot fi delimitate
prin ghilimele. Pentru a elimina ghilimelele atunci când încărcaţi fişierul în baza dumneavoastră de date, folosiţi
clauza FIELDS ENCLOSED BY pentru instrucţiunea LOAD DATA, respectiv opţiunea --fields-enclosed-by
pentru mysqlimport. Vezi informaţiile despre instrucţiunea LOAD DATA din Anexa D pentru a obţine mai
multe detalii.
De aici, încotro?
Acum ştiţi destule despre MySQL. Puteţi configura o bază de date si crea tabele. Puteţi insera înregistrări în
aceste tabele, le puteţi regăsi în diverse moduri, le puteţi modifica sau şterge. Dar mai aveţi încă multe de învăţat
despre MySQL; manualul din acest capitol a reprezentat numai o mică introducere. Puteţi vedea acest lucru
examinând starea bazei noastre de date exemplu. Am creat baza de date şi tabelele sale şi am populat-o cu unele
date iniţiale. Pe parcursul acestui proces am văzut modul de scriere al unora dintre interogările de care avem
nevoie pentru a răspunde la întrebări privind informaţiile din baza de date. Mai sunt, însă, multe de făcut...
De exemplu, nu dispunem de nici o modalitate interactivă convenabilă de a introduce noi înregistrări de punctaje
în cadrul proiectului de evidenţă a rezultatelor şcolare, respectiv înregistrări cu noii membri în catalogul Ligii
istorice. Nu avem nici o metodă convenabilă de a edita înregistrările existente. Şi tot nu putem genera
formularele tipărite sau în format electronic pentru catalogul Ligii. Aceste sarcini, precum şi altele, vor fi
abordate din nou în capitolele următoare, mai ales în capitolele 7, „Interfaţa API pentru Perl DBI", respectiv 8,
„Interfaţa API pentru PHP".
Drumul pe care veţi merge în continuare printre paginile acestei cărţi depinde de domeniul dumneavoastră de
interes. Dacă doriţi să vedeţi modul în care am finalizat proiectele legate de Liga istorică şi de evidenţa
rezultatelor şcolare, Partea a Il-a se referă la programarea în MySQL. Dacă veţi îndeplini funcţia de
administrator MySQL pentru situl dumneavoastră, Partea a IlI-a a volumului de faţă tratează despre sarcinile
administrative. Totuşi, eu vă recomand să acumulaţi mai întâi cunoştinţe suplimentare de ordin general despre
utilizarea sistemului MySQL, citind celelalte capitole din Partea I. Aceste capitole discută despre modul de
manipulare a datelor în MySQL, furnizează informaţii suplimentare despre sintaxă şi despre utilizarea
instrucţiunilor de interogare şi vă prezintă modalităţi de rulare mai rapidă a interogărilor. O bază solidă de
cunoştinţe legate de aceste subiecte vă va fi de folos indiferent de contextul în care folosiţi MySQL: rulaţi
programul mysql, vă scrieţi propriile programe sau îndepliniţi atribuţiile unui adminişr trator de baze de date.
CAPITOLUL 2
Lucrul cu date în MySQL şi SQL
Practic, tot ceea ce faceţi în MySQL implică, într-un fel sau altul, date, deoarece rostul unui sistem de gestiune a
bazelor de date este, prin definiţie, acela de gestiune a datelor. Chiar si o simplă instrucţiune SELECT 1 implică
o evaluare a unei expresii, cu scopul de a produce o valoare a unei date întregi.
Fiecare valoare a unei date din MySQL are un tip. De exemplu, 37.4 este un număr, iar "abc" este un şir. Uneori,
tipurile de date sunt explicite, ca atunci când emiteţi o instrucţiune CREATE TABLE care specifică tipul fiecărei
coloane pe care o declaraţi ca parte a tabelului:
CREATE TABLE tabelul meu
int_col INT, /"
sir_col CHAR(20), /' data col DATE /"
coloana cu valori întregi */ coloana cu valori şir */ coloana cu valori date */
în alte situaţii, tipurile de date sunt implicite, cum este atunci când faceţi referire la valori literale dintr-o
expresie, când transferaţi valori unei funcţii sau când folosiţi valoarea retur-nată din acea funcţie:
INSERT INTO tabeluljneu (int_col,sir_col,data_col)
VALUES(14,CONCAT("a","b"),19990115)
Această instrucţiune INSERT efectuează următoarele operaţii, din care toate implică tipuri de date:
• Atribuie valoarea 14 coloanei întregi1 int_col.
• Transferă valorile şir "a" si "b" funcţiei CONCAT(). Această funcţie returnează valoarea şir "ab", care este
repartizată în coloana şir sir_col.
• Atribuie valoarea întreagă 19990115 coloanei de tip dată data_col. Atribuirea implică o neconcordanţă între
tipuri, deci MySQL converteşte şirul 19990115 în data "1999-01-15"; acest fapt arată că MySQL execută
conversia automată a tipului.
Pentru a folosi MySQL în mod eficient, este esenţial să înţelegeţi modul de manipulare a datelor în MySQL.
Acest capitol descrie tipurile de date pe care le poate manipula MySQL şi discută problemele implicate de lucrul
cu aceste tipuri:
1 Prin coloană întreagă se va înţelege coloană care conţine valori numere întregi. S-a preferat în traducere prima
formă, din motive de concizie. La fel de va proceda cu expresii gen coloană şir şi altele. - N.T.