Primii pași spre Full-stack BAZE DE DATE CURS INTRODUCTIV RAPID Copyright 2022 Editura L&S SOFT Toate drepturile asupra acestei lucrǎri aparţin editurii L&S SOFT. Reproducerea integralǎ sau parţialǎ a textului din aceastǎ carte este posibilǎ doar cu acordul în scris al editurii L&S SOFT. ISBN: 978-606-95233-4-6 Materialul este aprobat MEN prin Ordinul nr. 1561/83. ATENȚIE! După confirmarea plății, fiecare carte poate fi descărcată de maximum 5 ori şi este disponibilă spre descărcare 30 de zile. Fiecare PDF este securizat în 28 de zone cu watermark invizibil (id comandă, e-mail, nume) pentru a nu putea fi distribuit pe alte căi virtuale. Adresa: Aleea Aviației nr. 10, Voluntari, Ilfov Telefon: 0727.731.947 E-mail: office@infobits.ro www.infobits.ro https://ebooks.infobits.ro CUPRINS Capitolul 1. Proiectarea bazelor de date 7 1.1. Noţiuni introductive 8 1.2. Normalizarea datelor 22 1.3. Implementarea modelului conceptual 29 1.4. Managementul de proiect 41 Teme de proiect 45 Capitolul 2. Limbajul MySQL 47 2.1. Ce este şi de ce învăţăm MySQL ? 48 2.2. Cum rulăm MySQL pe propriul calculator ? 49 2.3. Crearea unei baze de date 51 2.4. Tabele. Noţiuni elementare 53 2.5. Tipuri de date în MySQL 55 2.6. Operatori utilizaţi în MySQL 59 2.7. Funcţii MySQL 64 2.8. Afişarea coloanelor care rezultă în urma unui calcul 67 2.9. Valoarea NULL 68 2.10. Valori implicite 70 2.11. Cheie primară şi cheie unică 71 2.12. Autoincrementare 73 2.13. Sortarea datelor 74 2.14. Filtrarea datelor 75 6 Baze de date – curs introductiv 2.15. Actualizări într-un tabel 76 2.16. Funcţii agregate 79 2.17. Utilizarea subinterogărilor 80 2.18. Gruparea datelor 82 2.19. Uniuni de tabele 84 2.20. Elemente care privesc securitatea bazelor de date 87 Probleme Propuse 88 Capitolul 3. Limbajul PHP 92 3.1. Introducere 93 3.2. Formulare 95 3.3. Elementul TEXTAREA 99 3.4. Elementul SELECT 100 3.5. Constante. Variabile. Operatori 101 3.6. Instrucţiunile limbajului PHP 105 3.7. Funcţii în PHP 108 3.8. Funcţii “matematice” 111 3.9. Afişarea datelor - echo şi print 112 3.10. Funcţii pentru prelucrarea şirurilor de caractere 113 3.11. Masive în PHP 116 3.12. Variabile cookie 119 3.13. Utilizarea în comun a limbajelor PHP şi MySQL 121 3.14. Aplicaţii 128 3.15. Proiectarea paginilor web 138 Probleme propuse 140 Teme de proiect 142 Anexa 1. Instalarea pachetului EasyPHP 143 Indicaţii / Răspunsuri 154 CAPITOLUL Proiectarea bazelor de date În acest prim capitol veţi studia principiile generale ale proiectării bazelor de date, vă veţi familiariza cu diversele anomalii care pot invalida bazele de date, precum şi cu modalităţile de evitare ale acestora. De asemenea, se va defini modelul conceptual al unei baze de date. În ultima parte a capitolului, vor fi prezentate etapele realizării unui proiect, avantajele lucrului în echipă şi veţi învăţa cum să pregătiţi şi să susţineţi o prezentare în faţa publicului. Ce este modelul conceptual şi care este rolul său ? Ce este un ERD ? Ce este o entitate şi cum se reprezintă ea într-un ERD ? Ce este o instanţă ? Ce sunt şi cum se stabilesc atributele unei entităţi ? Cum se stabilesc relaţii între entităţi ? Ce tipuri de relaţii pot exista între entităţi ? Cum se reprezintă relaţiile în ERD ? Normalizarea datelor Cum se implementează modelul conceptual ? Managementul de proiect Cuvinte cheie: dată, informaţie, cunoştinţă, analiză, model conceptual, entitate, instanţă, atribut, identificator unic, relaţie, normalizare, mapare, integritate, proiect, echipă, prezentare 8 Baze de date – curs introductiv 1.1. Noţiuni introductive 1.1.1. Date. Informaţii. Cunoştinţe Auzim adesea vorbindu-se despre “era informaţiilor” sau “societatea informaţională” sau “tehnologia informaţiei”, însă de multe ori cuvântul "informaţie" este folosit fără a-i înţelege clar sensul, diferenţa dintre date, informaţii sau cunoştinţe. În general, conţinutul gândirii umane operează cu următoarele concepte: Date – constau în material brut, fapte, simboluri, numere, cuvinte, poze fără un înţeles de sine stătător, neintegrate într-un context, fără relaţii cu alte date sau obiecte. Ele se pot obţine în urma unor experimente, sondaje, etc. Informaţii – prin prelucrarea datelor şi găsirea relaţiilor dintre acestea se obţin informaţii care au un înţeles şi sunt integrate într-un context. Datele, organizate şi prezentate într-un mod sistematic pentru a sublinia sensul acestora, devin informaţii. Pe scurt, informaţiile sunt date prelucrate. Informaţiile se prezintă sub formă de rapoarte, statistici, diagrame, etc. Cunoştinţe – colecţii de date, informaţii, adevăruri şi principii învăţate, acumulate de-a lungul timpului. Informaţiile despre un subiect, reţinute şi înţelese, ce pot fi folosite în luarea de decizii, care pot forma judecăţi şi opinii, devin cunoştinţe. Cu alte cuvinte, cunoştinţele apar în momentul utilizării informaţiei. 1.1.2. Colectarea şi analizarea datelor. Modelul conceptual Primul pas în realizarea unei aplicaţii de baze de date este analiza datelor şi realizarea unei scheme conceptuale (model conceptual) ale acestora. În această etapă sunt analizate natura şi modul de utilizare a datelor. Sunt identificate datele care vor trebui memorate şi procesate, se împart în grupuri logice şi se identifică relaţiile care există între aceste grupuri. Analiza datelor este un proces uneori dificil, care necesită mult timp, însă este o etapă obligatorie. Fără o analiză atentă a datelor şi a modului de utilizare a acestora, vom realiza o bază de date care, putem constata în final, că nu întruneşte cerinţele beneficiarului. Ideea de bază a analizei datelor şi a construirii modelului conceptual este "să măsori de două ori şi să tai o singură dată". Informaţiile necesare realizării modelului conceptual se obţin folosind metode convenţionale precum intervievarea oamenilor din cadrul organizaţiei şi studierea documentelor folosite. Capitolul 1. Proiectarea bazelor de date 9 Odată obţinute aceste informaţii, ele trebuie reprezentate într-o formă convenţională care să poată fi uşor înţeleasă de toată lumea. O astfel de reprezentare este diagrama entităţi-relaţii, numită şi harta relaţiilor sau ERD-ul (Entity Relationship Diagram). Aceste scheme sunt un instrument util care uşurează comunicarea între specialiştii care proiectează bazele de date şi programatori, pe de o parte şi beneficiari, pe de altă parte. Aceştia din urmă pot înţelege cu uşurinţă o astfel de schemă, chiar dacă nu sunt cunoscători în domeniul IT. În cele ce urmează, vom prezenta principalele elemente care intră în componenţa unui ERD precum şi convenţiile de reprezentare a acestora. 1.1.3. Entităţi. Instanţe. Atribute. Identificator unic O entitate este un lucru, obiect, persoană sau eveniment care are semnificaţie pentru afacerea modelată, despre care trebuie să colectăm şi să memorăm date. O entitate poate fi un lucru real, tangibil precum o clădire, o persoană, poate fi o activitate precum o programare sau o operaţie sau poate fi o noţiune abstractă. O entitate este reprezentată în ERD printr-un dreptunghi cu colţurile rotunjite. Numele entităţii este întotdeauna un substantiv la singular şi se scrie în partea de sus a dreptunghiului cu majuscule, ca în Fig. 1.1: PROFESOR PACIENT ELEV Figura 1.1. Exemple de entităţi şi modul de reprezentare O entitate este de fapt o clasă de obiecte şi pentru orice entitate există mai multe instanţe ale sale. O instanţă a unei entităţi este un obiect, o persoană, un eveniment particular din clasa de obiecte care formează entitatea. De exemplu, elevul X din clasa a IX-a A de la Liceul de Informatică din localitatea Y este o instanţă a entităţii ELEV. După cum se vede, pentru a preciza o instanţă a unei entităţi, trebuie să specificăm unele caracteristici ale acestui obiect, să-l descriem (să precizăm de exemplu numele, clasa, şcoala, etc.). Aşadar, după ce am identificat entităţile trebuie să descriem aceste entităţi în termeni reali, adică să le stabilim atributele. Un atribut este orice detaliu care serveşte la identificarea, clasificarea, cuantificarea, sau exprimarea stării unei instanţe a unei entităţi. Atributele sunt informaţii specifice ce trebuie cunoscute şi memorate. De exemplu, atributele entităţii ELEV sunt numele, prenumele, adresa, numărul de telefon, adresa de e-mail, data naşterii, etc. 10 Baze de date – curs introductiv În cadrul unui ERD, atributele se vor scrie imediat sub numele entităţii, cu litere mici. Un atribut este un substantiv la singular (vezi Fig. 1.2). Figura 1.2. Exemplu de entitate ELEV # * * * * ○ ○ cnp nume prenume data_nasterii adresa telefon e-mail Un atribut poate fi obligatoriu sau opţional. Dacă un atribut este obligatoriu, pentru fiecare instanţă a entităţii respective trebuie să avem o valoare pentru acel atribut: de exemplu, este obligatoriu să cunoaştem numele elevilor. Pentru un atribut opţional putem avea instanţe pentru care nu cunoaştem valoarea atributului respectiv. De exemplu, atributul ”e-mail” al entităţii ELEV este opţional, un elev putând să nu aibă adresă de email. Un atribut obligatoriu este precedat în ERD de un asterisc ”*”, iar un atribut opţional va fi precedat de un cerculeţ ”o”. Atributele care definesc în mod unic instanţele unei entităţi se numesc identificatori unici (UID). UID-ul unei entităţi poate fi compus dintr-un singur atribut, precum codul numeric personal ce poate fi un identificator unic pentru entitatea ELEV. În alte situaţii, identificatorul unic este compus dintr-o combinaţie de două sau mai multe atribute. De exemplu, combinaţia dintre titlu, numele autorului şi data apariţiei poate forma unicul identificator al entităţii CARTE. Oare combinaţia titlu şi nume autor nu era suficientă? Răspunsul este NU, deoarece pot exista mai multe volume scrise de Mihai Eminescu având toate titlul ”Poezii”, dar apărute la date diferite. Atributele care fac parte din identificatorul unic al unei entităţi vor fi precedate de semnul diez ”#” (vezi Fig. 1.2 şi Fig. 1.3). Figura 1.3. Alt exemplu de entitate CARTE # # # * * titlu autor data_aparitiei format numar_pagini Observaţie ! Atributele din UID sunt întotdeauna obligatorii, însă semnul ”#” este suficient, nu mai trebuie pus şi un semn asterisc în faţa acestor atribute. Valorile unor atribute se pot modifica foarte des, ca de exemplu atributul vârstă. Spunem, în acest caz, că avem de a face cu un atribut volatil. Dacă valoarea unui atribut însă se modifică foarte rar sau deloc (de exemplu data naşterii) acesta este un atribut non-volatil. Evident este de preferat să folosim atribute non-volatile atunci când acest lucru este posibil. Capitolul 1. Proiectarea bazelor de date 11 Identificaţi entităţile pentru următoarele scenarii. Identificaţi apoi pentru fiecare entitate atributele sale, stabiliţi opţionalitatea acestora şi precizaţi unicul identificator al fiecărei entităţi. Indicaţie. Subliniaţi substantivele care au semnificaţie pentru afacerea descrisă. Un substantiv va fi subliniat doar la prima sa apariţie. Dintre aceste substantive veţi alege apoi entităţile. 1. Pentru a se abona la diverse reviste, persoanele doritoare trebuie să furnizeze numele, adresa şi un număr de telefon. Fiecare revistă este identificată prin titlu, numărul volumului şi data apariţiei. Abonaţii semnează pentru abonare un contract pe o anumită perioadă de timp specificată prin data de început a abonamentului şi data finală. Bineînţeles că o persoană se poate abona la mai multe reviste în acelaşi timp. 2. Despre angajaţii unei firme se cunoaşte numele, titlul, numărul de telefon de la birou. Angajaţii pot fi implicaţi într-o serie de proiecte ce se desfăşoară în cadrul firmei. Despre fiecare proiect se cunoaşte numele, data la care a demarat proiectul şi se poate cunoaşte o dată la care se va finaliza proiectul. La fiecare proiect lucrează un singur angajat, însă un angajat poate fi implicat în mai multe proiecte. Fiecare angajat are un manager, cu excepţia directorului. Managerii pot fi şi ei implicaţi în proiecte. 1.1.4. Relaţii între entităţi În lumea reală, obiectele nu există izolat. Percepem obiectele din lumea reală doar în conexiune cu alte obiecte, de exemplu vom spune 'pământul se învârte în jurul soarelui', 'el este medic', etc. Aşadar, după ce aţi identificat care sunt entităţile şi atributele acestor entităţi, este timpul să punem în evidenţă relaţiile care există între aceste entităţi, modul în care acestea comunică între ele. O relaţie este o asociere, legătură sau conexiune existentă între entităţi şi care are o semnificaţie pentru afacerea modelată. Orice relaţie este bidirecţională, legând două entităţi sau o entitate cu ea însăşi. De exemplu, elevii studiază mai multe materii, o materie e studiată de către elevi. Orice relaţie este caracterizată de următoarele elemente: - numele relaţiei; - opţionalitatea relaţiei; - gradul (cardinalitatea) relaţiei. 12 Baze de date – curs introductiv Să luăm ca exemplu relaţia existentă între entităţile JUCĂTOR şi ECHIPĂ. Vom spune: Un JUCĂTOR joacă într-o ECHIPĂ. Numele relaţiei este: joacă. Pentru a stabili opţionalitatea relaţiei trebuie să răspundem la următoarele întrebări: un jucător trebuie să joace într-o echipă? Se poate ca un jucător să nu joace în nicio echipă? Dacă acceptăm faptul că toţi jucătorii trebuie să joace într-o echipă, relaţia este obligatorie sau mandatorie şi vom spune că: Un JUCĂTOR trebuie să joace într-o ECHIPĂ. Dacă însă acceptăm că există jucători care nu joacă în nicio echipă (de exemplu, li s-a terminat contractul şi în momentul de faţă nu mai joacă la nicio echipă), atunci relaţia este opţională. În acest caz, vom spune că: Un JUCĂTOR poate juca la o ECHIPĂ. Cardinalitatea relaţiei este dată de numărul de instanţe ale entităţii din partea dreaptă a relaţiei care pot intra în relaţie cu o instanţă a entităţii din partea stângă a relaţiei. Adică va trebui să răspundem la întrebări de genul: la câte echipe poate juca un jucător? Răspunsurile posibile sunt unul şi numai unul sau unul sau mai mulţi. Vom spune: Un JUCĂTOR trebuie/poate să joace la o ECHIPĂ şi numai una. sau Un JUCĂTOR trebuie/poate să joace la una sau mai multe ECHIPE. Cea mai realistă variantă a relaţiei dintre JUCĂTOR şi ECHIPĂ este aşadar: Un JUCĂTOR poate să joace la o ECHIPĂ şi numai una. Am precizat însă mai înainte că orice relaţie este bidirecţională. Relaţia dintre ECHIPĂ şi JUCĂTOR o putem enunţa astfel: La o ECHIPĂ trebuie să joace unul sau mai mulţi JUCĂTORI. 1.1.4.1. Convenţii de reprezentare a relaţiilor În cadrul diagramei entităţi-relaţii, o relaţie va fi reprezentată printr-o linie ce uneşte cele două entităţi. Deoarece o relaţie este bidirecţională, linia ce uneşte cele două entităţi este compusă din două segmente distincte, câte unul pentru fiecare entitate. Tipul segmentului ce pleacă de la o entitate ne va indica opţionalitatea relaţiei dintre această entitate şi entitatea aflată în cealaltă parte a relaţiei. Dacă acest segment este continuu este vorba de o relaţie obligatorie, o linie întreruptă indică o relaţie opţională. Capitolul 1. Proiectarea bazelor de date 13 De exemplu, în Fig. 1.4 segmentul ce pleacă de la entitatea JUCĂTOR, fiind întrerupt, înseamnă că un jucător poate juca la o echipă, adică relaţia este opţională. Segmentul ce pleacă dinspre entitatea ECHIPĂ este continuu, deci la o echipă trebuie să joace jucători. Figura 1.4. Reprezentarea relaţiilor Modul în care o linie se termină spre o entitate este important. Dacă se termină printr-o linie simplă, înseamnă că o instanţă şi numai una a acestei entităţi este în relaţie cu o instanţă a celeilalte entităţi. În exemplul anterior, linia de la JUCATOR la ECHIPA se termină în partea dinspre ECHIPA cu o linie simplă, deci un jucător joacă la o echipă şi numai una. Dacă linia se termină cu trei linii (picior de cioară), înseamnă că mai multe instanţe ale entităţii pot corespunde unei instanţe a celeilalte entităţi. În exemplul anterior, linia de la ECHIPĂ la JUCĂTOR se termină cu piciorul de cioară, înseamnă că unei instanţe a entităţii ECHIPĂ îi corespund mai multe instanţe ale entităţii JUCĂTOR, adică o echipă are unul sau mai mulţi jucători. Caracteristica relaţiei Valoare Mod de reprezentare Numele relaţiei un verb se scrie deasupra relaţiei linie continuă Opţionalitatea relaţie obligatorie (TREBUIE) relaţie opţională (POATE) una şi numai una Cardinalitatea linie întreruptă linie simplă picior de cioară una sau mai multe Tabelul 1.1. Moduri de reprezentare a unei relaţii 1.1.4.2. Tipuri de relaţii Variantele de relaţii ce pot exista între două entităţi sunt prezentate mai jos: - relaţii one-to-one – acest tip de relaţie este destul de rar întâlnit – uneori, astfel de relaţii pot fi modelate transformând una dintre entităţi în atribut al celeilalte entităţi. - relaţii one-to-many – sunt cele mai întâlnite tipuri de relaţii, însă şi aici, cazurile c) şi d) prezentate în Fig. 1.6, sunt mai puţin uzuale; 14 Baze de date – curs introductiv Figura 1.5. Exemple de relaţii one-to-one Figura 1.6. Exemple de relaţii one-to-many - relaţii many-to-many – aceste tipuri de relaţii apar în prima fază a proiectării bazei de date, însă ele trebuie să fie ulterior eliminate. Fig. 1.7 prezintă câteva exemple de astfel de relaţii. Figura 1.7. Exemple de relaţii many-to-many Capitolul 1. Proiectarea bazelor de date 15 1.1.5. Rezolvarea relaţiilor many-to-many După cum am precizat anterior, relaţiile many-to-many pot apărea într-o primă fază a proiectării bazei de date, însă ele nu au voie să apară în schema finală. Să considerăm relaţia dintre entităţile STUDENT şi CURS (vezi Fig. 1.8). Se ştie că orice curs se termină în general cu un examen. Unde vom memora nota studentului la fiecare examen? Figura 1.8. Exemplu de relaţie many-to-many Dacă încercăm să introducem atributul nota la entitatea STUDENT, nu vom şti cărei materii îi corespunde acea notă, întrucât unei instanţe a entităţii STUDENT îi corespund mai multe instanţe ale entităţii CURS. Invers, dacă încercăm să memorăm nota în cadrul entităţii CURS, nu vom şti cărui student îi aparţine acea notă. Rezolvarea unei relaţii many-to-many constă introducerea unei noi entităţi numită entitate de intersecţie, pe care o legăm de entităţile originale prin câte o relaţie one-to-many. Paşii în rezolvarea unei relaţii many-to-many sunt următorii: se găseşte entitatea de intersecţie - pentru exemplul nostru, vom introduce entitatea INSCRIERE: Figura 1.9. Rezolvarea relaţiilor many-to-many, pasul 1 16 Baze de date – curs introductiv crearea noilor relaţii: o opţionalitatea: relaţiile care pleacă din entitatea de intersecţie sunt întotdeauna obligatorii în această parte. În partea dinspre entităţile originale, relaţiile vor păstra opţionalitatea relaţiilor iniţiale. o cardinalitatea: ambele relaţii sunt de tip one-to-many, iar partea cu many va fi întotdeauna înspre entitatea de intersecţie. o numele noilor relaţii: Figura 1.10. Rezolvarea relaţiilor many-to-many, pasul 2 adăugarea de atribute în cadrul entităţii de intersecţie, dacă acestea există. În exemplul nostru ne poate interesa, să spunem, data la care s-a înscris un student la un curs, data la care a finalizat cursul, precum şi nota obţinută la sfârşitul cursului. Figura 1.11. Rezolvarea relaţiilor many-to-many, pasul 3 Capitolul 1. Proiectarea bazelor de date 17 stabilirea identificatorului unic pentru entitatea de intersecţie: dacă entitatea de intersecţie nu are un identificator unic propriu, atunci acesta se poate forma din identificatorii unici ai entităţilor iniţiale, la care putem adăuga atribute ale entităţii de intersecţie. În exemplul nostru, identificatorul unic al entităţii de intersecţie este format din id-ul studentului, id-ul cursului şi data înscrierii la curs. Faptul că identificatorul unic al unei entităţi preia identificatorul unic din altă entitate cu care este legată este reprezentat grafic prin bararea relaţiei respective, înspre entitatea care preia UID-ul celeilalte entităţi: Figura 1.12. Rezolvarea relaţiilor many-to-many, pasul 4 1. O bază de date va memora orarul unei universităţi. Fiecare curs este parte a unui modul, iar fiecărui curs îi este asociat exact un profesor. La fiecare curs participă mai mulţi studenţi. Fiecare poziţie din orar corespunde unei zile a săptămânii şi unei anumite ore. Fiecare poziţie din orar durează exact o oră, dar uneori un curs poate dura mai multe ore consecutive, însă nici un curs nu poate apărea în zile diferite sau la ore diferite neconsecutive ale aceleiaşi zile. Fiecare profesor şi fiecare student pot avea mai multe ore de curs la care participă în decursul unei săptămâni. 18 Baze de date – curs introductiv Care dintre următoarele variante NU este o soluţie posibilă a acestei probleme ? a) Se stabileşte o relaţie one-to-many între CURS şi POZITIE_ORAR; b) Se stabileşte o relaţie many-to-many între CURS şi POZITIE_ORAR; c) Pentru fiecare curs vom avea un atribut start care reţine ora de începere a cursului şi un atribut durată care memorează numărul de poziţii consecutive din orar "ocupate" de acel curs; d) Pentru fiecare curs vom avea două atribute primul şi ultimul care memorează prima şi respectiv ultima poziţie din orar ocupată de acel curs. 2. Fie următoarea hartă a relaţiilor: Figura 1.13. Relaţia propusă pentru problema 2 Cum se citeşte corect relaţia dintre CLIENT şi MAŞINA? a) Fiecare CLIENT poate să închirieze o MAŞINĂ şi numai una. b) Fiecare CLIENT trebuie să închirieze o MAŞINĂ şi numai una. c) Fiecare CLIENT poate să închirieze una sau mai multe MAŞINI. d) Fiecare CLIENT trebuie să închirieze una sau mai multe MAŞINI. 3. Numele unei entitǎţi este de obicei: a) un verb; b) un substantiv ; c) un adverb; d) orice cuvânt. 4. Care dintre următoarele variante NU poate reprezenta un atribut al entităţii PANTOF? a) culoare; b) mărime; c) model; d) clasa. 5. Care dintre următoarele fraze pot fi citite din schema de mai jos? Figura 1.14. Relaţia propusă pentru problema 5 Capitolul 1. Proiectarea bazelor de date a) b) c) d) 19 Un student poate să urmeze mai multe cursuri. Un curs poate fi urmat de mai mulţi studenţi. Un student trebuie să urmeze un singur curs. Un curs trebuie să fie urmat de un student. 6. Ce semnificaţie are piciorul de cioară ( ) în cadrul unui ERD? a) relaţia este obligatorie; b) relaţia este opţională; c) pot exista una sau mai multe instanţe ale entităţii lângă care apare semnul în relaţie cu o instanţă a celeilalte entităţi; d) niciuna din variantele anterioare. 1. Completaţi în tabelul următor, în prima coloană, câte un exemplu de entitate a cărui atribut este specificat în coloana a doua: Entitate Atribut culoare nr_calorii volum Tabelul 1.2. 2. Citiţi, în ambele sensuri, următoarele relaţii. Din ce atribute este compus UID-ul fiecărei entităţi? Figura 1.15. Relaţiile propuse pentru problema 2 20 Baze de date – curs introductiv 3. Rezolvaţi următoarele relaţii many-to-many: Figura 1.16. Relaţiile propuse pentru problema 3 4. Daţi două exemple de relaţii one-to-many. (vezi baremele de notare şi de corectare de la pag. 380) 1. Reluaţi aplicaţiile de la pagina 11 şi stabiliţi relaţiile dintre entităţile de la fiecare exerciţiu. Rezolvaţi apoi eventualele relaţii many-to-many. Verificaţi să nu existe relaţii redundante în schemele obţinute. Pentru scenariile de la punctele 2-5, determinaţi entităţile, atributele acestora şi relaţiile dintre entităţi. Desenaţi harta relaţiilor pentru fiecare exerciţiu în parte. 2. O firmă produce mai multe tipuri de maşini, un model fiind caracterizat printr-un nume, mărimea motorului şi un sufix care indică gradul de lux al acesteia (de exemplu XL, GL). Fiecare model este construit din mai multe părţi, fiecare parte putând fi folosită pentru construirea mai multor modele de maşini. Fiecare parte are o descriere şi un cod. Fiecare model de maşină este produs de exact o fabrică a firmei, fabrică ce se poate găsi în una din ţările UE. O fabrică poate produce mai multe modele de maşini şi mai multe tipuri de părţi componente. De asemenea, fiecare tip de parte componentă poate fi produsă de o singură fabrică a firmei. Capitolul 1. Proiectarea bazelor de date 21 3. O universitate are în componenţa sa mai multe facultăţi, fiecare facultate având mai multe departamente. Fiecare departament oferă studenţilor mai multe cursuri. Un profesor poate lucra la un singur departament al unei singure facultăţi. Fiecare curs are mai multe secţiuni, iar o secţiune poate să facă parte din mai multe cursuri. Un profesor poate preda mai multe secţiuni, din acelaşi curs sau din cursuri diferite, dar o secţiune poate fi predată de mai mulţi profesori. 4. La o facultate este nevoie să se memoreze date despre studenţi, cursuri şi secţiunile fiecărui curs. Fiecare student are un nume, un număr de identificare, adresa de acasă, adresa temporară, pentru cei care nu fac facultatea în localitatea lor. Un student poate opta să urmeze un curs întreg sau doar anumite secţiuni ale unui curs. De asemenea, el poate urma mai multe cursuri şi/sau secţiuni de curs simultan. Un curs poate avea mai multe secţiuni, dar o secţiune poate fi parte a mai multor cursuri. 5. Angajaţii unei firme sunt redistribuiţi la diferitele departamente din cadrul firmei. Dorim ca în baza de date să memorăm, pentru fiecare angajat, departamentul la care lucrează acum, dar şi departamentul la care a lucrat prima dată, la angajarea în firmă. 22 Baze de date – curs introductiv 1.2. Normalizarea datelor 1.2.1. Ce este normalizarea? Normalizarea este o tehnică de proiectare a bazelor de date prin care se elimină (sau se evită) anumite anomalii şi inconsistenţe ale datelor. O bază de date bine proiectată nu permite ca datele să fie redundante, adică aceeaşi informaţie să se găsească în locuri diferite sau să se memoreze în baza de date informaţii care se pot deduce pe baza altor informaţii memorate în baza de date. Anomaliile care pot să apară la o bază de date nenormalizată sunt următoarele: - anomalii la actualizarea datelor – închipuiţi-vă că la secretariatul şcolii voastre sunt memorate într-o tabelă informaţiile despre toţi elevii şcolii: nume, adresă, telefon, etc. De asemenea, la biblioteca şcolii există fişele voastre tot într-o tabelă. Aceste fişe conţin numele, prenumele, adresa, telefonul, data înscrierii la bibliotecă, etc. Acum câteva zile v-aţi schimbat domiciliul. Noua adresă trebuie modificată atât la secretariat cât şi în fişa de la bibliotecă şi în toate locurile în care această informaţie apare. Dacă modificarea nu se produce în unul dintre aceste locuri (fie că aţi uitat să anunţaţi, fie din alte motive), datele devin inconsistente. Alt scenariu: la o bibliotecă se înregistrează într-o tabelă următoarele date despre cărţi: ISBN, titlu, autor, preţ, subiect, editură şi adresa editurii. La un moment dat, o editură îşi schimbă adresa. Bibliotecara va trebui să modifice adresa editurii respective în înregistrările corespunzătoare tuturor cărţilor din bibliotecă apărute la respectiva editură. Dacă această modificare nu se face cu succes, unele dintre înregistrări rămânând cu vechea adresă, apare din nou o inconsistenţă a datelor. - anomalii de inserare – în exemplul anterior, nu vom putea memora adresa unei edituri, lucru inacceptabil dacă dorim să avem informaţii şi despre edituri a căror cărţi nu le avem în bibliotecă, eventual de la care dorim să facem comenzi. - anomalii de ştergere – să presupunem că într-o tabelă memorăm următoarele informaţii: codul studentului, codul cursului şi codul profesorului. La un moment dat, niciun student nu mai doreşte să participe la un anume curs. Ştergând toate înregistrările corespunzătoare cursului, nu vom mai putea şti niciodată cine preda acel curs. Conceptul de normalizare a bazelor de date a fost pentru prima dată introdus de către Edgar Frank Codd. Formele normale oferă indicaţii pe baza cărora puteţi decide dacă un anumit ERD este bine proiectat, neexpus anomaliilor şi inconsistenţelor. În principiu, normalizarea implică descompunerea unei entităţi în două sau mai multe entităţi, prin compunerea cărora se pot obţine exact aceleaşi informaţii. Capitolul 1. Proiectarea bazelor de date 23 Formele normale se aplică fiecărei entităţi în parte. O bază de date (sau un ERD) se găseşte într-o anumită formă normală doar dacă toate entităţile se găsesc în acea formă normală. Edgar Codd a definit primele trei forme normale 1NF, 2NF şi 3NF. Ulterior, s-au mai definit formele normale 4NF, 5NF şi 6NF care însă sunt rar folosite în proiectarea bazelor de date. 1.2.2. Prima formă normală (1NF) O entitate se găseşte în prima formă normală dacă şi numai dacă: - nu există atribute cu valori multiple; - nu există atribute sau grupuri de atribute care se repetă. Cu alte cuvinte, toate atributele trebuie să fie atomice, adică să conţină o singură informaţie. Dacă un atribut are valori multiple, sau un grup de atribute se repetă, atunci trebuie să creaţi o entitate suplimentară pe care să o legaţi de entitatea originală printr-o relaţie de 1:m. În noua entitate vor fi introduse atributele sau grupurile de atribute care se repetă. Să considerăm entitatea din Fig. 1.17, referitoare la notele elevilor unei clase. Câteva observaţii referitoare la această entitate: câte discipline studiazǎ un elev? Câte perechi (disciplina, nota) va trebui să aibă entitatea ELEV? Să spunem că ştim exact numărul maxim de discipline pe care le poate studia un elev. Ce se întâmplă dacă în anul şcolar viitor acest număr de discipline va fi mai mare? În plus, la o materie un elev poate avea mai multe note. Câte note? Cum memorăm aceste note? Le punem în câmpul corespunzător disciplinei cu virgulă între ele? Cum rezolvăm această problemă? Vom crea o nouă entitate în care vom introduce disciplina şi nota la disciplina respectivă (vezi Fig. 1.18). În acest fel, fiecărui elev îi pot corespunde oricâte note, iar la o disciplină poate avea oricâte note, singura restricţie conform acestui model fiind că un elev nu va putea primi în aceeaşi zi, la aceeaşi materie, mai multe note. Să considerăm un alt exemplu. Pentru managementul unui proiect este important să ştim pentru fiecare membru al echipei care sunt abilităţile de care dispune, pentru a cunoaşte modul în care să atribuim sarcinile în cadrul grupului. Într-o primă etapă, am proiectat o entitate ANGAJAT, care are un atribut abilităţi, ca în Fig. 1.19. Însă se ştie că fiecare angajat are mai multe abilităţi pe care dorim să le memorăm. Aşadar, atributul abilităţi nu respectă prima formă normală. În consecinţă, vom crea o nouă entitate ABILITATE în care vom memora toate abilităţile fiecărui angajat (vezi Fig. 1.20). 24 Baze de date – curs introductiv Figura 1.17 Figura 1.18 Figura 1.19 Figura 1.20 1.2.3. A doua formă normală (2NF) O entitate se găseşte în a doua formă normală dacă şi numai dacă se găseşte în prima formă normală şi în plus, orice atribut care nu face parte din UID (Unique IDentifier) va depinde de întregul UID, nu doar de o parte a acestuia. De exemplu, dacă memorăm angajaţii unui departament într-o entitate ca mai jos: Figura 1.21. Exemplu de entitate Se observă că data_nasterii şi adresa sunt două atribute care depind doar de id-ul angajatului, nu de întregul UID care este combinaţia dintre atributele id_dep şi id_angajat. Această situaţie se rezolvă prin crearea unei noi entităţi ANGAJAT, pe care o legăm de entitatea DEPARTAMENT printr-o relaţie 1:m. Capitolul 1. Proiectarea bazelor de date 25 Figura 1.22. Relaţia între cele două entităţi O situaţie mai specială este în cazul relaţiilor barate, când trebuie ţinut seama că UID-ul unei entităţi este compus din atribute din entitatea respectivă, plus un atribut sau mai multe provenite din relaţia barată. Să considerăm următorul exemplu: Figura 1.23. Exemplu de relaţie barată Se observă că UID-ul entităţii APARTAMENT este compus din combinaţia a trei atribute: numărul apartamentului, numărul blocului şi strada. Deci, toate atributele din entitatea APARTAMENT care nu fac parte din UID, trebuie să depindă de întregul UID. Dar se ştie că atributul cod_postal depinde doar de strada şi de numărul blocului, nu şi de numărul apartamentului. Acest lucru ne spune că atributul nu este memorat la locul potrivit. Deoarece depinde doar de combinaţia (strada, nr_bloc), înseamnă că de fapt depinde de UID-ul entităţii bloc. Aşadar, vom muta atributul cod_postal în entitatea BLOC. Figura 1.24. Rezultatul modificării efectuate Observaţie ! Dacă o entitate se găseşte în prima formă normală şi UID-ul său este format dintr-un singur atribut, atunci ea se găseşte automat în a doua formă normală. 26 Baze de date – curs introductiv 1.2.4. A treia formă normală (3NF) O entitate se găseşte în a treia formă normală dacă şi numai dacă se găseşte în a doua formă normală şi în plus niciun atribut care nu este parte a UID-ului nu depinde de un alt atribut non-UID. Cu alte cuvinte, nu se acceptă dependenţe tranzitive, adică un atribut să depindă de UID în mod indirect. Luăm ca exemplu entitatea CARTE din Fig. 1.25. Atributul biografie_autor nu depinde de ISBN ci de atributul autor. Nerezolvarea acestei situaţii duce la memorarea de date redundante, deoarece biografia unui autor va fi memorată pentru fiecare carte scrisă de autorul respectiv. Rezolvarea acestei situaţii constǎ în crearea unei noi entităţi, să-i zicem AUTOR, pe care o legăm de entitatea CARTE printr-o relaţie 1:m (vezi Fig. 1.26). Figura 1.25. Entitate Iniţială Figura 1.26. Introducerea unei noi entităţi Atenţie! Acest model este corect doar dacă se acceptă că o carte are un singur autor. Lăsăm ca temă rezolvarea situaţiei în care o carte poate avea mai mulţi autori. În această situaţie, apare o relaţie many-to-many pe care trebuie să o rezolvaţi. 1. Care dintre următoarele enunţuri NU este un exemplu de redundanţă? a) O relaţie între două entităţi care poate fi dedusă din altă relaţie. b) O valoare dintr-o bază de date care poate fi obţinută direct pe baza altei valori. c) Două atribute din baza de date care au aceeaşi valoare. d) O valoare din baza de date care poate fi obţinută efectuând diferite calcule asupra altor valori. e) Niciuna dintre variantele anterioare. Capitolul 1. Proiectarea bazelor de date 27 2. Care dintre următoarele cerinţe NU sunt necesare pentru ca o entitate să se găsească în a treia formă normală? a) Trebuie să se găsească în a doua formă normală. b) Fiecare atribut care nu face parte din UID trebuie să depindă de întregul UID. c) Nu trebuie să existe dependenţe tranzitive. d) Niciuna dintre variantele anterioare. 3. Care este cea mai avansată formă normală în care se găseşte entitatea alăturată? a) 1NF; c) 3NF; b) 2NF; d) ERD-ul nu este normalizat. Figura 1.27. Entitatea propusă 4. În ce formă normală se găseşte fiecare dintre următoarele entităţi? a) b) c) 5. Un magazin vinde o gamă variată de pantofi de diferite mărimi şi modele. Un model este identificat printr-un cod. Fiecare model are o descriere şi aceeaşi descriere se poate aplica mai multor modele. Atributul vanzare_saptamanala va memora numărul de pantofi de un anumit model şi o anumită mărime vânduţi săptămâna anterioară (de exemplu, 25 de perechi, model 17, mărimea 39). Atributul valoare_lunara_model reprezintă valoarea totală a pantofilor vânduţi pentru fiecare model în parte, indiferent de model. Desenaţi un ERD în forma normală 3NF, conţinând toate aceste informaţii. 6. Se dă următoarea schemă a unei baze de date existente într-o videotecă. Presupunând că videoteca dispune de un singur exemplar din fiecare film video, stabiliţi în ce formă normală se găseşte acest ERD. Dacă el nu se găseşte în forma normală 3NF, faceţi modificările necesare pentru aducerea sa la forma normală 3NF. Figura 1.28. Schema propusă 28 Baze de date – curs introductiv 7. Plecând de la următoarea entitate, desenaţi un ERD în forma normală 3NF: Figura 1.29. Entitatea propusă pentru problema 7 8. Desenaţi ERD-ul pentru următorul scenariu şi aduceţi-l în forma normală 3NF: într-o clădire se găsesc mai multe birouri. Fiecare birou este identificat unic printr-un număr. În fiecare birou se găseşte un singur telefon. Un telefon poate fi de două tipuri: telefon interior (cu care nu se pot face apeluri în afara clădirii) şi telefon exterior, cu care se pot face apeluri atât în interiorul clădirii cât şi cu exteriorul. Fiecare telefon are un număr unic. Într-un birou pot lucra mai mulţi angajaţi, pentru fiecare cunoscându-se numele, prenumele, adresa, e-mail-ul, data naşterii şi data angajării. Se ştie că un angajat poate lucra într-un singur birou. 9. Modificaţi ERD-ul de la problema anterioară în ipoteza că într-un birou pot exista mai multe telefoane, folosite în comun de către toţi angajaţii care lucrează în acel birou. 10. Aduceţi modificările necesare entităţii alăturate astfel încât să obţineţi un ERD în forma normală 3NF. Entitatea reţine informaţii despre angajaţii unei agenţii de plasare a forţei de muncă, care oferă personal cu normă întreagă sau cu program redus, pentru diferite hoteluri din întreaga ţară. Se memorează numărul de ore lucrate de fiecare angajat în diferite hoteluri. Se ştie că numărul de contract este întotdeauna dependent de codul hotelului dar nu şi invers. Figura 1.30. Entitatea propusă pentru problema 10 Capitolul 1. Proiectarea bazelor de date 29 1.3. Implementarea modelului conceptual 1.3.1. Modele de baze de date Bazele de date au fost concepute pentru stocarea volumelor mari de informaţii relativ omogene între care se pot stabili anumite relaţii. O bază de date este deci o colecţie structurată de date aflate în interdependenţă, date care pot fi consultate pentru a răspunde diferitelor interogări. Înregistrările returnate ca răspuns la o interogare devin informaţii care pot fi utilizate în luarea unor decizii ulterioare. Sistemul complex de programe care permite descrierea, organizarea, memorarea, regăsirea, administrarea şi securizarea informaţiilor dintr-o bază de date se numeşte sistemul de gestiune a bazelor de date (SGBD). Memorarea datelor conţinute de bazele de date se face pe suporturile de memorie internă sau externă folosite de calculatoare. SGBD este un software special asociat bazelor de date care asigură interfaţa între o bază de date şi utilizatorii ei, rezolvând toate cererile de acces la datele memorate. Pentru orice bază de date poate fi compusă o descriere a datelor şi obiectelor memorate, precum şi relaţiile existente între aceste obiecte. O astfel de descriere se numeşte schema bazei de date. Există mai multe modele de baze de date, acestea diferenţiindu-se în funcţie de modul de organizare a schemei bazei de date. Un model de bază de date nu este doar un mod de structurare a datelor, el defineşte de asemenea un set de operaţii care pot fi realizate cu datele respective. Cele mai cunoscute modele de baze de date sunt următoarele: Modelul tabelar – toate datele sunt memorate sub forma unui singur tabel, un tablou bidimensional de date. Modelul ierarhic – datele sunt organizate sub forma unor structuri arborescente, există deci o rădăcină cu mai mulţi dependenţi, care la rândul lor pot avea alţi dependenţi. IMS (Information Management System) produs de IBM este un exemplu de SGBD bazat pe acest tip de model. Modelul reţea este un model performant, dar complicat. O bază de date de tip reţea reprezintă o colecţie de noduri şi legături, fiecare nod putând fi legat de oricare altul. Legăturile trebuie stabilite având tot timpul în minte interogările posibile şi acţiunile viitoare probabile. Modelul relaţional reprezintă cel mai utilizat model de stocare a datelor, în care datele sunt organizate sub formă de tabele între care există diverse legături. 30 Baze de date – curs introductiv Modelul obiectual, destinat să suporte modele de obiecte complexe (organizare de tip heap cu referinţe între componente) este oarecum asemănător reţelei, iar prin faptul că pentru accesare directă, stochează o hartă a ierarhiilor şi relaţiilor claselor de obiecte, are ascendent şi în modelul ierarhic. Modelul obiectual se pretează pentru înmagazinarea informaţiilor complexe: atribute descriptive asociate datelor multimedia, documentelor, desenelor, arhivelor, etc. Modelele hibride sunt mixturi ale modelelor prezentate anterior, din care cel mai semnificativ este modelul relaţional-obiectual, obţinut prin extensii ale modelului de organizare tabelar şi izvorât din tendinţa spre universalitate a bazei de date (entităţi complexe şi de naturi diferite, evoluând în condiţii eterogene). 1.3.2. Baze de date relaţionale Bazele de date relaţionale au fost dezvoltate având în vedere în primul rând utilizatorii finali. Acest model are la bază teoria matematică a relaţiilor, ceea ce a făcut posibilă tratarea algoritmică a proiectării bazelor de date şi problema normalizării datelor. Modelul relaţional este un model simplu, bazat pe algebra relaţională, care a făcut posibilă dezvoltarea limbajelor relaţionale sub forma unui software specializat ce asistă procesul de implementare a bazelor de date. Astfel de limbaje sunt SQL-ul (Structured Query Language) şi QBE (Query By Example). În momentul de faţă, există multe sisteme performante de gestiune a bazelor de date relaţionale precum Oracle, DB2, MySQL, Informix, etc. În cazul bazelor de date relaţionale mari şi foarte mari, s-au impus SGBD-uri precum Oracle, DB2 şi Informix. Acestea au la bază tehnologia client-server. Transformarea modelului conceptual, a ERD-ului, în modelul fizic, adică în baza de date propriu-zisă, se numeşte mapare. Acest proces implică transformarea fiecărui element al ERD-ului. Prima etapă a acestui proces constă în crearea tabelelor bazei de date. Astfel: fiecărei entităţi îi va corespunde câte un tabel. Spre deosebire de entitate, un tabel va avea numele un substantiv la plural. De exemplu, entitatea ANGAJAT se va transforma în tabela ANGAJAŢI, entitatea ELEV, în tabela ELEVI, etc. Fiecare atribut al unei entităţi va deveni o coloană a tabelei. Fiecare coloană va memora date de acelaşi tip. Fiecare instanţă a unei entităţi se va transforma într-un rând (sau înregistrare) al tabelului corespunzător. Unicul identificator al entităţii devine cheia primară a tabelei. Coloana sau combinaţia de coloane care identifică în mod unic toate liniile unui tabel se numeşte cheie primară. Capitolul 1. Proiectarea bazelor de date 31 Deci, orice tabelă are linii şi coloane şi conţine datele organizate conform anumitor structuri. În limbajul bazelor de date, coloanele se numesc câmpuri. Fiecare coloană reprezintă un câmp cu o denumire unică, de un anumit tip (şir de caractere, numeric, dată calendaristică, etc.), având o dimensiune prestabilită. Rândurile tabelei se numesc înregistrări. Vom vedea pe parcursul următorului paragraf cum mapăm relaţiile dintre entităţi. numele entităţii la plural numele tabelei atributul coloana instanţa linia Figura 1.31. Maparea entităţilor Informaţiile despre o tabelă a bazei de date vor fi prezentate folosind diagramele de tabelă care sunt nişte tabele de forma celui din Tabelul 1.3, în care vom nota numele coloanelor pe care le va avea tabela bazei de date, notăm dacă o coloană face parte din cheia primară, caz în care vom scrie un pk (primary key) în coloana a treia, sau dacă face parte din cheia străină, caz în care vom scrie în coloana a doua un fk (foreign key), iar în ultima coloană vom nota dacă atributul este opţional sau obligatoriu. Pentru aceasta vom folosi aceleaşi simboluri ca şi în cazul ERD-ului. Asupra cheilor străine vom reveni în paragraful următor. În tabelul 1.3 vedeţi diagrama tabelei CĂRŢI corespunzǎtoare entitǎţii CARTE: Numele coloanei Tip cheie Opţionalitatea titlu Pk * autor Pk * data_apariţiei * format * nr_pagini * Tabelul 1.3. Exemplu de diagramă de tabel Se observă că deocamdată nu avem nicio cheie străină, deoarece cheia străină provine din relaţiile în care entitatea este implicată. Cum deocamdată această entitate nu are nici o relaţie cu nicio altă entitate, nu vom avea nicio cheie străină. 32 Baze de date – curs introductiv Completaţi diagramele de tabelă pentru entităţile de mai jos: Figura 1.32. Entităţi propuse ca exerciţiu 1.3.3. Maparea relaţiilor 1.3.3.1. Maparea relaţiilor one-to-many Să începem cu un exemplu. Vom considera ERD-ul din Fig. 1.33: Figura 1.33. Exemplu de ERD Să ne reamintim cum se citeşte relaţia dintre cele două entităţi: Fiecare JUCĂTOR poate juca într-o ECHIPĂ şi numai una. La fiecare ECHIPĂ trebuie sǎ joace unul sau mai mulţi JUCĂTORI. Observăm că nu putem memora toţi jucătorii care joacă la o echipă în cadrul tabelei ECHIPA, deoarece ar trebui să introducem o coloană cu valori multiple. Invers însă, putem cu uşurinţă să memorăm, pentru fiecare jucător, echipa la care joacă, deoarece acesta nu poate juca decât la o singură echipă. Capitolul 1. Proiectarea bazelor de date 33 Oare cum putem memora echipa la care joacă un jucător? Răspunsul este destul de simplu. Vom memora pentru fiecare jucător codul echipei la care joacă. Adică diagrama de tabel corespunzătoare entităţii JUCĂTOR va fi următoarea: Numele coloanei Nr_legitimatie Nume Prenume Data_nasterii Adresa Telefon Email cod_echipa Tip cheie Pk Fk Opţionalitatea * * * * * o o o Tabelul 1.4. Diagrama de tabel corespunzătoare entităţii JUCĂTOR De pe tabela anterioară puteţi deduce încă un element important al mapării relaţiilor: dacă relaţia pe partea many este opţională atunci şi coloanele cheii străine vor fi opţionale. Ce înseamnă acest lucru? Cum un jucător poate la un moment dat să nu joace la nici o echipă, câmpul cod_echipă va rămâne necompletat în dreptul lui (va avea valoarea NULL). Dacă însă relaţia este obligatorie pe partea many, atunci coloanele ce fac parte din cheia străină vor fi obligatorii. În general, la maparea unei relaţii de tip one-to-many vom introduce, în tabela corespunzătoare entităţii de pe partea many a relaţiei, cheia primară a entităţii de pe partea one a relaţiei. Câmpurile astfel introduse se vor numi chei străine (în engleză, foreign key). Aşadar: - cheia străină a unei tabele este cheia primară din tabela referinţă; - cheia străină este întotdeauna introdusă în tabela corespunzătoare entităţii din partea many a relaţiei. 1.3.3.2. Maparea relaţiilor one-to-one Dându-se două entităţi A şi B legate între ele printr-o relaţie one-to-one, este evident că putem include cheia primară a lui A în cadrul tabelei B, dar putem proceda la fel de bine şi invers, incluzând cheia primară a tabelei B în cadrul tabelei A, deoarece fiecărei instanţe a entităţii A îi corespunde cel mult o instanţă a entităţii B, dar şi invers, oricărei instanţe a entităţii B îi corespunde cel mult o instanţă a entităţii A. Pentru relaţia din Fig. 1.34, de exemplu, putem memora - pentru fiecare persoană, seria de paşaport, dar şi invers - pentru fiecare paşaport, putem memora cnp-ul deţinătorului. 34 Baze de date – curs introductiv Decizia depinde de specificul afacerii modelate. Dacă, de exemplu, ne interesează în primul rând persoanele şi abia apoi datele de pe paşapoarte, atunci vom adopta probabil prima variantă, a memorării seriei de paşaport în cadrul tabelei PERSOANE, iar dacă însă baza de date este destinată evidenţei paşapoartelor, atunci probabil vom adopta varianta a doua. Figura 1.34. Exemplu de ERD Uneori este convenabil să memorăm cheia străină în ambele părţi ale relaţiei, în exemplul nostru, pentru fiecare paşaport să memorăm cnp-ul persoanei care îl deţine, dar şi pentru fiecare persoană să memorăm seria de paşaport. 1.3.4. Maparea relaţiilor barate Relaţiile barate se transformǎ în urma mapǎrii în cheie străină în tabela aflată în partea many a relaţiei, la fel ca la maparea oricărei relaţii one-to-many. Bara de pe relaţie exprimă faptul că acele coloane ce fac parte din cheia străină vor deveni parte a cheii primare a tabelei din partea many a relaţiei barate. Pentru exemplul din Fig. 1.35, cheia primară a tabelei ATRIBUTE va fi formată din coloanele denumire_atribut şi denumire_entitate, aceasta din urmă fiind de fapt cheie străină în tabela ATRIBUTE: Figura 1.35. Maparea relaţiilor barate Vom avea diagramele de tabel: Numele coloanei denumire Tip cheie Pk Opţionalitatea * Tabelul 1.5. Diagrama de tabel ENTITĂŢI Capitolul 1. Proiectarea bazelor de date Numele coloanei denumire_atribut denumire_entitate opţionalitate Tip cheie Pk Pk, Fk 35 Opţionalitatea * * * Tabelul 1.6. Diagrama de tabel ATRIBUTE Să considerăm acum un exemplu în care există mai multe relaţii barate, în cascadă: Figura 1.36. Relaţii barate în cascadă Vom avea tabelele de mai jos: Numele coloanei idA C1 Tip cheie Pk Opţionalitatea * * Tabelul 1.7. Diagrama de tabel pentru entitatea A Numele coloanei idB C2 idA Tip cheie Pk Pk, Fk Opţionalitatea * * * Tabelul 1.8. Diagrama de tabel pentru entitatea B Numele coloanei idC C3 idA idB Tip cheie Pk Pk, Fk Pk, Fk Opţionalitatea * * * * Tabelul 1.9. Diagrama de tabel pentru entitatea C Numele coloanei idD C4 idA Tip cheie Pk Fk Opţionalitatea * * * Tabelul 1.10. Diagrama de tabel pentru entitatea D 36 Baze de date – curs introductiv 1. Reluaţi exerciţiile propuse pe parcursul paragrafelor anterioare şi realizaţi maparea ERD-urilor obţinute. 2. Realizaţi maparea următoarei hărţi a relaţiilor. Figura 1.37. Harta relaţiilor propusă ca aplicaţie 1.3.5. Operaţii specifice prelucrării bazelor de date Orice sistem de gestiune a bazelor de date (SGBD) trebuie să asigure următoarele funcţii: definirea structurii bazei de date; încărcarea datelor în baza de date (adăugarea de noi înregistrări la baza de date); accesul la date pentru: o interogare (afişarea datelor, sortarea lor, calcule statistice, etc.); o ştergere; o modificare; Capitolul 1. Proiectarea bazelor de date 37 întreţinerea bazei de date: o refacerea bazei de date prin existenţa unor copii de siguranţă; o repararea în caz de incident; o colectarea şi refolosirea spaţiilor goale; posibilitatea de reorganizare a bazei de date prin: o restructurarea datelor; o modificarea accesului la date; securitatea datelor. O parte din aceste operaţii pot fi realizate cu ajutorul limbajului SQL, altele cu ajutorul unor programe specializate, care sunt puse la dispoziţia administratorului bazei de date de către sistemul de gestiune al bazelor de date. 1.3.6. Reguli de integritate Detalierea caracteristicilor pe care trebuie să le prezinte un SGBD pentru a fi considerat relaţional a fost realizată de către E. F. Codd în 1985 sub forma a 13 reguli. Una dintre aceste reguli precizează că restricţiile de integritate trebuie să poată fi definite în limbajul utilizat de SGBD pentru definirea datelor. Regulile de integritate garantează că datele introduse în baza de date sunt corecte şi valide. Aceasta înseamnă că dacă există orice regulă sau restricţie asupra unei entităţi, atunci datele introduse în baza de date respectă aceste restricţii. Tipurile de reguli de integritate sunt următoarele: Integritatea entităţilor – indică faptul că nici o coloană ce face parte din cheia primară nu poate avea valoarea NULL. În plus, pentru fiecare înregistrare, cheia primară trebuie să fie unică. Integritatea de domeniu – acest tip de reguli permite ca într-o anumită coloană să se introducă doar valori dintr-un anumit domeniu. De exemplu, putem impune ca salariul unui angajat să fie cuprins între 4500 şi 5000 RON. Integritatea referenţială – este o protecţie care asigură ca fiecare valoare a cheii străine să corespundă unei valori a cheii primare din tabela referită. De exemplu, referindu-ne la tabelele JUCĂTORI şi ECHIPE, corespunzătoare ERD-ului din Fig. 1.33, cod este cheie primară în tabela ECHIPE, iar în tabela JUCĂTORI, cod devine cheie străină. Astfel, valoarea câmpului cod din cadrul tabelei JUCĂTORI corespunzătoare unui anumit jucător trebuie să se regăsească printre valorile câmpului cod din tabela ECHIPE, altfel ar însemna că jucătorul respectiv joacă la o echipă inexistentă (vezi Fig. 1.38). 38 Baze de date – curs introductiv Figura 1.38. Exemplu de încălcare a integrităţii referenţiale Situaţii de încălcare a integrităţii referenţiale pot apărea: la adăugarea unei noi înregistrări în baza de date, se poate încerca introducerea unor valori invalide pentru câmpurile cheii străine; la actualizarea bazei de date; la ştergerea unei înregistrări. De exemplu, se şterge înregistrarea corespunzătoare unei anumite echipe (echipa se desfiinţează). Înregistrările jucătorilor care au jucat la acea echipă vor încălca integritatea referenţială, deoarece se vor referi la o echipă care nu mai există. Soluţiile posibile sunt ca la ştergerea unei echipe, toţi jucătorii care au activat la acea echipă să fie şi ei şterşi din baza de date (ştergere în cascadă), sau valoarea câmpului cod_echipă pentru acei jucători să fie setată la NULL, ceea ce înseamnă că acei jucători nu activează la nicio echipă. 1.3.7. Programe de validare şi de acţiune În realizarea modelului conceptual al unei baze de date se ţine cont de modul în care funcţionează afacerea modelată, datele care trebuie să fie memorate, relaţiile dintre acestea, etc. Modul de utilizare a diferitelor date, modul în care acestea sunt relaţionate pot diferi de la o afacere la alta. Regulile afacerii unei organizaţii se referă în esenţă la procesele şi fluxurile tuturor datelor şi activităţilor zilnice din cadrul organizaţiei. Cum funcţionează organizaţia? Care sunt activităţile sale? Capitolul 1. Proiectarea bazelor de date 39 Regulile afacerii acoperă următoarele aspecte ale unei organizaţii: Orice tip de politici organizaţionale de orice tip şi de la orice nivel al organizaţiei. Orice tip de formule de calcul (ca, de exemplu, modul de calcul al ratelor pentru diverse împrumuturi, modul de calcul al salariilor, etc.). Orice tip de reguli impuse de lege sau reguli interne ale organizaţiei. Regulile simple ale afacerii pot fi implementate în modelul bazei de date prin intermediul relaţiilor dintre entităţi. Acest tip de reguli se numesc reguli structurale. Alte reguli ale afacerii pot fi implementate folosind regulile de integritate despre care am discutat în paragraful anterior. Există totuşi reguli pentru implementarea cărora va trebui să scriem programe speciale folosind limbaje specializate specifice SGBD-ului utilizat. Acest tip de reguli se numesc reguli procedurale. 1. Când mapaţi un ERD, care dintre următoarele afirmaţii NU este adevărată? a) Fiecare entitate este mapată într-o tabelă. b) Fiecare atribut este mapat într-o coloană a tabelei corespunzătoare. c) Fiecare entitate în parte este mapată într-o linie din tabelul corespunzător. d) Fiecare relaţie one-to-many se transformă într-o cheie străină. 2. Într-o tabelă, o instanţă a unei entităţi este mapată ca: a) o relaţie many-to-many; b) o linie din tabel; c) o coloană din tabel; d) un atribut; e) un index. 3. Două entităţi A şi B se găsesc într-o relaţie one-to-one care este opţională la ambele capete. Care dintre următoarele variante este o soluţie corectă de mapare ? a) Combinarea celor două entităţi A şi B într-o singură relaţie. b) Crearea a două tabele separate şi includerea unei chei străine atât în tabela A cât şi în tabela B. c) Combinarea celor două entităţi A şi B într-o singură tabelă. d) Utilizarea unei chei primare care să fie o combinaţie a cheilor primare din A şi B. 4. Referitor la ERD-ul din Fig. 1.39, care dintre afirmaţiile de mai jos corespund unei variante corecte de mapare? a) Se preia cheia primară din tabela PERSOANE şi se adaugă ca şi cheie străină la tabela MASINI. 40 Baze de date – curs introductiv Figura 1.39. ERD propus pentru problema 4 b) Se preia cheia primară din tabela MASINI şi se adaugă ca şi cheie străină la tabela PERSOANE. c) Se creează o singură tabelă cu informaţiile din ambele entităţi. d) Se preiau cheile primare din ambele tabele şi se introduc într-o nouă tabelă numită PROPRIETARI. e) Oricare dintre variantele de mai sus este corectă. 5. O bază de date conţine următoarele tabele: Tabela DEPARTAMENTE DepNo Departament 1 IT 2 Electric 3 Geografie 4 Istorie 5 Business Tabela ANGAJATI IdAngajat NumeAngajat 1 Ionescu 2 Georgescu 3 Vasilescu 4 Marinescu 5 Andreescu Tabela ANGAJARI IdAngajat DepNo 1 1 3 2 4 1 3 3 1 2 2 5 Desenaţi ERD-ul din care s-au obţinut prin mapare aceste tabele. (vezi baremele de notare şi de corectare de la pag. 380) Capitolul 1. Proiectarea bazelor de date 41 1.4. Managementul de proiect 1.4.1. Ce este un proiect ? Un proiect este o secvenţă de acţiuni intercorelate, care se derulează într-o perioadă de timp clar definită şi delimitată, orientate către îndeplinirea unor obiective cu caracter unic şi precis. Putem defini managementul de proiect ca fiind efortul planificării, organizării şi mobilizării resurselor pentru un scop dat. Managementul de proiect este de fapt un instrument, un set de metode şi tehnici care ne ajută să atingem cu eficacitate scopurile şi obiectivele propuse. 1.4.2. Etape în realizarea unui proiect Orice proiect trece printr-o serie întreagă de etape. Ceea ce este important de reţinut este faptul că orice proiect este un proces iterativ, în sensul că orice etapă poate fi repetată de mai multe ori, în funcţie de necesităţile de redefinire a anumitor cerinţe. Etapele principale pe care le parcurgem pentru realizarea unui proiect sunt următoarele: Definirea proiectului o Validarea proiectului – în această etapă vor fi analizate toate documentele prezente în propunerea de proiect. Această analiză va duce fie la confirmarea şi acceptarea proiectului, fie va duce la respingerea, sau eventual regândirea acestuia, în cazul în care propunerea de proiect nu a fost bine realizată, şi resursele au fost subestimate. o Definirea proiectului – constă în enunţarea problemei, stabilirea scopului proiectului, stabilirea unei liste a posibilelor soluţii etc. Orice proiect propus poate avea elemente pe care cel care l-a propus nu a considerat necesar să le detalieze, însă echipa de proiect poate avea nevoie de informaţii suplimentare pentru a înţelege corect enunţul proiectului. Organizarea proiectului o Stabilirea obiectivelor o Stabilirea grupului ţintă - când încercaţi să stabiliţi care este grupul ţintă al unui proiect trebuie să răspundeţi la următoarele întrebări: Cine trebuie să ştie despre proiect? Cine va folosi acest proiect? Asupra cui vor avea impact rezultatele proiectului? Cine finanţează acest proiect? Cine aprobă acest proiect? Cine livrează proiectul? Cine va trebui să fie instruit? 42 Baze de date – curs introductiv o Stabilirea cerinţelor proiectului – este un proces iterativ care poate implica negocierea. Fiind conducătorul proiectului, ştiţi ce este posibil şi ce nu în ceea ce priveşte scopul proiectului, timpul alocat, costurile, cerinţele de calitate etc. Un proiect care nu îndeplineşte cerinţele utilizatorului este din start un eşec. o Stabilirea infrastructurii proiectului – infrastructura se referă la elemente precum instrumente de comunicare în cadrul echipei (telefoane mobile, PDA, laptop, etc.), spaţiu de lucru pentru membrii echipei (birou), echipamente de birou, laboratoare, etc. o Stabilirea sistemului de calitate a proiectului o Formarea echipei de proiect Planificarea proiectului – planul proiectului prezintă desfăşurarea normală, ideală a unui proiect. Acesta oferă reperele necesare evaluării situaţiei proiectului. Fără existenţa unui plan dinainte stabilit, exercitarea controlului este practic imposibilă. Elaborarea proiectului – în această etapă se stabilesc detaliile tehnice şi de design, se revizuiesc schemele tehnice, se revizuiesc criteriile de cost şi performanţă. Această etapă vizează conturarea unui model al aplicaţiei. Se proiectează bazele de date, se realizează interfeţele aplicaţiei (rapoarte, ecrane, meniuri etc), se proiectează prelucrările automate, etc. Dezvoltarea proiectului Implementarea proiectului – constă în instalarea produsului şi testarea sa, instruirea viitorilor utilizatori. Se testează produsul în condiţii reale, şi se înlătură eventualele erori depistate în funcţionare. Tot în această fază personalul implicat în proiect este redus. Se încep activităţile de publicitate. Finalizarea proiectului – se elaborează rapoartele finale, se eliberează personalul angajat în derularea proiectului. 1.4.3. Principiile lucrului în echipă Colaborarea înseamnă mai ales implicarea oamenilor în crearea propriilor soluţii la problemele cu care se confruntă. În cadrul unui proces colaborativ, oamenii au ocazia să afle punctul de vedere şi perspectiva celorlalţi şi să gândească împreună soluţii la problemele comune. Colaborarea este o artă ! Sunteţi uneori puşi în situaţia de a vorbi şi colabora cu persoane pe care nu le cunoaşteţi, cu care nu sunteţi de acord sau pe care nu le agreaţi. Mărimea echipei de proiect este un factor important care poate influenţa modul de conducere al acesteia. Într-o echipă prea mare pot apărea probleme de comunicare, dificultăţi în luarea deciziilor. În general, se acceptă că o echipă pentru a putea fi eficientă nu trebuie să depăşească un număr de 10 persoane. Capitolul 1. Proiectarea bazelor de date 43 În cazul proiectelor cu număr mare de persoane implicate, participanţii la proiect trebuie organizaţi în mai multe echipe satelit intercorelate, coordonate de o echipă principală al cărei rol principal este să asigure o comunicare facilă şi eficientă între toate echipele satelit. Fiecare membru al unei echipe de proiect trebuie să cunoască ceea ce se aşteaptă de la el. Trebuie stabilite de la început rezultatele aşteptate de la fiecare membru al echipei în parte, acţiunile ce trebuie executate pentru obţinerea rezultatului, relaţiile de subordonare în cadrul echipei, metodele de măsurare a performanţei fiecărui membru al echipei. Liderul echipei are în primul rând rolul de a stabili, menţine şi proteja un proces colaborativ care permite tuturor să participe neîngrădit la munca grupului. 1.4.4. Pregătirea şi susţinerea unei prezentări În momentul finalizării unui proiect va trebui să prezentaţi rezultatul muncii voastre în faţa clientului. Susţinerea unei prezentări în public este pentru oricine o sursă de stres. Mulţi oameni ar prefera să evite complet această problemă, dar de multe ori este imposibil de evitat. Indiferent că lucrăm singuri sau în echipă, vom putea fi puşi în situaţia de a face o prezentare publică. Adevărul este că susţinerea unei prezentări publice nu trebuie să fie un motiv de stres. Atât timp cât aveţi în minte câteva principii de bază, vorbitul în public va deveni o experienţă plăcută pentru voi. Iată câteva principii de care trebuie să ţineţi seama pentru a depăşi dificultăţile legate de vorbitul în public: Vorbitul în public NU este un motiv real de stres. Mulţi oameni erau la început îngroziţi de ideea de a vorbi în public. Le tremurau genunchii, vocea, gândurile o luau razna. Totuşi, ei au învăţat să elimine definitiv teama de a vorbi în public. Nu îţi propune să fi genial sau perfect, chiar dacă aşa ţi se pare că ar trebui. Ţi se poate întâmpla să greşeşti, ţi se poate întâmpla să uiţi porţiuni întregi din ceea ce vroiai să spui, poţi să nu glumeşti deloc şi totuşi prezentarea ta să aibă succes. Totul depinde de cum defineşti tu şi auditorul tău succesul. Auditorul tău nu aşteaptă de la tine să fi perfect. Cu cât încerci mai tare să fi perfect cu atât mai mult vei adânci anxietatea pe care o ai şi efectul va fi contrar celui scontat. Ceea ce este important atunci când faci o prezentare în faţa unui public este să oferi ceva auditorului. Atâta timp cât auditorul rămâne cu ceva de pe urma prezentării, ei o vor considera un succes. Nu încerca să transmiţi prea multe informaţii într-o prezentare. Nu trebuie să transmiţi “tone” de informaţii şi detalii, oferă publicului doar ceea ce el doreşte cu adevărat. Studiile arată că oamenii îşi amintesc doar foarte puţine elemente din ceea ce vorbitorii prezintă. 44 Baze de date – curs introductiv Nu încerca să mulţumeşti pe toată lumea, acesta este un punct de vedere nerealist. Nu încerca să imiţi alţi prezentatori, e un lucru foarte dificil. Încearcă să fi tu însuţi, e mult mai uşor. Nu încerca să controlezi comportamentul auditorului. Dacă oamenii sunt agitaţi, nu încerca să le controlezi comportamentul. Dacă cineva vorbeşte cu vecinul, citeşte ziarul sau chiar a adormit în timpul prezentării tale, ignoră-l! Nu te scuza. Auditorul nu are de unde să ştie că ai uitat să spui ceva. Vorbeşte cu încredere, fi convins de ceea ce spui. Organizează logic conţinutul prezentării. Orice prezentare va avea o introducere, conţinutul propriu-zis şi o concluzie. Utilizează materiale audio-vizuale ajutătoare, dacă acestea sunt necesare. O prezentare în PowerPoint, bine realizată, poate fi de un real folos. o Asigură-te din timp că întregul echipament (calculator, video-proiector, etc) funcţionează. o Nu bombarda auditorul cu efecte sonore sau cu animaţii exagerate şi utilizează în mod echilibrat culorile. o Include în prezentarea electronică doar cele mai importante idei. o Nu încărca slide-urile cu prea multe texte. În general, se admit 7-10 cuvinte pe linie şi maximum 10 linii de text pe un slide. o Preferă un grafic în locul textelor, dar nu exagera cu mai mult de două grafice pe un slide. Păstrează contactul vizual cu auditorul. Încearcă în acest fel să faci fiecare persoană din public să se simtă implicată. Fă scurte pauze. Nu alerga prin prezentare, dă-ţi răgazul să respiri. Dă timp audienţei să reflecte la ceea ce ai spus. Adaugă puţin umor prezentării tale, dacă acest lucru este posibil. Păstrează treaz interesul auditorului pe parcursul întregii prezentări. Nu ţine mâinile în buzunar în timpul prezentării ! Adoptă o ţinută adecvată în ziua prezentării. Atrage atenţia auditorului asupra a ceea ce spui, nu a mesajelor de pe tricou sau asupra bijuteriilor pe care le porţi ! În acest moment v-aţi însuşit elementele de bază ale modelării bazelor de date. Aţi văzut diverse situaţii ce pot apărea pe parcursul creării modelului conceptual şi cum să rezolvaţi anumite situaţii problemă. Capitolul 1. Proiectarea bazelor de date 45 În acest subcapitol aţi aflat cum se organizează un proiect, care sunt etapele de realizare ale lui şi cum să organizaţi munca într-o echipă. De asemenea, ştiţi cum să pregătiţi şi să faceţi o prezentare în faţa unui public. Este momentul să aplicaţi toate aceste cunoştinţe! Ideal ar fi să formaţi o echipă din 2 - 4 persoane, alegeţi-vă o temă de proiect, fie din temele propuse în această secţiune, fie una propusă chiar de voi. Realizaţi modelul conceptual al afacerii modelate. Atenţie! Repartizaţi sarcinile în mod echitabil în cadrul echipei de proiect. Pregătiţi o prezentare a proiectului realizat, materialele vizuale (postere, pliante, prezentarea PowerPoint), care să vă ajute la susţinerea prezentării. Puteţi chiar organiza împreună cu cadrul didactic, un concurs la nivelul clasei sau al şcolii / academiei, la care să invitaţi şi alte persoane. Nu uitaţi să documentaţi fiecare presupunere pe care aţi făcut-o în realizarea proiectului! Tema 1: „Campionatul Naţional de Fotbal”. O bază de date memorează informaţii despre jucătorii şi cluburile din cele patru divizii din campionatul naţional de fotbal. Fiecare club de fotbal are un nume unic în întregul campionat. Un club de fotbal poate avea mai multe echipe în campionat. Pentru fiecare echipă se cunoaşte căpitanul său, care este unul dintre jucători. Jucătorii au asociat un identificator unic, un nume, nu neapărat unic şi sunt angajaţi la diferitele echipe. În baza de date se păstrează şi detalii privind nivelul abilităţilor (notă cuprinsă între 1 şi 10) fiecărui jucător pentru fiecare dintre poziţii de joc (portar, apărător, mijlocaş, etc). De exemplu, jucătorul Ionescu poate avea nivelul 10 pe postul de portar, 7 pentru poziţia de apărător, etc. Este important ca în baza de date să se memoreze un istoric al tuturor jucătorilor, la ce echipe au jucat, în ce perioadă, etc. Se va memora şi un istoric al golurilor marcate de fiecare jucător de-a lungul carierei. Pentru fiecare gol se va şti data, meciul în care a fost marcat, minutul, etc. Tema 2: „Firmă de închirieri de maşini”. Trebuie să proiectaţi baza de date a unei firme care oferă spre închiriere maşini de diferite tipuri. Trebuie să păstraţi informaţii despre maşinile firmei, firmele cu care firma are contracte de colaborare (de exemplu, garaje), maşinile închiriate, veniturile firmei şi bineînţeles date despre clienţii firmei. Maşinile sunt descrise prin date precum: producătorul, modelul, anul de fabricaţie, mărimea motorului, tipul de combustibil, numărul de pasageri, numărul de înmatriculare, preţul de cumpărare, data cumpărării, preţul de închiriere şi detalii privind asigurarea maşinii. 46 Baze de date – curs introductiv Toate reparaţiile importante asupra maşinilor firmei sunt făcute de firme cu care colaborează. Unele firme solicită plata serviciilor de service imediat după ce reparaţia a fost făcută, altele acceptă plata în rate a serviciilor. Trebuie ţinută evidenţa clară a fiecărei maşini, a celor închiriate, a celor aflate în reparaţii, etc. Se păstrează de asemenea evidenţa tuturor veniturilor şi cheltuielilor firmei: cumpărarea de noi maşini, închirierea de maşini, cheltuieli de reparaţii, vânzarea unor maşini mai vechi din parcul auto (firma preferă să nu păstreze în parcul auto o maşină mai mult de un an), taxe de asigurare a maşinilor etc. Firma deţine un portofoliu de clienţi destul de stabil. Pentru clienţii privilegiaţi oferă reduceri la închirierea de maşini. Aceşti clienţi au de asemenea posibilitatea de a rezerva o maşină din timp. O maşină poate fi închiriată pentru o perioadă de timp de la o zi la un an. Plata pentru închirierea unei maşini se poate face cash sau cu credit card. Se acceptă orice tip de card. Datele importante despre clienţi precum numele, adresa, numărul de telefon, seria permisului de conducere, etc. vor fi şi ele memorate în baza de date. Tema 3: „Firmă IT”. Trebuie să proiectaţi baza de date a unei companii de dimensiune medie din domeniul IT. Firma livrează diferite produse clienţilor săi, de la simple aplicaţii create la cerere, până la instalări de echipamente hardware şi software particularizat. Firma are ca angajaţi diverşi experţi, consultanţi şi personal auxiliar. Întregul personal este angajat pe termen nelimitat, nu există angajaţi temporari sau colaboratori. Compania este împărţită în mai multe departamente, fiecare departament fiind condus de către un angajat din cadrul departamentului respectiv. Pentru un proiect care trebuie dezvoltat în cadrul firmei, se formează o echipă de persoane selectate din mai multe departamente. Managerul de proiect este pe deplin şi exclusiv responsabil de conducerea proiectului, independent de ierarhia de conducere din cadrul firmei. Tema 4: „Spital”. Un spital este format din mai multe secţii, precum Pediatrie, Oncologie, Dermatologie etc. În fiecare secţie sunt internaţi mai mulţi pacienţi, pe baza recomandării medicului de familie şi a confirmării făcute de către un specialist al spitalului. La internare, sunt înregistrate datele personale ale pacienţilor. O fişă separată ţine evidenţa investigaţiilor făcute pacientului pe toată perioada internării, rezultatele acestor investigaţii, tratamentul aplicat pacientului şi rezultatele obţinute în urma tratamentelor efectuate. Un pacient este repartizat unui anumit medic care coordonează toate investigaţiile şi tratamentele aplicate pacientului, însă acesta poate solicita şi altor colegi să examineze pacientul său. Medicii sunt specialişti în diverse ramuri ale medicinii, şi pot avea în supraveghere mai mulţi pacienţi, nu neapărat toţi din aceeaşi secţie. Tema 5: „Editură”. O editură editează cărţi ştiinţifice din diferite domenii. Cărţile sunt scrise de autori specializaţi într-un anumit domeniu. Firma are angajaţi mai mulţi editori care nu sunt neapărat specialişti în diferitele domenii, fiecare editor fiind responsabil pentru mai multe publicaţii. O carte acoperă unul din domeniile în care este specialist autorul, fiecare autor lucrează cu un editor, dar poate avea spre publicare o altă carte de care este responsabil un alt editor. CAPITOLUL Limbajul MySQL În acest capitol veţi învăţa MySQL, limbaj de programare specializat pentru gestiunea bazelor de date relaţionale pe Internet. Cu ajutorul platformei EasyPHP, veţi putea lucra uşor pe calculatorul dvs. Ce este şi de ce învăţăm MySQL ? Cum rulăm MySQL pe propriul calculator ? Cum creăm o bază de date ? Tabele – noţiuni elementare Tipuri de date, operatori şi funcţii Afişarea c oloanelor care rezultă în urma unui calcul Valoarea NULL. Valori implicite Ce sunt cheile primare şi cheile unice ? Autoincrementare Cum sortăm şi cum filtrăm datele ? Cum actualizăm datele dintr-un tabel ? Funcţii agregate Utilizarea subinterogărilor Cum putem grupa datele ? Ce sunt uniunile de tabele ? Cuvinte cheie: tipuri de date, operatori, funcţii, valoare implicită, cheie primară, cheie unică, autoincrementare, sortare, filtrare, actualizare, funcţii agregate, subinterogări, grupare, uniune 48 Baze de date – curs introductiv 2.1. Ce este şi de ce învăţăm MySQL ? În ultimii ani, utilizarea bazelor de date pe Internet a luat o amploare deosebită. Există o mulţime de aplicaţii, extrem de utile (care le utilizează), cum ar fi: - Aplicaţii de contorizare, aplicaţii prin care cei care au creat un site au posibilitatea să-şi contorizeze numărul de vizitatori ai site-ului respectiv sau, mai mult, contorizarea se poate extinde la nivel de pagină afişată. Pentru a evita cu un vizitator, care a deschis de mai multe ori pagina respectivă, să nu fie contorizat de mai multe ori, se va reţine adresa IP a acestuia. - Comerţ electronic, aplicaţii prin care anumite firme îşi promovează şi vând produsele de care dispun. În acest caz, bazele date vor reţine, pe de o parte, informaţii despre produse, iar pe de altă parte, comenzile clienţilor. Avantajele comerţului electronic sunt uriaşe pentru că nu implică cheltuieli pentru spaţiile de desfacere (magazine) şi, teoretic, oferta se adresează clienţilor din toată lumea. - Votul electronic, aplicaţii prin care se poate afla părerea vizitatorilor site-ului respectiv referitoare la tema supusă votului. Şi aici este important ca un vizitator să nu voteze de mai multe ori, motiv pentru care baza de date reţine adresa IP a vizitatorului. - Aplicaţii de comunicare - aplicaţii prin care diverşi vizitatori ai site-ului respectiv dezbat o anumită problemă, supusă discuţiei. În astfel de cazuri, bazele de date vor reţine informaţii despre cei care scriu (de multe ori, aceştia, mai întâi, trebuie să se înscrie în baza de date a site-ului) şi mesajele acestora. Există o mulţime de alte aplicaţii ale bazelor de date: site-urile diverselor ziare, posturi de televiziune, bazele de date create şi exploatate de motoarele de căutare, etc. Şirul acestora este practic infinit. Iată motivul pentru care ne vom iniţia în crearea şi exploatarea bazelor de date pe Internet. 1 Ce este SQL ? În practica creării şi utilizării bazelor de date relaţionale s-a impus necesitatea existenţei unui limbaj standard care permite efectuarea acestor operaţii. Astfel, a apărut SQL - Structured Query Language. Limbajul este supervizat de comisia de standardizare ANSI (American National Standards Institute), motiv pentru care se mai numeşte şi ANSI SQL. SQL nu este un limbaj de firmă, el este implementat de o mulţime de SGBD-uri, de această dată consacrate, cum ar fi: Microsoft Access, Oracle, Microsoft SQL Server şi, bineînţeles, MySQL. Ce este MySQL ? MySQL este un limbaj specializat pentru gestiunea bazelor de date relaţionale pe Internet. Aşa cum s-a mai spus, are la bază limbajul SQL. MySQL gestionează baze de date care se găsesc pe server, iar comenzile pot fi date cu ajutorul limbajului PHP, dar şi cu alte limbaje, ca de exemplu Java. 1 Revedeţi “Capitolul 1. Proiectarea bazelor de date” deoarece stă la baza tuturor noţiunilor prezentate aici! Capitolul 2. Limbajul MySQL 49 Prin utilizarea pachetului EasyPHP putem să instalăm MySQL pe calculatorul nostru. Vedeţi Anexa 1! Exact în acest mod vom proceda pentru studiul limbajului MySQL, adică vom crea baze de date pe calculatorul nostru. 2.2. Cum rulăm MySQL pe propriul calculator ? Dacă ați instalat pachetul EasyPHP, urmând paşii prezentaţi în Anexa 1, ați observat faptul că avem la dispoziție modulul phpMyAdmin, care ne permite să creăm și să manipulam baze de date, având o interfață intutivă. De aici putem să creăm, edităm și să ștergem baze de date și tabele; executăm comenzi SQL; importăm / exportăm date din\în fișiere csv; asigurăm mentenanța serverului de date; efectuăm căutări în bazele de date, ș.a.m.d Pentru mai multe informații, accesați pagina oficială a proiectului: https://phpmyadmin.net Pe calculatorul nostru putem accesa pagina phpmyadmin din Dashboard, care poate fi accesat la adresa locală: http://127.0.0.1:10000/ Se va deschide apoi următoarea fereastră: Pentru a scrie comenzi SQL, alegem din meniu comanda asociată care va deschide un editor online unde putem lucra: 50 Baze de date – curs introductiv Scriem comenzile potrivite apoi apăsăm butonul Execută. Important. Dacă obțineți erori la crearea tabelelor în paginile ce vor urma, executați întregul cod sql (copy/paste în editorul SQL) ce se găsește la adresa de mai jos: https://raw.githubusercontent.com/phpmyadmin/phpmyadmin/master/ sql/create_tables.sql (implicit nu este creată o bază de date specifică pentru phpmyadmin) Să vedem în continuare cum putem crea prima noastră bază de date! Capitolul 2. Limbajul MySQL 51 2.3. Crearea unei baze de date În MySQL, pentru a crea o bază de date se dă comanda: CREATE DATABASE nume_baza; În urma acestei comenzi, teoretic, se creează baza de date cu numele indicat de noi (prima_db în acest prim exemplu): În fapt, se creează un folder cu numele bazei de date în subfolder-ul data al folder-ului MySQL, în cazul nostru: C:\Program Files\EasyPHP-Webserver-14.1b2\binaries\dbserver\data 52 Baze de date – curs introductiv Dacă există deja o bază de date cu acest nume, se refuză crearea uneia noi: În cazul în care, după deschiderea aplicaţiei sau în timpul sesiunii de lucru, se doreşte lucrul cu o anumită bază de date, se dă comada de mai jos: USE nume_baza; Pentru ştergerea unei baze de date se utilizează comanda: DROP DATABASE nume_baza Uneori, din anumite motive, dorim o listă a bazelor de date existente. În acest caz, se utilizează comanda de mai jos: SHOW DATABASES Interfața grafică a modulului phpmyadmin ne permite bineînțeles să lucrăm și vizual, precum observați probabil deja – există opțiunea creării unei baze de date noi direct din lista care se regăsește în partea stângă: Vom prefera însă în acest curs să ne axăm mai mult pe partea de programare, deci utilizăm mai des comenzi SQL din motive pur pedagogice. Capitolul 2. Limbajul MySQL 53 2.4. Tabele. Noţiuni elementare Într-un tabel coloanele sunt identificabile prin nume, iar rândurile, prin valorile pe care le memorează. Toate datele dintr-o coloană au acelaşi tip. Un tabel are un număr specificat de coloane, dar are un număr nespecificat de rânduri. Uneori, când ne referim la un rând, folosim şi termenul de înregistrare, iar atunci când ne referim la data din rând, situată într-o anumită coloană, folosim 1 şi termenul de câmp . O instrucţiune prin care se poate crea un tabel este prezentată mai jos, unde 2 ceea ce este trecut între paranteze drepte este considerat facultativ : CREATE TABLE nume_tabel ( nume_coloana1 tip_data nume_coloana2 tip_data ... nume_coloanan tip_data ); [specificatori], [specificatori], [specificatori] Specificatorii se referă la cheia primară, valori distincte, valori implicite, autoincrementare, dacă printre valorile reţinute se poate găsi sau nu valoarea NULL. Toate acestea sunt tratate separat. Exemplul 2.1. Se creează un tabel cu 3 coloane: prima conţine codul materialului şi poate reţine cel mult 5 caractere, a doua conţine denumirea materialului şi poate reţine până la 20 de caractere, iar ultima reţine cantitatea din fiecare material (în bucăţi) şi poate reţine un număr natural cu maximum 3 cifre: CREATE TABLE materiale (cod char(5), denumire char(20), cantitate_buc INT(3) ); Pentru introducerea rândurilor în tabel se utilizează comanda de mai jos, unde se introduc, pe rând, toate câmpurile unei linii, în ordinea în care au fost declarate coloanele: INSERT INTO nume_tabel VALUES (data1, data2...datan); Exemplul 2.2. Se inserează în tabel două linii: INSERT IMTO materiale VALUES(‘001’, ‘panouri’,78); INSERT IMTO materiale VALUES(‘007’, ‘placi’,0); Noţiunea de tabel este prezentată pe scurt. O prezentare amănunţită se găseşte în capitolul anterior, “Proiectarea bazelor de date”. 1 Specificatorii vor fi trataţi separat. Tabelul astfel creat nu conţine nicio înregistrare. Pentru adăugarea înregistrărilor utilizăm altă instrucţiune MySQL. 2 54 Baze de date – curs introductiv În practica utilizării bazelor de date, instrucţiunea anterioară este considerată ca generatoare de erori, prin faptul că se poate greşi ordinea de introducere a datelor, iar efectul este acela că tabelul va conţine date eronate. Din acest motiv, se preferă forma de mai jos a instrucţiunii, deşi este necesar să scriem mai mult… Se inserează în tabel valori pentru coloanele indicate în ordinea în care au fost declarate coloanele: INSERT INTO nume_tabel (nume_coloana1,... nume_coloanak) VALUES (data1,...datak); Pentru a afişa întreg tabelul se utilizează comanda: SELECT * FROM nume_tabel Exemplul 2.3. Pentru tabelul creat anterior se dă comanda: SELECT * FROM materiale Figura 2.3. Exemplu de tabel Există posibilitatea să afişăm numai anumite coloane ale tabelului, în ordinea pe care o dorim, dacă folosim instrucţiunea de mai jos: SELECT nume_coloana1,... nume_coloanak FROM nume_tabel Să observăm faptul că, atunci când afişăm un tabel, în capul său se trece numele coloanelor din tabel. Dacă dorim ca în capul de tabel să figureze alt nume pentru o coloană, atunci, în instrucţiunea de mai sus, în loc de a scrie numai numele coloanei, vom scrie numele coloanei urmat de cuvântul cheie AS şi de numele care dorim să fie afişat în capul de tabel. Astfel, utilizăm alias-uri pentru numele coloanelor. Exemplul 2.4. Pentru tabelul de mai sus vom scrie: SELECT cod AS codul, denumire AS nume, cantitate_buc AS cantitate FROM materiale; Putem vizualiza numele tabelelor existente în baza de date prin utilizarea instrucţiunii următoare: SHOW TABLES [FROM nume_baza] În caz că am uitat care sunt coloanele dintr-un anumit tabel al bazei de date şi tipul acestora, putem utiliza instrucţiunea: SHOW COLUMNS FROM nume_tabel Capitolul 2. Limbajul MySQL 55 2.5. Tipuri de date în MySQL Aşa cum am văzut, coloanele au un anumit tip. Până în prezent, am utilizat câteva tipuri fără a oferi o prezentare unitară a acestora. Este momentul s-o facem acum. În linii mari, tipurile de date din MySQL se împart în: a) tipuri care reţin şiruri de caractere; b) tipuri numerice; c) tipuri care reţin data, ora. În continuare, vom prezenta detaliat pe fiecare în parte. 2.5.1. Tipuri de date care reţin şiruri de caractere Câteva tipuri care reţin şiruri de caractere sunt prezentate mai jos: Tip Descriere CHAR[(n)] VARCHAR TINYTEXT TEXT MEDIUMTEXT LONGTEXT ENUM SET Un câmp de acest tip reţine un şir de caractere de lungime n (fixă). În caz că n nu este precizat, reţine un caracter. Ocupă n octeţi. Un câmp de acest tip reţine şiruri de cel mult 255 de caractere. Un octet reţine lungimea efectivă a şirului. Ocupă n+1 octeţi. La fel ca mai sus. Un câmp de acest tip reţine şiruri de cel mult 65535 de caractere. Doi octeţi reţin lungimea efectivă a şirului. Ocupă n+2 octeţi, unde n este numărul de caractere al şirului. Un câmp de acest tip reţine şiruri de cel mult 16.777.215 caractere. Trei octeţi reţin lungimea efectivă a şirului. Ocupă n+3 octeţi. Un câmp de acest tip reţine şiruri de cel mult 4.294.967.295 de caractere. Patru octeţi reţin lungimea efectivă a şirului. Ocupă n+4 octeţi. Baza va reţine un vector de şiruri de caractere. Un câmp de acest tip poate reţine un singur şir de caractere din vector. De altfel, coloana reţine indicele din şir. Vedeţi exemplul 2.5! Baza va reţine un vector de şiruri de caractere.. Un câmp de acest tip poate reţine unul sau mai multe şiruri de caractere din vector. De altfel, se reţine vectorul caracteristic al şirurilor. Vedeţi exemplul 2.6! Tabelul 2.1. Tipuri de date care reţin şiruri de caractere 56 Baze de date – curs introductiv Exemplul 2.5. Se creează tabelul ”tstA” care are două coloane: prima reţine un şir de cel mult 65.535 de caractere şi a doua, un şir din mulţimea şirurilor ”luni”, ”marti”, ”miercuri”. În tabel se inserează 3 linii şi, la sfârşit, tabelul este afişat. Să observăm că în cazul coloanelor de tip SET, dacă se încearcă memorarea unui şir inexistent în definiţia coloanei (în exemplu, şirul ”joi”), atunci acea coloană va reţine şirul vid. Figura 2.4. Tabelul “tstA” CREATE TABLE tstA ( v1 TEXT, v2 ENUM (‘luni’,‘marti’,‘miercuri’) ); INSERT INTO tstA VALUES (‘un sir’,’miercuri’); INSERT INTO tstA VALUES (‘un sir’,’joi’); INSERT INTO tstA VALUES (‘alt sir’,’luni’); SELECT * FROM tstA; Exemplul 2.6. Se creează tabelul ”tstB” care conţine două coloane c1, de lungime fixă de 3 caractere şi c2 de tip SET. Un element al coloanei poate reţine o submulţime a mulţimii {”dimineata”, ”pranz”, ”seara”}. Pentru a vedea cum se introduce submulţimea, analizaţi cele trei instrucţiuni INSERT. În figura alăturată puteţi observa tabelul rezultat în urma introducerii datelor. Observaţi faptul că, la ultima inserare, se încearcă introducerea unui şir, ”noaptea”, care nu este prezent în definiţia tipului coloanei. Nu se semnalează eroare, dar şirul nu este introdus. Figura 2.5. Tabelul “tstB” CREATE TABLE tstB (c1 char(3), c2 SET (‘dimineata’, ‘pranz’, ‘seara’) ); INSERT INSERT INSERT INSERT INTO INTO INTO INTO tstB tstB tstB tstB VALUES VALUES VALUES VALUES SELECT c1,c2 FROM tstB; (‘abc’,‘dimineata,seara’); (‘abc’,‘seara,pranz’); (‘abc’,‘seara’); (‘abc’,‘seara,noaptea’); Capitolul 2. Limbajul MySQL 57 2.5.2. Tipuri de date numerice Tipurile numerice se împart la rândul lor în tipuri întregi şi tipuri reale. O parte dintre tipurile întregi se găsesc în tabelul de mai jos: Tip TINYINT SMALLINT MEDIUMINT INT BIGINT Descriere Un câmp de acest tip ocupă 1 octet. Reţine numere întregi cuprinse în intervalul [-128,127], iar dacă este urmat de UNSIGNED reţine numere naturale cuprinse în intervalul [0,255]. Un câmp de acest tip ocupă 2 octeţi. Reţine numere întregi cuprinse în intervalul [-32768,32767], iar dacă este urmat de UNSIGNED, reţine numere naturale cuprinse în intervalul [0,65535]. Un câmp de acest tip ocupă 3 octeti. Reţine numere întregi cuprinse în intervalul [-8.388.608, 8.388.607], iar dacă este urmat de UNSIGNED, reţine numere naturale cuprinse în intervalul [0, 16.777.215]. Un câmp de acest tip ocupă 4 octet.i Reţine numere întregi cuprinse în intervalul [-2.147.483.648, 2.147.483.647], iar dacă este urmat de UNSIGNED, reţine numere naturale cuprinse în intervalul [0, 4294967295]. Un câmp de acest tip ocupă 8 octet.i Reţine numere întregi cuprinse în intervalul [-9.223.372.036.854.775.808, 9.223.372.036.854.775.807], iar dacă este urmat de UNSIGNED, reţine numere naturale cuprinse în intervalul [0, 18.446.744.073.709.551.615]. Tabelul 2.2. Tipuri de date numerice întregi Observaţie ! Uneori veţi folosi declaraţii de tip de genul int(4). Aceasta înseamnă că în coloană se rezervă automat o lăţime de 4 caractere pentru afişarea numerelor. De exemplu, dacă numărul reţinut este 1, atunci el este afişat ca bbb1, unde prin b am notat blank-ul. Aceasta nu afectează valorile care pot fi memorate. De exemplu, dacă dorim să memorăm numărul 12345, acesta va fi memorat şi se va afişa 12345. Dar, astfel este afectată lăţimea întregii coloane. Tipurile reale se găsesc în tabelul de mai jos: Tip FLOAT DOUBLE Descriere Ocupă 4 octeţi. Ocupă 8 octeţi. 58 Baze de date – curs introductiv DECIMAL(n,d) Numărul este stocat ca şir de caractere. Parametrul n reprezintă numărul de cifre nenule aflate înaintea virgulei, plus, dacă este cazul, 1, pentru semnul -, iar d reprezintă numărul de zecimale. Dacă numărul introdus are înaintea virgulei un număr de cifre +1 (pentru semn) mai mare decât n, acesta este trunchiat, iar dacă numărul de zecimale este mai mare ca d, atunci se reţin exact d zecimale. În acest din urmă caz, numărul se rotunjeşte la exact d zecimale. Tabelul 2.3. Tipuri de date numerice reale Exemplul 2.7. O coloană are tipul decimal(2,3): Se introduce 1.1 23.4567 1 -5 -15 Se memorează şi afişează 1.100 23.457 1.000 -5.000 -9.999 Tabelul 2.4. Cum se memorează şi se afişează diverse valori cu zecimale 2.5.3. Tipuri de date care reţin anul, data şi ora În tabelul următor aveţi tipurile care reţin anul, data şi ora: Tip YEAR TIME DATE DATETIME Descriere Un câmp de acest tip reţine ani. O dată se introduce ca şir de caractere. De exemplu, pentru anul 2001 se poate introduce ‘2001’ sau ‘1’, iar pentru anul 1989 se poate introduce ‘1989’ sau ‘89’. Un câmp de acest tip reţine ora din zi - se introduce ca şir de caractere sub forma ‘hh:mm:ss’. Un câmp de acest tip reţine data. Aceasta se introduce sub forma ‘yyyy-mm-dd’. De exemplu, ‘1999-11-24’. Un câmp de acest tip reţine date de forma dată şi oră. O dată se introduce ca şir de forma: ‘yyyy-mm-dd hh:mm:ss’ Tabelul 2.5. Tipuri de date care reţin anul, data şi ora Capitolul 2. Limbajul MySQL 59 2.6. Operatori utilizaţi în MySQL Începem prin a prezenta principalii operatori în ordinea crescătoare a priorităţii lor. Utilitatea operatorilor va fi evidenţiată în paragrafele următoare, deocamdată doar îi prezentăm: 1. ||, OR, XOR 2. &&, AND 3. BETWEEN, CASE WHEN, THEN, ELSE 4. ==,>=,<=,<,>,!=,<>,IS,IS,LIKE,IN 5.| 6. & 7. <<, >> 8. -,+ (operatori binari) 9. *, /, DIV, %, MOD 10. ^ 11. -,+ (operatori unari) 12. !, NOT Pentru a putea testa un operator puteţi utiliza instrucţiunea SELECT. De exemplu, dacă daţi comanda SELECT 1+1;, rezultatul se 1 vede în imaginea alăturată . Observaţie foarte importantă ! În MySQL există o valoare specială, numită Null. Semnificaţia ei este valoare necunoscută. Modul în care este posibil ca un câmp să reţină respectiva valoare va fi prezentat ulterior. Deocamdată reţineţi faptul că, dacă Null este un operand, atunci rezultatul oricărei operaţii care se efectuează cu Null este Null. Evident, dacă operandul este Null, atunci el este necunoscut, iar operaţia va avea rezultat necunoscut, indiferent de celălalt operand sau de tipul operaţiei. Exemplul 2.8. 1+NullNull sau Null=NullNull. Operatorii se împart, aşa cum suntem deja obişnuiţi din alte limbaje de programare, în mai multe grupe. Acestea sunt prezentate în continuare. 2.6.1. Operatori aritmetici Operatorii aritmetici acţionează asupra tipurilor numerice şi returnează o valoare de tip numeric. Pot fi analizaţi în Tabelul 2.6. În cele ce urmează, pentru a evidenţia rezultatul unei operaţii, vom utiliza caracterul ““. Spre exemplu, 1+1 2. 1 60 Baze de date – curs introductiv Operator Operaţie + adunare - scădere * / înmulţire împărţire DIV împărţire întreagă MOD sau % restul împărţirii întregi -, + Minus şi plus, operatori unari Rezultat 2+3 1; 2 - 3 -1; 3.5*26 5 / 2 2.5; 5 DIV 2=2; -5 DIV 2 =-2 5 MOD 21 -5 MOD 2 -1 5 % 2 1; -5 % 2 -1 --11 Tabelul 2.6. Operatori aritmetici 2.6.2. Operatori de comparare Pot fi comparate două valori numerice sau două şiruri. Şirurile se compară lexicografic (ordinea din dicţionar) şi nu se face distincţie între literele mari şi cele mici. Rezultatul este 1 pentru adevărat şi 0 pentru fals. Operaţie Operator < <= > >= = <> sau != mai mic. mai mic sau egal mai mare mai mare sau egal egalitate diferit Rezultat 2 < 1 0; 1 <2 0; ‘ab’<b 1; 2 <=2 1; 3>2 1 ‘b’>’ab’ 1 7>=7 1; 7 XOR 8 0; 7=71 ‘un’=’UN’1 1<>21; 1!=21; 1<>10’ 1!=10; Tabelul 2.7. Operatori de comparare Observaţie ! Se pot compara şi date de tipul TIME, DATE, etc. În fapt, o astfel de comparare este lexicografică. Vedeţi exemplul 2.9! Capitolul 2. Limbajul MySQL 61 Exemplul 2.9. Analizaţi operaţiile de comparare de mai jos: ‘1:12:3’<‘2:00:00’1, ‘1999-12-29’= ‘1999-12-29’1. ‘1:12:3’<‘13:00:00’0 - compararea s-a facut lexicografic, motiv pentru care rezultatul este eronat. ‘01:12:3’<‘13:00:00’1 pentru ca rezultatul să fie corect, chiar dacă compararea este lexicografică, trebuie ca orele, minutele, secundele să fie cu acelaşi număr de cifre. De fapt, atunci când se memorează în baza de date o astfel de valoare, operaţia este făcută automat. Acesta este motivul pentru care comparările efectuate cu date memorate în baza de date sunt corecte. 2.6.3. Operatori logici În MySQL se consideră 2 valori logice: false, reprezentat de 0 şi true reprezentat de 1 şi de orice valoare diferită de 0. Operatorii logici acţionează asupra valorilor logice şi returnează 1, pentru adevărat şi 0, pentru fals. Operator Operaţie || sau OR sau logic, dacă ambii operanzi sunt 0, rezultatul este 0, altfel rezultatul este 1. && sau AND şi, dacă ambii operanzi sunt diferiţi de 0, rezultatul este 1, altfel rezultatul este 0. NOT negare, dacă operandul este 0, rezultatul este 1, altfel rezultatul este 0. XOR sau exclusiv, dacă un operand este 0 şi altul diferit de 0, rezultatul este 1, altfel, rezultatul este 0. Rezultat 2 || 1 1; 0 || 1 1; 0 || 0 0; 2 && 1 1; 2 && 0 0; 0 && 0 0; NOT 30; NOT 1=0; NOT 0=1; 7 XOR 0 1; 7 XOR 8 0; 0 XOR 0 0; Tabelul 2.8. Operatori logici 2.6.4. Operatori logici pe biţi Se aplică tipurilor întregi şi acţionează asupra tuturor biţilor aflaţi pe poziţii corespondente. Operator | & Operaţie sau pe biţi, dacă ambii biţi sunt 0, rezultatul este 0, altfel rezultatul este 1. şi pe biţi, dacă ambii biţi sunt 1, rezultatul este 1, altfel rezultatul este 0. Rezultat 2 | 1 3 2 & 1 0 62 Baze de date – curs introductiv ^ ~ sau exclusiv pe biţi, dacă biţii sunt diferiţi, rezultatul este 1, altfel rezultatul este 0. negare, un bit 0 devine 1, un bit 1 devine 0. 10^39 (1010 ^00111001) ~0...011...10 Tabelul 2.9. Operatori logici pe biţi 2.6.5. Operatori de deplasare Se aplică tipurilor întregi şi acţionează asupra tuturor biţilor aflaţi pe poziţii corespondente. Operator << >> Operaţie deplasare stânga, deplasare către stânga a biţilor operandului din stânga cu k poziţii, unde k este operandul din dreapta. Poziţiile rămase libere se completează cu 0. deplasare dreapta, deplasare către dreapta a biţilor operandului din stânga cu k poziţii, unde k este operandul din dreapta. Rezultat 1<<24 (1 trece în 100) 4>>21 (100 trece în 1) Tabelul 2.10. Operatori de deplasare 2.6.6. Alţi operatori Operatorii IS NULL, IS NOT NULL testează dacă o valoare este sau nu NULL (sunt singurii operatori prin care se testează acest lucru)! Exemplul 2.10. Analizaţi operaţiile următoare: 1 IS NULL 0;, NULL IS NULL 1;, 1 IS NOT NULL 1;, NULL IS NOT NULL 0;. Operatorii IN, NOT IN testează apartenenţa unei valori la o mulţime. Valoarea poate fi de formă numerică sau de un tip care reţine un şir de caractere. Importanţa mare pe care o au aceşti operatori va rezulta studiind paragrafele următoare. Exemplul 2.11. Analizaţi operaţiile următoare: 1 IN(1,2,3,4) 1;, 5 IN(1,2,3,4) 0;, ‘1sir’ IN (‘1sir’,’2sir’,’3sir’) 1;, ‘4sir’ IN (‘1sir’,’2sir’,’3sir’) 0;. Operatorul LIKE, NOT testează dacă un şir de caractere îndeplineşte anumite condiţii: dacă este prefixat sau nu de un anumit subşir, dacă este postfixat sau nu de acesta sau dacă subşirul se găseşte în interior. Pentru substituirea unui număr neprecizat de caractere necunoscute se utilizează caracterul ”%”, iar pentru un singur caracter neprecizat se utilizează caracterul ”_”. Capitolul 2. Limbajul MySQL 63 Exemplul 2.12. Analizaţi operaţiile următoare: ‘popescu’ LIKE ‘pop%’1; (‘popescu’ este prefixat de subşirul ‘pop’), ‘ponescu’ LIKE ‘pop%’0; (‘ponescu’ nu este prefixat de subşirul ‘pop’), ‘ionescu’ LIKE ‘%escu’1 (‘ionescu’ este postfixat de subşirul ‘escu’). Operatorul BETWEEN min AND max testează dacă o valoare se găseşte între o valoare minimă şi una maximă. În caz afirmativ returnează 1, altfel, 0. Se poate aplica şi pentru date care reţin şiruri de caractere (ordinea lexicografică), dar şi pentru datele de timp. Exemplul 2.13. Analizaţi operaţiile următoare: 1 BETWEEN 0 AND 4 1;, ‘mama’ BETWEEN ‘min’ AND ‘lin’ 1;, 2 BETWEEN 2 AND 2 1;, 1 BETWEEN 2 AND 4 0;, ‘1999-10-31’ BETWEEN ‘1989-12-10’ AND ‘2000-01-01’1. Operatorul CASE WHEN THEN ELSE. Poate fi aplicat în două forme: Forma 1. Se evaluează v şi dacă produce valoarea vi se returnează valoarea vali, iar dacă nicio condiţie vi nu este îndeplinită, se returnează valoarea valn+1: CASE v WHEN v1 THEN val1 [WHEN v2 THEN val2] ... [WHEN vn THEN valn] [ELSE valn+1] END Exemplul 2.14. Se afişează ‘doi’: CASE 2 WHEN 1 THEN ‘unu’ WHEN 2 THEN ‘doi’ WHEN 3 THEN ‘trei’ ELSE ‘alta valoare’ END; Forma 2. În această formă, dacă este îndeplinită condiţia i, se returnează valoarea vi,. Dacă niciuna din condiţiile vi nu este îndeplinită, se returnează valoarea vn+1: CASE WHEN conditie1 THEN v1 [WHEN conditie2 THEN v2] ... [WHEN conditien THEN vn] ELSE vn+1 END 64 Baze de date – curs introductiv Exemplul 2.14. Se afişează ‘adevarat’: CASE WHEN 1<2 THEN ‘adevarat’ ELSE “nu se poate’ END; 2.7. Funcţii MySQL În MySQL există o serie de funcţii predefinite. Întrucât astfel de funcţii au fost prezentate pentru limbajul studiat (Pascal sau C/C++), în acest paragraf vor fi tratate pe scurt. Pot fi testate prin utilizarea instrucţiunii SELECT. 2.7.1. Câteva funcţii matematice Funcţie ABS(x) Descriere Modul de x. CEIL(x) Cel mai mic întreg mai mare sau egal cu x. FLOOR(X) Cel mai mare întreg mai mic sau egal cu x. EXP(x) LOG(b,x) ex Logaritm în baza b din x. PI() POW(x,y) xy ROUND(x) Cel mai apropiat întreg de x. SIN(x) COS(x) Sinus de x. Cosinus de x. SIGN(x) Semnul lui x: -1, dacă x<0, 0, dacă x=0, 1 dacă x>0. SQRT(x) Radical din x. Rezultat ABS(-2) 2; CEIL(1.7) 2; CEIL(2)2; CEIL(-1.7)-1; FLOOR(-1.7) -2 FLOOR(2) 2; FLOOR(1.7) 1; EXP(1) 2.7182... LOG(2,4) 2; PI()3.141593 POW(2,3)8 ROUND(2.3)2; ROUND(2.7)3; ROUND(-2.8)-3; SIN(PI()/2)1; COS(0) 1 SIGN(2) 1; SIGN(0) 0; SIGN(-2) -1; SQRT(4) 2; Tabelul 2.11. Funcţii matematice Capitolul 2. Limbajul MySQL 65 2.7.2. Câteva funcţii care lucrează asupra şirurilor de caractere Funcţie Descriere LENGTH(x) Lungimea şirului x. CONCAT(x1 ,x2,..) Concatenează şirurile x1,x2,…. INSTR(x,y) SUBSTRING(x, p,l) RTRIM(x) LTRIM(x) TRIM(x) UPPER(x) LOWER(x) FIND_IN SET (x,’s1,s2, ..sn’) FORMAT(x,d) STRCMP(x,y) Caută dacă y este subşir pentru x. În caz afirmativ, returnează indicele de început (indicii încep cu 1), altfel returnează 0. Returnează subşirul din x care începe în poziţia p şi are lungimea l. Returnează şirul fără blank-urile din dreapta. Returnează şirul fără blank-urile din stânga. Returnează şirul fără blank-urile din stânga şi din dreapta. Returnează şirul scris cu litere mari. Returnează şirul scris cu litere mici. Returnează indicele apariţiei şirului x în şirul de şiruri ‘s1,s2,...,sn’. Dacă x nu este găsit, returnează 0. Converteşte numărul real x la un şir cu d zecimale. Pentru ultima zecimală se face, dacă este cazul, rotunjirea. Compară lexicografic şirurile x şi y. Dacă x<y returnează -1, dacă x=y returnează 1, iar dacă x>y, returnează 1. Rezultat LENGTH(‘abc’) 3; CONCAT(‘ab’,’c’) ‘abc’ INSTR(‘abc’,’b’)2 INSTR(‘abc’,’d’)0 SUBSTRING(‘abc’,2,1) ’b’ RTRIM(‘un LTRIM(‘ ‘)’un’ un’) ’un’ TRIM(‘ un ‘)’un’ UPPER(‘Un’) ’UN’ LOWER(‘Un’) ’un’ FIND_IN_SET (‘b’,’a,b,c’) 2 FORMAT(1.789,2) 1.79 STRCMP(‘ma’,’mama’) -1 Tabelul 2.12. Funcţii care lucrează asupra şirurilor de caractere 2.7.3. Câteva funcţii care lucrează asupra datei şi orei Funcţie NOW() Descriere Returnează data şi ora sub forma yyyy-mm-dd hh:mm:ss. Rezultat 66 Baze de date – curs introductiv Dacă x este de tipul DATA, afişează anul extras din x. YEAR(x) DAY(X) Ca mai sus, extrage ziua. MONTH(x) Ca mai sus, extrage luna. TIME(x) Ca mai sus, extrage ora. HOUR(x) Ca mai sus, extrage ora ca număr între 0 şi 23. MINUTE Ca mai sus, extrage minutul. DATEDIFF (x,y) DATE_ADD (data,INTERV AL,nr, x) DATE_SUB (data,INTERV AL,nr, x) x şi y sunt de tip DATETIME sau DATE. Calculează diferenţa în zile dintre x şi y. Calculează data care se obţine dacă la valoarea data se adună nr, unde x specifică ce este nr: YEAR - ani, DAY - zile, MONTH - luni. La fel ca mai sus, numai că se obţine data diferenţă. YEAR(NOW()) returnează anul curent. DAY(NOW())returnează ziua curentă. MONTH(NOW()) returnează luna curentă. TIME(NOW()) returnează ora curentă. HOUR(NOW()) returnează ora curentă ca număr între 0 şi 23. MINUTE(NOW()) returnează minutul curent DATEDIFF(NOW(), ’1989-12-21’) DATE_ADD(NOW(), INTERVAL, 10 DAY) Se obţine data care va fi peste 10 zile. DATE_SUB(NOW(), INTERVAL, 10 DAY) Se obţine data care a fost acum 10 zile. Tabelul 2.13. Funcţii care lucrează asupra datei şi orei 2.7.4. Două funcţii speciale Funcţia IF are forma de mai jos, unde expresie1 este de un tip întreg. Dacă expresie1 este nenulă, atunci se returnează expresie2, altfel se returnează expresie3. Atenţie: dacă expresie1 este de tip real, se rotunjeşte la cel mai apropiat întreg. Dacă, de exemplu, acesta este 0.41, atunci se rotunjeşte la 0, prin urmare, rezultatul va fi altul... IF(expresie1,expresie2,expresie3) Exemplul 2.15. Analizaţi exemplele următoare: IF (1>2, ‘adevarat’, ’fals’) ‘adevarat’, IF (0.45, ‘nu’,’da’) ’da’;. Funcţia IFNULL are forma de mai jos. Dacă expresie1 nu este NULL, returnează expresie1,, altfel returnează expresie2. IFNULL(expresie1, expresie2) Capitolul 2. Limbajul MySQL 67 Exemplul 2.16. Analizaţi exemplele următoare: IFNULL (0,‘este null’) 0, IFNULL (NULL,‘este NULL’) ’este NULL’. 2.8. Afişarea coloanelor care rezultă în urma unui calcul O regulă elementară în crearea şi utilizarea bazelor de date spune că acestea vor reţine numai date care nu rezultă în urma unui calcul. Nerespectarea ei conduce la baze de date care ocupă mult spaţiu. Acum vom învăţa să afişăm datele care rezultă în urma unui calcul. Exemplul 2.17. Priviţi tabelul din Fig. 2.6, numit ”prod”, care reţine produsele existente într-un depozit. Pentru fiecare produs se cunoaşte cantitatea (”cant”) şi preţul unitar (”pret_unitar”), preţul unui exemplar din produsul respectiv. Să presupunem că dorim ca, pe lângă informaţiile afişate, să aflăm valoarea totală pentru fiecare produs în parte. Valoarea se obţine ca produs între cantitate şi preţul unitar. Figura 2.6. Tabelul “prod” Pentru aceasta, atunci când afişăm tabelul, vom crea o nouă coloană sub forma: expresie AS nume_coloana, unde expresie calculează valorile din coloana respectivă, iar ”nume_coloana” va fi numele ei. Iată cum se afişează noul tabel, prezentat mai jos: Figura 2.7. Tabelul rezultat SELECT den, cant, pret_unitar, cant*pret_unitar AS valoare FROM prod; Exemplul 2.18. Se dă tabelul alăturat, numit ”persoane”. Se cere să se separe numele de prenume şi să se afişeze datele ca mai jos: Figura 2.9. Tabelul dorit Figura 2.8. Tabelul “persoane” 68 Baze de date – curs introductiv Rezolvare. Se utilizează funcţii care operează asupra şirurilor de caractere: SELECT SUBSTRING(nume,1,INSTR(nume,’ ‘)-1) AS prenume SUBSTRING(nume, 1+INSTR(nume,’ ‘),LENGTH(nume)) AS nume FROM persoane; Exemplul 2.19. Se consideră tabelul din Fig. 2.10, numit ”pers”, care cuprinde numele şi data naşterii pentru două persoane: Figura 2.10. Tabelul “pers” Se cere să se afişeze numele şi vârsta fiecărei persoane. Figura 2.11. Tabelul dorit Rezolvare. Se utilizează funcţii care operează asupra câmpurilor de tip DATE şi DATETIME. SELECT nume, YEAR(NOW())-YEAR(data_n) AS varsta FROM pers; Exemplul 2.20. Se consideră un tabel, numit ”numar”, alcătuit dintr-o singură coloană, c de tip INT. Se cere să se listeze acea coloană şi o alta, numită ”paritate”, care să conţină două valori: ‘par’ dacă numărul din c este par şi ‘impar’, în caz contrar (vezi figura alăturată). SELECT c, IF(C MOD 2, ‘impar’,’par’) AS paritate FROM numar; Figura 2.12. Tabelul rezultat 2.9. Valoarea NULL În MySQL, aşa cum s-a arătat, există o valoare specială, numită NULL. Semnificaţia ei este “nicio valoare“. Atunci când definim o coloană se poate trece specificatorul NULL. Oricum, se asumă implicit, NULL. De altfel, acest lucru poate fi testat. Creaţi un tabel, de exemplu, cu o coloană de un anumit tip, fără a specifica NULL sau NOT NULL şi apoi daţi comanda SHOW COLUMNS FROM nume_tabel; şi analizaţi tabelul afişat! Exerciţiu! Capitolul 2. Limbajul MySQL 69 a) Atunci când o coloană are trecut specificatorul NULL sau nu s-a trecut nimic şi la introducerea unui rând, pentru coloana respectivă, nu este trecută o valoare pentru acel câmp, acolo se va memora valoarea specială NULL. b) Atunci când o coloană are trecut specificatorul NOT NULL şi la introducerea unui rând, în coloana respectivă, nu este trecută o valoare, acolo se va memora: - 0, dacă coloana este de tip numeric; - şirul vid, dacă coloana este de un tip care permite reţinerea de şiruri; - indicele 0 din şir, dacă coloana este de tip ENUM şi aceasta are ca efect afişarea primului şir al enumerării; - mulţimea vidă, dacă coloana este de tip SET. Analizaţi exemplele următoare şi verificaţi, de fiecare dată, tabelul afişat. Exemplul 2.21. Rulaţi secvenţa de mai jos şi se va afişa tabelul alăturat: CREATE TABLE testnull (c1 INT NULL, c2 INT NOT NULL); INSERT INTO testnull (c2) VALUES (10); INSERT INTO testnull (c1) VALUES (7); Figura 2.13. Tabelul rezultat SELECT * FROM testnull; Exemplul 2.22. Se creează tabelul alăturat, în care coloanele reţin şiruri de caractere: CREATE TABLE testnull1 (c1 TEXT NULL, c2 TEXT NOT NULL); INSERT INTO testnull1(c1) VALUES (‘Un sir’); INSERT INTO testnull1(c2) VALUES (‘Alt sir’); Figura 2.14. Tabelul rezultat SELECT * FROM testnull1; Exemplul 2.23. Se creează tabelul din figura alăturată, în care coloanele reţin date de tip ENUM: CREATE TABLE testnull2 ( c1 ENUM(‘sir1’,’sir2’,’sir3’) NULL, c2 ENUM(‘sir1’,’sir2’,’sir3’) NOT NULL); INSERT INTO testnull2(c1) VALUES (‘sir2’); INSERT INTO testnull2(c2) VALUES (‘sir1’); SELECT * FROM testnull2; Figura 2.15. Tabelul rezultat 70 Baze de date – curs introductiv Exemplul 2.24. Se creează tabelul alăturat, în care coloanele reţin date de tip SET. CREATE TABLE testnull3 ( c1 SET(‘sir1’,’sir2’,’sir3’) NULL, c2 SET(‘sir1’,’sir2’,’sir3’) NOT NULL); INSERT INTO testnull2(c1) VALUES (‘sir1,sir2’); INSERT INTO testnull2(c2) VALUES (‘sir3’); Figura 2.16. Tabelul rezultat SELECT * FROM testnull3; Aşa cum am arătat, semnificaţia lui NULL este “nicio valoare”. De asemenea, am învăţat că o operaţie în care un operand este NULL, va avea rezultatul NULL. Acest fapt este util, pentru că, în caz contrar s-ar putea ajunge la rezultate false. Exemplul 2.25. Tabelul alăturat “elev“ prezintă numele, nota la engleză şi nota la franceză pentru doi elevi. Unul dintre ei, nu are notă la franceză (NULL). Figura 2.17. Tabelul “elev” Se cere să se calculeze mediile celor doi elevi. Scriem interogarea: SELECT nume, (engleza+franceza)/2 AS media; Figura 2.18. Rezultatul interogării Să observăm că pentru elevul căruia nu i se cunoaşte nota la franceză, nu i se cunoaşte nici media. Logic vorbind, aşa este corect. Dacă nu se cunoaşte o notă, nu se cunoaşte nici media deoarece n-ar fi normal ca pentru acest elev să se afişeze media 10... 2.10. Valori implicite În practică, nu întotdeauna se cunosc datele în totalitate. În astfel de cazuri, atunci când pentru un anumit rând nu se cunoaşte un câmp, fie acesta va reţine NULL, fie va reţine o valoare implicită, convenabil aleasă. De exemplu, dacă la un examen de admitere un elev nu se prezintă la o probă, se poate lua decizia ca, la acea probă, elevul să primească nota 1. Pentru ca un câmp, în absenţa datelor să aibă o valoare implicită, se foloseşte specificatorul DEFAULT: DEFAULT valoare_implicita Facem observaţia că pot lua valori implicite câmpurile cu lungime fixă. De exemplu, un câmp declarat CHAR(20) poate lua o valoare implicită, dar unul declarat TEXT nu poate lua o astfel de valoare. Capitolul 2. Limbajul MySQL 71 Exemplul 2.26. În tabelul următor, se reţin numele şi oraşul de domiciliu pentru mai multe persoane: Figura 2.19. Tabelul propus Dacă oraşul de domiciliu nu este cunoscut, se preferă, în locul valorii NULL, să fie afişat şirul ‘necunoscut’: CREATE TABLE pers_oras (nume CHAR (25), domiciliu CHAR(30) DEFAULT ‘necunoscut’); INSERT into pers_oras (nume) VALUES (‘Gulagea Constantin’); INSERT into pers_oras VALUES (‘Ionescu Mihai’, ‘Galati’ ); SELECT * FROM pers_oras; 2.11. Cheie primară şi cheie unică A) Cheia primară este constituită dintr-un câmp (sau mai multe câmpuri) şi trebuie să îndeplinească simultan condiţiile: a) Valorile reţinute de coloana care alcătuieşte cheia primară trebuie să fie distincte. În cazul în care cheia este alcătuită din mai multe coloane, pentru a avea două chei distincte, este necesar ca acestea să fie diferite pentru cel puţin o coloană dintre ele. Tentativa de a înscrie în tabel o înregistrare care are cheia primară identică cu alta, existentă în tabel, este sancţionată cu eroare. b) Câmpul (câmpurile) care alcătuieşte (alcătuiesc) cheia primară trebuie să aibă o lungime fixă. De exemplu, nu poate fi cheie primară un câmp de tip TEXT. Raţiunea existenţei cheii primare este dată de accesul foarte rapid la înregistrarea de cheie dată. Trebuie ştiut că înregistrările unui tabel se reţin în ordinea introducerii lor. Atunci când, de exemplu, un câmp este declarat cheie primară, se construieşte un tabel auxiliar, invizibil pentru utilizator, în care pentru fiecare valoare din câmpul cheie primară se reţine poziţia rândului care are acea cheie. În acel tabel, cheile sunt în ordine crescătoare (dacă este vorba de şiruri de caractere, acestea se reţin în ordine lexicografică, ca mai jos). Pornind de la cheie, în tabelul “invizibil” se identifică rapid rândul cu acea cheie. În acest caz, se foloseşte căutarea binară, algoritm studiat la informatică (revedeţi) şi, după cum ştiţi, o astfel de căutare se face în O(log(n)). Din acel rând se află poziţia înregistrării cu acea cheie din tabelul iniţial. De aici, accesul la înregistrare este imediat. Căutarea clasică se efectuează în O(n). 72 Baze de date – curs introductiv Exemplul 2.27. Presupunem că tabelul principal are drept cheie primară câmpul "Nume": Tabel principal Nume Ion Maria Ana Cristian Mihai Vârstă 15 17 21 12 18 Înălţime (cm) 173 159 159 171 182 Tabel “invizibil” Nume Ana Cristian Ion Maria Mihai Poziţie 3 4 1 2 5 Pentru a preciza că o anumită coloană este cheie primară se foloseşte, atunci când se descrie coloana, specificatorul PRIMARY KEY. Exemplul 2.28. Se creează un tabel în care prima coloană este cheie primară: CREATE TABLE ex_cheie_primara_un_camp (c1 INT PRIMARY KEY, c2 TEXT ); Dacă cheia primară este alcătuită din mai multe câmpuri, după descrierea coloanelor, se utilizează specificatorul PRIMARY KEY sub forma: PRIMARY KEY(nume_col1, nume_col2,...) Exemplul 2.29. În tabelul următor cu trei coloane, coloanele c1 şi c2 alcătuiesc cheia primară: CREATE TABLE cheie_primara_doua_campuri (c1 INT, c2 CHAR(10), c3 TEXT, PRIMARY KEY(c1,c2) ); B) Cheie unică - se poate cere ca o coloană (sau mai multe) să conţină numai valori distincte. Pentru aceasta se va utiliza specificatorul UNIQUE KEY. În acest caz, dacă în coloana respectivă se va încerca o inserare cu o valoare care există deja, se va semnala eroare. Observaţie ! Şi în acest caz, nu este permis să solicităm valori distincte pentru o coloană de un tip de lungime variabilă (cum ar fi, de exemplu, tipul TEXT). Exemplul 2.30. Se creează un tabel în care în ambele coloane vor fi numai valori distincte: CREATE TABLE distincte ( c1 INT UNIQUE KEY, c2 char(10) UNIQUE KEY ); Capitolul 2. Limbajul MySQL 73 2.12. Autoincrementare Cu siguranţă, aţi văzut o mulţime de tabele în care rândurile sunt numerotate. Mai jos, aveţi un astfel de tabel, cu numele unor persoane: Figura 2.20. Tabelul cu număr de ordine Este incomod, ca la introducerea fiecărui rând să se specifice numărul de ordine pentru rândul respectiv. Din acest motiv, s-a pus la dispoziţia clienţilor un mecanism prin care incrementarea se face automat. Pentru ca o coloană să reţină numerele de ordine al rândurilor respective, este necesar ca, la definirea ei, să se utilizeze specificatorul AUTO_INCREMENT. Pentru a putea utiliza acest specificator, este necesar să fie îndeplinite condiţiile: a) coloana să fie de un tip întreg; b) coloana să reţină numai valori distincte - pentru a determina aceasta, coloana trebuie să fie declarată ca fiind cheie primară sau cu cheie unică. Exemplul 2.31. Iată cum am creat tabelul din Fig. 2.20, numit ”auto”: CREATE TABLE auto (nr_ord INT UNIQUE KEY AUTO_INCREMENT, nume CHAR(20) ); INSERT INTO auto (nume) VALUES(‘Marius Oprean’); INSERT INTO auto (nume) VALUES(‘Mihai Minca’); Există şi posibilitatea să se introducă valori dorite în coloana declarată cu autoincrementare. În acest caz, la următoarele introduceri, valorile implicite vor fi obţinute pornind de la valoarea introdusă. Exemplul 2.32. Analizaţi tabelul alăturat, obţinut din cel anterior, la care s-au mai făcut inserările de mai jos: Figura 2.21. Tabel modificat INSERT INTO auto VALUES(7,‘Doru Cran’); INSERT INTO auto (nume) VALUES(‘Dan Bitu’); 74 Baze de date – curs introductiv 2.13. Sortarea datelor Uneori este necesar ca datele să fie afişate sortate după valorile unei coloane sau după valorile mai multor coloane. Dacă acestea sunt de tip şir de caractere, ele pot fi sortate în ordine lexicografică. Alăturat aveţi un tabel, numit “sortare”, pe care vom exersa modul de afişare a datelor sortate după anumite criterii. Figura 2.22. Tabel “sortare” Exemplul 2.33. Datele sunt afişate sortate crescător, după valorile existente în coloana ”nume”. Să observăm că sortarea crescătoare este cea implicită, pentru a o obţine nu este necesar să o specificăm: SELECT * FROM sortare ORDER BY nume; Figura 2.23. Tabelul sortat crescător Exemplul 2.34. Pentru a sorta datele descrescător, se foloseşte specificatorul DESC. Mai jos, am sortat datele descrescător după valorile câmpului ”data_n” (data naşterii): SELECT * FROM sortare ORDER BY data_n DESC; Figura 2.24. Tabelul sortat descrescător după data naşterii Există posibilitatea să sortăm datele (crescător sau descrescător) după valorile existente în mai multe coloane. Practic, mai întâi se sortează datele după prima coloană specificată, apoi, păstrând această ordine, după a doua coloană, ş.a.m.d. Exemplul 2.35. Datele sunt sortate crescător după valorile existente în coloana ”nume”, apoi, păstrând această ordine, descrescător după valorile din coloana ”data_n”. SELECT * FROM sortare ORDER BY nume, data_n DESC; Figura 2.25. Tabelul sortat crescător după ambele coloane Observaţie ! Avem două persoane cu numele ”Mihai”. Mai întâi este afişată persoana născută în 1980, apoi cea născută în 1975 (sortate descrescător după câmpul ”data_n”). Capitolul 2. Limbajul MySQL 75 Exemplul 2.36. Există posibilitatea să afişăm numai un număr dorit de rânduri. Pentru aceasta se foloseşte clauza LIMIT, ca mai jos: SELECT * FROM sortare ORDER BY nume LIMIT 3; Figura 2.26. Afişarea unor rânduri din tabel 2.14. Filtrarea datelor Uneori nu suntem interesaţi să fie afişate toate liniile tabelului, ci numai unele, care îndeplinesc anumite condiţii. O astfel de operaţie se numeşte filtrare. Pentru a realiza operaţia de filtrare se ataşează instrucţiunii SELECT, la sfârşit, clauza WHERE condiţie. În acest fel, sunt selectate numai liniile pentru care condiţia din clauza WHERE este îndeplinită. În cazul în care datele se cer sortate, clauza ORDER BY se va trece după WHERE. Exemplul 2.37. Se consideră un tabel precum cel de mai jos, în care se prezintă situaţia şcolară a elevilor unei clase: CREATE TABLE elevi ( nume TEXT, matematica INT, engleza INT, informatica INT); Figura 2.27. Tabelul “elevi” 1. Afişaţi numele elevilor care au la matematică note mai mari sau egale cu 8. SELECT nume FROM elevi WHERE matematica>=8; Figura 2.28. Exemplu de filtrare 2. La fel ca mai sus, numai că se cere ca numele elevilor să fie afişate în ordine alfabetică. SELECT nume FROM elevi WHERE matematica>=8 ORDER BY nume; Figura 2.29. Exemplu de filtrare 76 Baze de date – curs introductiv 3. Se cere să se afişeze numele elevilor care au promovat la fiecare materie şi media generală pe care aceştia o au. SELECT nume, (matematica+engleza+informatica)/3 AS media FROM elevi WHERE matematica>4 AND engleza>4 AND informatica>4; Figura 2.30. Exemplu de filtrare Observaţie ! Filtrarea datelor este un caz considerat particular al unui concept cu mult mai general, interogarea! De obicei, interogăm o bază de date cu ajutorul instrucţiunii SELECT. 2.15. Actualizări într-un tabel În general, prin actualizarea unui tabel înţelegem modificarea datelor reţinute de acesta şi/sau modificarea structurii sau numelui tabelului. A) Prin actualizarea datelor dintr-un tabel înţelegem operaţii precum inserarea unor linii, modificarea valorilor unor câmpuri şi ştergerea unor rânduri. A1) Pentru inserare, putem utiliza instrucţiunile de mai jos: 1. INSERT INTO nume_tabel [(nume_col1, nume_col2..) VALUES(expresie1, expresie2...) Această instrucţiune a fost deja prezentată, nu vom mai reveni asupra ei. 2. INSERT INTO [DISTINCT} nume_tabel [(nume_col1, nume_col2..) SELECT .... Într-un tabel se pot insera linii dintr-un alt tabel. Pentru aceasta se foloseşte instrucţiunea de mai sus (vezi Exemplul 2.38, pct. 1). Dacă se doreşte inserarea numai a liniilor distincte din tabelul sursă, utilizaţi specificatorul DISTINCT. A2) Pentru a modifica valorile reţinute de un câmp sau mai multe, se utilizează instrucţiunea de mai jos (vezi Exemplul 2.38, pct. 2): UPDATE nume_tabel SET coloana1=expresie1, ... coloanak=expresiek [WHERE conditie]; Capitolul 2. Limbajul MySQL 77 În urma executării acestei comenzi, pentru fiecare rând din tabel care îndeplineşte condiţia din WHERE, se actualizează coloanele indicate de SET cu expresiile corespunzătoare. Observaţie ! În absenţa clauzei WHERE sunt afectate toate rândurile din tabel. A3) Ştergerea unui rând sau a mai multor rânduri se face cu instrucţiunea de mai jos (vezi Exemplul 2.38, pct. 3 şi pct. 4): DELETE FROM nume_tabel [WHERE conditie] Observaţie ! În absenţa clauzei WHERE sunt şterse toate rândurile din tabel. B) Modificări ale numelui tabelului sau a structurii acestuia. B1) Pentru a modifica numele unui tabel se utilizează instrucţiunea: RENAME TABLE nume_vechi TO nume_nou B2) Pentru ştergerea unei coloane, chiar dacă conţine date, se utilizează instrucţiunea: ALTER TABLE nume_tabel DROP COLUMN nume_coloana B3) Pentru adăugarea unei coloane cu un tip de date, se utilizează instrucţiunea: ALTER TABLE nume_tabel ADD nume_col tip; Exemplul 2.38. Fie tabelele de mai jos, ambele cu aceleaşi coloane, de acelaşi tip: Figura 2.31. Tabelul “prs” Figura 2.32. Tabelul “prs1” 1. Se cere să se insereze în tabelul ”prs”, acele persoane din ”prs1” care sunt din ”Craiova”. Alăturat, vedeţi tabelul ”prs” actualizat. INSERT INTO prs (nume, oras) SELECT nume, oras FROM prs1 WHERE oras=’craiova’; Figura 2.33. Tabelul “prs” actualizat 78 Baze de date – curs introductiv 2. S-a observat că Ioana şi Florin sunt de fapt din braila. Corectaţi datele din tabel! Alăturat aveţi afişat tabelul cu datele actualizate. UPDATE prs SET oras=’braila’ WHERE nume IN (‘ioana’, ‘florin’); Figura 2.34. Tabelul actualizat 3. Ştergeţi din tabelul ”prs” înregistrarea în care numele este Mirela (vezi figura alăturată): DELETE FROM prs WHERE nume=’Mirela’; Figura 2.35. 4. Creaţi un nou tabel ”prs2”, cu aceeaşi structură ca şi ”prs”, care să conţină numai persoanele din ”prs” care sunt din Braila şi Ploiesti. CREATE TABLE prs2 AS SELECT nume, oras FROM prs WHERE nume IN (‘braila’, ‘ploiesti’); Figura 2.36. Tabelul nou “prs2” Exemplul 2.39. În tabelul alăturat, ”prs”, există mai multe înregistrări identice. Se cere să se elimine duplicatele. a) Creăm un alt tabel, de manevră, numit ”mand”, care conţine aceleaşi înregistrări cu ”prs”, doar că au fost eliminate duplicatele: CREATE mand AS SELECT distinct nume, oras FROM prs; b) Ştergem tabelul ”prs”: DROP TABLE prs; Figura 2.37. Tabelul “prs” c) Redenumim tabelul ”mand” ca ”prs” şi-l afişăm pe ”prs”: RENAME TABLE mans TO PRD; SELECT * FROM prs; Figura 2.38. Tabelul rezultat Observaţie! Mare atenţie când efectuaţi astfel de operaţii. În cazurile reale, în care tabelele au mii de rânduri, se pot produce pierderi de date. Din acest motiv, înainte de a actualiza un tabel, este bine să-i creaţi o copie de siguranţă pe care, după ce aţi rezolvat problema, s-o ştergeţi. Capitolul 2. Limbajul MySQL 79 2.16. Funcţii agregate Toate calculele făcute până în prezent au avut ca operanzi doar câmpurile unui aceluiaşi rând. Întrebarea este: se pot efectua calcule cu valorile reţinute de o coloană? Răspunsul este afirmativ. Pentru astfel de calcule se utilizează aşa-numitele “funcţii agregate”. În cele ce urmează, prezentăm aceste funcţii, iar pentru exemplificare utilizăm tabelul ”elevi”, tabel pe care l-am creat în paragraful 2.14. Funcţiile agregate sunt prezentate în tabelul de mai jos: Nume funcţie COUNT() MIN() MAX() SUM() AVG() Ce realizează Sub forma COUNT(*), afişează numărul de linii ale tabelului. Sub forma COUNT(nume_coloana), numără valorile, din coloana de nume dat, care nu sunt NULL. Sub forma MIN(nume_coloana), calculează cea mai mică valoare din coloană. Valorile NULL sunt ignorate. Sub forma MAX(nume_coloana), calculează cea mai mare valoare din coloană. Valorile NULL sunt ignorate. Sub forma SUM(nume_coloana), calculează suma valorilor dintr-o coloană. Valorile NULL sunt ignorate. Sub forma AVG(nume_coloana), calculează media aritmetică a valorilor dintr-o coloană. Valorile NULL sunt ignorate. Tabelul 2.14. Funcţii agregate Exemplul 2.40. Câţi elevi sunt în tabel? SELECT COUNT(*) AS numar_elevi FROM elevi; Figura 2.39. Exemplul 2.41. Care este numărul de note la engleză? Se afişează 4, pentru că un elev nu are notă la această disciplină (NULL). SELECT COUNT(engleza) AS numar_note_engleza FROM elevi; Figura 2.40. Exemplul 2.42. Care este media notelor la informatică? SELECT AVG(informatica) AS media_note_informatica FROM elevi; Figura 2.41. Exemplul 2.43. Câţi elevi au nota 10 la engleză? SELECT COUNT(engleza) AS elevi_de_zece FROM elevi WHERE engleza=10; Figura 2.42. 80 Baze de date – curs introductiv Exemplul 2.44. Câţi elevi au media generală peste 8? SELECT COUNT(nume) AS elevi_peste_8 WHERE (matematica+engleza+informatica)/3>8; Figura 2.43. Rezultat 2.17. Utilizarea subinterogărilor Aşa cum am arătat, filtrările sunt cazuri particulare de interogări. În general, orice rezultat care este obţinut cu ajutorul unei instrucţiuni SELECT este o interogare. La modul general, o interogare poate returna: A) o coloană, eventual cu un singur rând; B) mai multe coloane - în acest caz se poate spune că interogarea returnează un tabel. Problema care se pune în continuare este următoarea: pot fi utilizate datele returnate de o interogare pentru a efectua o nouă interogare? Răspunsul este, în general afirmativ şi este valabil începând cu versiunea MySQL incorporată în versiunea EasyPHP 1.8. În aceste cazuri, prima interogare care se efectuează este interogarea subordonată. Rezultatul ei este utilizat pentru a efectua o nouă interogare. Vom considera, pe rând, cazurile în care interogarea subordonată returnează o valoare, o coloană şi un tabel (mai multe coloane). Toate exemplele care urmează vor avea ca bază de plecare tabelul elevi, utilizat şi în paragrafele anterioare. A) Interogarea subordonată returnează o coloană cu un singur rând Exemplul 2.45. Se cere să se determine numele elevilor care au cea mai mare notă la matematică. Figura 2.44. Rezultatul dorit Practic, ar trebui să vedem care este cea mai mare notă la matematică, o primă interogare, problemă pe care o rezolvăm utilizând funcţia agregată MAX(), apoi într-o nouă interogare, aflăm numele elevilor care au această notă. În acest caz, interogarea subordonată returnează o valoare, cea maximă. SELECT nume, matematica FROM elevi WHERE matematica=(SELECT MAX(matematica) FROM elevi); Mai întâi, se execută interogarea subordonată, apoi cea care o subordonează. În acest caz, interogarea subordonată calculează nota maximă obţinută la matematică (să observăm că aceasta nu este în mod obligatoriu 10), iar cealaltă interogare returnează numele elevilor cu această notă. Capitolul 2. Limbajul MySQL 81 Exemplul 2.46. Se cer numele elevilor care au cea mai mare medie la obiectele matematică şi informatică. Vom proceda în acelaşi mod. În primul rând, selectăm media maximă la obiectele amintite, apoi numele elevilor cu această medie. SELECT nume, (matematica+informatica)/2 AS media FROM elevi WHERE (matematica+informatica)/2= (SELECT MAX((matematica+informatica)/2) FROM elevi); Figura 2.45. Rezultatul obţinut Exemplul 2.47. Se cer numele elevilor şi media generală, unde media generală a lor este strict mai mare decât media elevilor din tabel. Se cere, de asemenea, ca elevii să fie ordonaţi în ordinea crescătoare a mediilor. Interogarea subordonată calculează media generală a elevilor, iar prima interogare afişează elevii care au media generală peste aceasta. SELECT nume, (matematica+informatica+engleza)/3 AS media FROM elevi where (matematica+engleza+informatica)/3> (SELECT AVG((matematica+engleza+informatica)/3) FROM elevi) ORDER BY media DESC; Figura 2.46. Rezultatul obţinut Exemplul 2.48. Care este numele elevilor care au aceeaşi notă la matematică ca şi elevul Popescu Marius? În acest caz, interogarea subordonată returnează nota la matematică a elevului Popescu Marius, iar interogarea care o subordonează returnează numele elevilor care au această notă: SELECT nume FROM elevi WHERE matematica= (SELECT matematica FROM elevi WHERE nume=’Popescu Marius’); Figura 2.47. Rezultatul obţinut B) Interogarea subordonată returnează un tabel În acest caz, tabelul returnat de interogarea subordonată trebuie să aibă un nume. Acesta se dă cu ajutorul clauzei AS. De asemenea, interogarea care subordonează adresează câmpurile tabelului, pornind de la numele său, urmat de ‘.’ şi de numele coloanei. Exemplul 2.49. Care este media generală la matematică şi la informatică a elevilor care au cel puţin 8 la una dintre discipline? 82 Baze de date – curs introductiv Tabelul returnat de interogarea subordonată conţine notele la matematică şi la informatică ale elevilor care au cel puţin 8 la una dintre aceste discipline. Acest tabel se numeşte virtual. Interogarea principală calculează mediile la matematică şi informatică ale acestor elevi. SELECT AVG(virtual.matematica) AS medie_mate, AVG(virtual.informatica) AS medie_info FROM (SELECT matematica, informatica FROM elevi WHERE matematica>=8 OR informatica>=8) AS virtual; Figura 2.48. Rezultatul obţinut 2.18. Gruparea datelor Datele dintr-un tabel pot fi grupate în funcţie de valorile dintr-o anumită coloană. De exemplu, datele din tabelul din Fig. 2.49 pot fi grupate după valorile din coloana ”data”. Astfel, un grup este alcătuit din rândurile de tabel corespunzătoare datei ‘2006-03-15‘, altul, corespunzătoare datei ‘2006-03-16‘, ..., altul corespunzător unei date necunoscute (NULL). Prelucrările datelor din fiecare grup se fac cu ajutorul funcţiilor agregate, aşa cum suntem deja obişnuiţi. Pentru a grupa datele din tabel după valorile unei coloane se utilizează clauza GROUP BY nume_coloana, adăugată după numele tabelului. Exemplele care urmează vă vor lămuri! Exemplul 2.50. Se consideră tabelul “vanzari” cu structura de mai jos care conţine data vânzării, numele produsului vândut şi valoarea vânzării din produsul respectiv: CREATE TABLE vanzari ( data DATE, produs CHAR(15), valoare INT); Figura 2.49. Tabelul “vanzari” Întrebări: 1. Care este numărul de vânzări din fiecare zi? Grupăm datele după dată şi numărăm vânzările pentru fiecare grup. Capitolul 2. Limbajul MySQL 83 SELECT data, COUNT(*) AS numar_vanzari FROM vanzari GROUP BY data; Figura 2.50. Numărul de vânzări din fiecare zi 2. Care este suma vânzărilor zilnice? Grupăm datele după dată şi însumăm vânzările pentru fiecare dată în parte: SELECT data, SUM(valoare) AS suma_vanzari FROM vanzari GROUP BY data; Figura 2.51. Suma vânzărilor zilnice 3. Care este numărul de vânzări şi suma vânzărilor în fiecare zi (se va afişa un singur tabel)? SELECT data, COUNT(*) AS nr_vanzari, SUM(valoare) AS suma_incasata FROM vanzari GROUP BY data; Figura 2.52. Numărul de vânzări şi suma încasată din fiecare zi 4. Care este data în care am am vânzări în valoare maximă, câte vânzări au fost şi care este suma încasată? Ar trebui să obţinem un tabel care să conţină data şi suma vânzărilor zilnice, apoi, din aceasta să selectăm vănzarea maximă şi, apoi, data la care s-a efectuat vânzarea zilnică. În MySQL, în varianta de care dispunem, nu este permisă mai mult de o interogare cu subordonata sa. Din acest motiv, vom crea un tabel de manevră (“manevra“) care conţine data şi suma vânzărilor zilnice (ca la punctul 2). Apoi din acesta, utilizând o interogare cu subinterogare, vom extrage datele cerute. În final, vom şterge tabelul “manevra“. CREATE TABLE manevra AS SELECT data, SUM(valoare) AS vanzare FROM vanzari GROUP BY data; Pentru probă, afişăm tabelul manevra. SELECT * FROM manevra; Figura 2.53. Tabelul “manevra” 84 Baze de date – curs introductiv Pornind de la “manevra”, obţinem datele cerute. SELECT data, vanzare FROM manevra WHERE vanzare= (SELECT MAX(vanzare) FROM manevra); Ştergem tabelul ”manevra”: Figura 2.54. Rezultatul final DROP TABLE manevra; 2.19. Uniuni de tabele Un punct de maximă importanţă şi eficienţă în utilizarea bazelor de date este dat de uniunea de tabele. Uniunea este alcătuită din două sau mai multe tabele între care există o legătură. De cele mai multe ori, legătura este dată de valorile existente în câte o coloană a fiecărui tabel din uniune. Pentru tabelele care alcătuiesc uniunea se pot utiliza alias-uri de nume, sub forma nume_tabel AS alias_nume, introduse în clauza FROM. Alias-urile pot fi utilizate în orice parte a instrucţiunii select. În aceste cazuri, adresarea unei coloane a tabelului se face sub forma: alias_nume.nume_col. Avantajele utilizării alias-urilor sunt: - se pot asigura nume scurte pentru tabele, gen A, B,..., etc. - în cazul auto-uniunilor, caz care este prezentat în actualul paragraf, un singur tabel poate avea alias-uri diferite, fapt care permite anumite interogări speciale ca şi cum am avea mai multe tabele: A, B, etc. În astfel de cazuri, atunci când ne referim la o coloană, pentru a nu se crea confuzie, trebuie precizat tabelul, cum ar fi de exemplu: A.nume_col, B.nume_col, etc. 1) O primă formă de realizare a unei uniuni este utilizarea instrucţiunii SELECT în care la clauza FROM se trec, pe rând, toate tabelele care o alcătuiesc, iar legătura este precizată cu ajutorul clauzei WHERE. Vezi exemplele 2.51, pct. 1.a), 4, 5. 2) Atunci când uniunea este alcătuită din două tabele, se poate preciza o legătură de tip INNER sub forma de mai jos. În astfel de cazuri, se afişează datele din tabel1 şi din tabel2, pentru care este îndeplinită condiţia din ON. O astfel de uniune se mai numeşte şi uniune interioară. Vezi exemplul 2.51, pct. 1.b)! nume_tabel1 [AS...]INNER JOIN nume_tabel2 [AS...] ON conditia de uniune 3) Atunci când uniunea este alcătuită din două tabele, se poate preciza o legătură de tip LEFT OUTER sub forma de mai jos. În astfel de cazuri, se afişează toate datele din tabel1, iar datele din tabel2 se afişează numai dacă este Capitolul 2. Limbajul MySQL 85 îndeplinită condiţia din ON, altfel, în locul lor se afişează valoarea implicită (NULL, de cele mai multe ori). O astfel de uniune se mai numeşte şi uniune exterioară stânga. Vezi exemplul 2.51, pct. 2. nume_tabel1 [AS...]LEFT JOIN nume_tabel2 [AS...] ON conditia de uniune 4) Atunci când uniunea este alcătuită din două tabele, se poate preciza o legătură de tip RIGHT OUTER sub forma de mai jos. În astfel de cazuri, se afişează toate datele din tabel2, iar datele din tabel1 se afişează numai dacă este îndeplinită condiţia din ON, altfel, în locul lor se afişează valoarea implicită (NULL, de cele mai multe ori). O astfel de uniune se mai numeşte şi uniune exterioară dreapta. Vezi exemplul 2.51, pct. 3. nume_tabel1 [AS...]LEFT JOIN nume_tabel2 [AS...] ON conditia de uniune Exemplul 2.51. Priviţi tabelele de mai jos, numite "produse" (cel din stânga) şi "date_produse" (cel din dreapta). Logic, legătura dintre ele este dată de coloanele "cod", din fiecare tabel. Astfel, pentru un cod dat, din tabelul "produse" se poate extrage cantitatea existentă într-un magazin dintr-un anumit produs şi denumirea produsului, producătorul său din tabelul "date_produse". Figura 2.55. Cele două tabele propuse ca exemplu 1. Pentru fiecare produs existent în magazin, care este trecut în tabelul "date_produse", să se afişeze denumirea şi cantitatea. Pentru a rezolva această problemă, vom utiliza două forme de interogare şi ambele dau acelaşi rezultat. a) SELECT B.denumire, A.cantitate FROM produse AS A, date_produse AS B WHERE A.cod=B.cod; b) SELECT B.denumire, A.cantitate FROM produse AS A INNER JOIN date_produse AS B ON A.cod=B.cod; Figura 2.56. Rezultatul interogărilor 86 Baze de date – curs introductiv 2. La fel ca la punctul anterior, numai că se vor trece datele existente pentru toate produsele din tabelul ”produse”: SELECT B.denumire, A.cantitate FROM produse AS A LEFT OUTER JOIN date_produse AS B ON A.cod=B.cod; Figura 2.57. Rezultat 3. La fel ca mai sus, numai că se vor trece datele existente pentru toate ”date_produse”: produsele din tabelul SELECT B.denumire, A.cantitate FROM produse AS A RIGHT OUTER JOIN date_produse AS B ON A.cod=B.cod; Figura 2.58. Rezultat 4. O problemă serioasă care apare în cerinţele de acest tip este dată de faptul că nu întotdeauna, orice cod din primul tabel, în exemplu ”produse”, se află printre codurile din al doilea tabel, în exemplu, ”date_produse”. Se cere să afişăm codurile din tabelul ”produse” care nu se regăsesc între codurile din tabelul ”date_produse”. Analizaţi interogarea de mai jos, este şi un exemplu de utilizare a operatorului NOT IN: SELECT cod FROM produse WHERE cod NOT IN (SELECT cod FROM date_produse); Figura 2.59. 5. Selectaţi codurile de produse din tabelul ”date_produse” care nu 1 se regăsesc printre codurile din tabelul ”produse” . SELECT cod FROM date_produse WHERE cod NOT IN (SELECT cod FROM produse); Figura 2.60. Auto-uniuni. Se pot obţine rezultate interesante dacă cele două tabele sunt, de fapt, unul şi acelaşi, dar au aliasuri diferite şi se consideră unite printr-o coloană. Exemplul 2.52. În tabelul alăturat, numit “st_or“ (referitor la localităţile din care provin studenţii unei grupe dintr-o facultate), care sunt persoanele care locuiesc în acelaşi oraş cu Ioana? Figura 2.61. Tabelul “st_or” În cazul în care considerăm tabelul ”produse” drept tabel tată şi tabelul ”date_produse” drept tabel fiu, pentru a fi respectată integritatea referenţială, ar trebui ca mulţimea acestor coduri să fie vidă. 1 Capitolul 2. Limbajul MySQL 87 Privim acelaşi tabel ca pe două tabele diferite legate prin câmpul oras. Unul dintre tabele are numele A şi al doilea are numele B. Interogarea constă în afişarea tuturor numelor din B care au aceeaşi valoare în câmpul oras cu înregistrarea din A care, pentru câmpul nume, reţine ‘Ioana’. SELECT B.nume FROM st_or AS A INNER JOIN st_or AS B ON A.oras=B.oras AND A.nume=’Ioana’; Figura 2.62. Rezultatul interogării Observaţie ! Evident, aceeaşi problemă poate fi rezolvată prin utilizarea mecanismului subinterogărilor. Exerciţiu! 2.20. Elemente care privesc securitatea bazelor de date Asigurarea securităţii bazelor de date este o problemă extrem de serioasă. Paragraful are numai rolul de a vă iniţia în această activitate. În realitate, problema este cu mult mai complexă. Sistemul de privilegii MySQL. O primă problemă care apare este de a securiza, pe cât posibil, bazele de date. Din acest punct de vedere, în afara administratorului, există posibilitatea ca la bazele de date să aibă acces şi alte persoane, identificabile prin nume şi parole. Totuşi, aceste persoane au acces numai la anumite operaţii cu bazele de date. Pentru ca o astfel de persoană (care nu este utilizator) să acceseze baza de date este necesar ca administratorul să introducă numele ei, parola şi operaţiile pe care le poate efectua. Astfel, administratorul va utiliza instrucţiunea MySQL numită GRANT, a cărei formă simplificată o prezentăm mai jos: GRANT operaţii ON nume_bază TO nume_utilizator IDENTIFIED BY ‘parola’; Operaţiile permise sunt: SELECT, UPDATE, INSERT, INDEX, ALTER, CREATE, DROP şi trebuie să fie separate prin virgule. Atunci când accesul utilizatorului este permis pentru orice bază de date, se utilizează *.*. Exemplul 5.70. De acum, utilizatorul lavinia cu parola puful are acces la operaţii de tip SELECT şi INSERT pentru orice bază de date. GRANT SELECT, INSERT ON *.* TO lavinia IDENTIFIED BY ‘puful’ 88 Baze de date – curs introductiv Pentru a revoca unele drepturi ale unui utilizator sau chiar toate, administratorul foloseşte instrucţiunea REVOKE. Pentru a revoca toate operaţiile permise, se utilizează ALL. REVOKE operaţii ON nume_bază FROM nume_utilizator Exerciţiu. Acordaţi drepturi unor utilizatori, verificaţi dacă le au, revocaţi anumite drepturi şi verificaţi apoi dacă le mai au. 1. ”Materiale”. O firmă dispune de mai multe depozite. În fiecare depozit există mai multe materiale. Pentru fiecare material se cunoaşte preţul şi cantitatea. Se cere să se răspundă la întrebările de mai jos. Tabelul se numeşte ”materiale”. Figura 2.63. Tabelul “materiale” 1.1. Care sunt materialele existente într-un anumit depozit (în exemplu, ‘Depozit 2’) ? 1.2. Care sunt depozitele în care există un anumit material (în exemplu, ‘material 3’? Se presupune că numele materialului poate fi precedat de blank-uri. 1.3. Care este valoarea totală a tuturor materialelor din depozite? 1.4. Care este valoarea totală a materialelor din fiecare depozit? 1.5. Presupunem că ‘material 1’ se scumpeşte cu 10%. Actualizaţi datele din tabel. 1.6. Să se şteargă un anumit material (‘material 2’) din tabel. 2. “Împrumuturi”. Există persoane care au credite, în acelaşi timp, la mai multe bănci. O astfel de situaţie este prezentată în tabelul de mai jos, numit ”imprumuturi”. Se cere să se răspundă la următoarele întrebări: Capitolul 2. Limbajul MySQL 89 Figura 2.64. Tabelul “imprumuturi” 2.1. Care sunt persoanele care au împrumutat de la ‘Banca 1’? 2.2. Care este suma împrumutată de fiecare bancă populaţiei? 2.3. Afişaţi, pentru fiecare persoană, numărul împrumuturilor pe care le-a efectuat. 2.4. Care sunt persoanele care au împrumutat o sumă maximă şi care este această sumă? 2.5. Care sunt persoanele care au împrumutat bani cel puţin de la o bancă la care a împrumutat şi ‘Ionescu Grigore’? 3. “Proprietari, proprietăţi”. Se dă un tabel (“proprietari“), în care pentru fiecare persoană se cunoaşte codul (cod) şi numele (nume). Se dă şi un alt tabel (“proprietati“) în care se cunoaşte codul proprietarului (cod_proprietar), tipul proprietăţii (tip_p) şi valoarea proprietăţii (valoare): Figura 2.65. Tabelul “proprietari” Figura 2.66. Tabelul “proprietati” 3.1. Afişaţi, pentru fiecare proprietar care are cel puţin o proprietate, numărul proprietăţilor. 3.2. Afişaţi numele proprietarilor care nu au proprietăţi. 3.3. Afişaţi codurile proprietarilor din tabelul “proprietati“ care nu au corespondent în tabelul “proprietari“. 3.4. Afişaţi valoarea proprietăţilor pe care le are fiecare proprietar. Datele vor fi afişate în ordinea descrescătoare a proprietăţilor. 4. “Prietenii”. Se dau două tabele (“baieti” şi “fete”) în care pentru fiecare persoană se cunoaşte codul (cod), numele (nume) şi vârsta (varsta). De asemenea, mai există un tabel, numit “prietenii”, în care sunt reţinute relaţiile dintre băieţi şi fete. 90 Baze de date – curs introductiv Figura 2.67. Tabelul ” baieti” Figura 2.68. Tabelul ” fete” 4.1 Care sunt prietenele lui Mihai, cel care are 18 ani? Observaţie ! Se presupune că există un singur băiat Mihai care are 18 ani. 4.2 Afişaţi numele fetelor care nu au prieten. 4.3 Pentru fiecare băiat care are cel puţin o prietenă, afişaţi numele prietenelor pe care acesta le are. 4.4 Afişaţi numele unei fete cu un număr maxim de prieteni şi numărul prietenilor ei. 4.5 Afişaţi media de vârstă a băieţilor şi a fetelor. Figura 2.69. Tabelul ” prietenii” 5. ”Angajaţi”. O firmă doreşte să reţină într-o bază de date un tabel referitor la angajaţi. Motivul este dat de faptul că se vrea obţinerea rapidă a răspunsurilor la întrebările de mai jos. Ce date va reţine tabelul? Cum obţinem răspunsul la întrebări? 5.1 Care sunt numele angajaţilor (în ordine alfabetică)? 5.2 Care sunt angajaţii cu vârsta sub 30 de ani? 5.3 Realizaţi o listă a femeilor angajate şi o alta a bărbaţilor angajaţi. 5.4 Realizaţi o listă a angajaţilor care au studii superioare. 5.5 Care sunt angajatele care sunt în concediu de maternitate? 5.6 Care sunt angajaţii cu o vechime în meseria pe care o practică la firmă de sub trei ani? 5.7 Care sunt angajaţii care au salariul minim pe economie? 5.8 Care sunt angajaţii care au salariul brut mai mare decât cel minim, dar mai mic decât 1000 de lei? 5.9 Care sunt angajaţii care ies la pensie anul următor? 5.10 Care sunt angajaţii care au copii şi câţi copii au? 5.11 Mâine este Sfânta Maria. Care este lista sărbatoritelor? 5.12 Găsiţi angajaţii care îşi sărbătoresc mâine ziua de naştere! 6. ”Cheltuieli / Venituri”. George este o persoană căreia îi place să-şi ţină evidenţa veniturilor şi cheltuielilor. El are un tabel cu următoarele câmpuri: Data, Cheltuiala/Venit, Suma cheltuită sau încasată, Explicaţii (Text). Să presupunem că George şi-a completat tabelul câteva luni, în fiecare zi. Fiecare sumă cheltuită sau încasată este înregistrată pe o linie a tabelului. Se cere: 6.1 Care sunt veniturile totale ale lui George şi care sunt cheltuielile sale pe toată perioada cât a ţinut evidenţa? Capitolul 2. Limbajul MySQL 91 6.2 A reuşit George să economisească o sumă de bani în această perioadă sau a fost nevoit să se împrumute? Care este suma economisită sau împrumutată? 6.3 În care lună George a cheltuit cea mai mare sumă? 6.4 În care lună George a avut cele mai mari încasări? 6.5 Care este luna în care George a economisit cea mai mare sumă? 6.6 Creaţi un tabel care să conţină toate veniturile lui George pe întreaga perioadă şi un altul care să conţină toate cheltuieliule în aceeaşi perioadă. 6.7 Creaţi un tabel care să conţină, pentru fiecare lună în parte, suma încasărilor şi suma cheltuielilor. 7. ”Biblioteca”. Ioana s-a angajat la o bibliotecă şi doreşte să ţină evidenţa cărţilor pe calculator. 7.1 Ioana doreşte, în primul rând, să ţină evidenţa titlurilor pe care le are la bibliotecă. Pentru aceasta, Ioana codifică toate titlurile şi reţine pentru fiecare titlul, editura şi autorul. 7.2 Pentru a ţine evidenţa numărului de exemplare din fiecare titlu, existente în gestiune, este nevoie de un alt tabel, numit Nr Exemplare. Creaţi-l!. 7.3 Care este legătura între cele două tabele? 7.4 În continuare, trebuie creat un tabel, numit Cititori, care conţine persoanele care împrumută cărţi de la bibliotecă (Nume, Adresa, Nr. telefon) Înainte de a împrumuta o carte, cititorul este înscris în acest tabel. Creaţi-l!. 7.5 De asemenea, trebuie creat un tabel (Imprumuturi) în care, pentru fiecare carte împrumutată, este scrisă o înregistrare care conţine numele cititorului şi codul cărţii (o înregistrare conţine un singur exemplar dintr-un titlu). Creaţi-l! 7.6 Se cere o situaţie în care sunt afişaţi cititorii care au împrumutat o carte de mai mult de două săptămâni. 7.7 Câte exemplare sunt împrumutate din fiecare carte? 7.8 Câte exemplare sunt împrumutate din cartea care are codul x? 7.9 Pentru fiecare carte împrumutată, se cere codul cărţii, tilul şi numărul exemplarelor aflate la cititori. 7.10 Un cititor solicită un anumit titlu. Ioana vrea să vadă dacă există exemplare neîmprumutate din acesta. 8. Problemă pentru lucrul în colectiv! “Grilă“. Scrieţi o grilă cu 18 itemi (18 întrebări la care răspunsul corect este unul singur, ales dintre 4 răspunsuri). În final se va afişa nota obţinută de cel care este testat prin utilizarea acestei grile. Fiecare răspuns corect este punctat cu 0.5 puncte şi un punct se acordă din oficiu. Desigur, dacă este necesar, itemii vor fi însoţiţi de imagini. Problema va fi rezolvată de echipe diferite, pe diverse domenii: informatică, matematică, biologie, limbi străine, chimie, istorie, ş.a.m.d. Un set de astfel de probleme va fi util elevilor din toată ţara! 9. Problemă pentru lucrul în colectiv! “Magazin“. Creaţi un site prin care o firmă vinde calculatoare şi diverese componente de calculatoare. Comenzile se pot face prin intermediul site-ului şi ele sunt stocate în baza de date. Se cere să se efectueze validarea datelor de intrare. De asemenea, reprezentanţii firmelor pot extrage din baza de date comenzile dintr-o anumită zi şi pot memora date referitoare la expedierea produselor. (pentru verificare, rezolvările se găsesc la finalul cărții) CAPITOLUL Limbajul PHP În acest capitol studiem limbajul PHP. După ce-l vom parcurge, vom şti să trimitem date de pe calculatorul client către server şi vom şti să le prelucrăm pe server. Vom cunoaşte cum să personalizăm paginile web, în funcţie de persoana care le accesează, dar adevărata forţă a limbajului se va vedea atunci când îl vom folosi la un loc cu limbajul MySQL. Introducere Cum realizăm un formular ? Constante, variabile, operatori Care sunt instrucţiunile limbajului PHP ? Funcţii PHP Funcţii “matematice” Cum afişăm datele ? Funcţii pentru prelucrarea şirurilor de caractere Masive în PHP Ce sunt variabilele cookie ? Cum le folosim ? Utilizarea în comun a limbajelor PHP şi MySQL Cuvinte cheie: formulare, input, submit, radio, checkbox, password, textarea, select, constante, variabile, operatori, echo, print, instrucţiuni, funcţii, masive, cookie, securitate Aplicaţii Capitolul 3. Limbajul PHP 93 3.1. Introducere Numele limbajului era iniţial un acronim pentru ”Personal Home Page” şi a fost inventat în 1994 de Rasmus Lerdof, care dorea să-şi contorizeze vizitatorii paginii sale de Internet. Astăzi, se cosideră că PHP este un limbaj de scriptare înglobat în HTML. Documentaţia oficială a limbajului se găseşte pe site-ul www.php.net. Script-urile scrise în acest limbaj se utilizează pe partea de server. Limbajul PHP este asemănător limbajului C. Script-urile scrise în acest limbaj rulează şi trebuie să se găsească pe server. În linii mari, ele au rolul de a prelucra datele pe care le trimite utilizatorul de pe propriul calculator şi de a transmite către browser rezultatele prelucrărilor. Nu toate site-urile permit utilizarea limbajului PHP. Din acest motiv, dacă vă interesează să-l utilizaţi, trebuie să studiaţi cu atenţie oferta de site-uri a firmei la care apelaţi (provider-ul). În ceea ce ne priveşte, pentru a învăţa limbajul PHP vom utiliza pachetul EasyPHP (vezi Anexa 1) care ne permite să rulăm PHP pe propriul calculator. Nu uitaţi, înainte de orice, lansaţi în executare programul EasyPHP din meniul Start/Programs! Practic, orice script PHP va fi memorat în subfolder-ul www al folder-ului EasyPHP din Program Files şi va fi apelat din browser prin ” http://192.168.1.217/nume_script.php”. Orice script PHP va avea extensia ”php”. În situaţia în care rulaţi pe un site real, după ce creaţi şi testaţi script-urile pe propriul calculator, cu EasyPHP, le puteţi transfera pe server cu ajutorul unui program FTP. Orice script PHP este cuprins între: <?php ... ?> Exemplul 3.1. Script-ul de mai jos afişează un mesaj. Îl vom memora în www sub numele de "primul.php". Apelul şi rezultatul se pot observa alăturat. <?php echo "eu sunt primul script php"; ?> 94 Baze de date – curs introductiv Observaţie foarte importantă! Atunci când se apelează un script, el este rulat pe server. Acesta va efectua operaţiile pe care le are programate şi eventual, va scrie ceva (de exemplu, ca în exemplul următor, cu "echo"). Rezultatul (adică ce este scris) este returnat către browser, iar acesta îl va trata ca pe un fişier html. Exemplul 3.2. Rulăm script-ul de mai jos care conţine câteva elemente de formatare. Rezultatul îl vedem în figura alăturată: <?php echo "eu sunt <b>primul</b> <br>script <b>php</b>"; ?> Exemplul 3.3. Acelaşi script poate fi apelat şi ca un link, aşa cum observaţi mai jos (fişierul html putând fi plasat oriunde): <html> <head></head> <body> <p> Un text oarecare! <a href=" http://192.168.1.217/primul.php">Apeleaza</a> </body> </html> Exemplul 3.4. Script-urile PHP pot conţine direct elemente de HTML, pe lângă secvenţa propriu-zisă, PHP. Scrieţi script-ul următor în www şi apelaţi-l. Veţi vedea că se afişează conţinutul unei variabile PHP (variabilele PHP vor fi tratate separat). La apel se afişează "Bine ati venit, domnule Ion Popescu!". <html> <head> </head> <body> <p> Bine ati venit, domnule <?php $nume=" Ion Popescu!"; echo ($nume); ?> </body> </html> Capitolul 3. Limbajul PHP 95 3.2. Formulare De la început trebuie spus că formularele sunt elemente ale limbajului HTML. Un formular este o grupare de componente HTML care permit trimiterea datelor şi a comenzilor către server. Pe server, comenzile sau datele sunt recepţionate de un script PHP care răspunde comenzilor şi prelucrează datele. Un formular se descrie între tag-urile <form action script> şi </form>. Prin script înţelegem numele script-ului care se rulează atunci când datele, comenzile sosesc pe server. Între cele două tag-uri se inserează diferite componente care permit introducerea datelor sau trimiterea comenzilor. Elementul INPUT – este un element HTML de o mare complexitate. Poate fi de diverse tipuri (type): de la un buton de validare, la unul radio, un edit, etc. După cum sugerează şi numele, rolul elementului input este de a permite utilizatorului să introducă date sau comenzi în pagina vizualizată de browser. Un element input se descrie cu tag-ul următor (formă simplificată): <input name="nume" type="tip" value="valoare" align="tip_aliniere"> unde valoare exprimă valoarea afişată de element. 3.2.1. Tipul “submit” Are aspectul unui buton. La apăsarea lui, datele sunt transmise către server, de unde, evident, răspunde un script care are rolul de a le prelucra. Exemplu 3.5. Nu se transmit date, dar la apăsarea butonului de tip submit se lansează un script care afişează un mesaj: <html> <head> </head> <body> <form action="http://192.168.1.217/exemplu.php"> <input type="submit" value="Primul exemplu de apel PHP"> </form> </body> </html> Alăturat, observaţi cum este afişată sursa HTML de către browserul web: 96 Baze de date – curs introductiv Iată şi script-ul PHP reținut în fișierul exemplu.php: În urma rulării, browser-ul afişează mesajul. 3.2.2. Tipul “text” Este sub forma unui edit şi permite utilizatorului să introducă date. Acestea se trimit către browser la apăsarea unui buton de tip submit. Pentru ca datele să fie recuperate de către script-ul de pe server, trebuie ca tag-ul <form> să conţină parametrul method ="post". Atunci când browser-ul execută o cerere către server, trimite şi valorile reţinute de cele două input-uri de tip text. Variabila $_POST este un vector 1 asociativ care reţine toate valorile astfel transmise. Valoarea transmisă de input-ul de tip text numit a se recuperează prin $_POST[‘a’], cea transmisă de input-ul de tip text b se recuperează prin $_POST[‘b’]. Această modalitate de transmitere şi recepţionare a parametrilor este generală şi trebuie reţinută. Exemplul 3.6. Vizitatorul trimite două valori numerice a şi b. Script-ul PHP returnează suma lor: <html> <head></head> <body> <form action="http://192.168.1.217/exemplu.php" method ="post"> <input type="text" name="a"><br> <input type="text" name="b"><br> <input type="submit" value="Suma?"> </form> </body></html> Sursa este vizualizată de către browser, aşa cum puteţi observa în figura de mai jos. Script-ul PHP care afişează suma celor două numere este: <?php // recuperarea datelor $a=$_POST['a']; $b=$_POST['b']; echo $a+$b; ?> 1 În PHP, variabilele încep cu caracterul ”$”. Vectorii asociativi sunt trataţi în cadrul acestui capitol. Capitolul 3. Limbajul PHP 97 3.2.3. Tipul “radio” Tipul radio (butoane radio) se utilizează în cazul în care utilizatorul trebuie să aleagă o singură opţiune dintre mai multe posibile. Pentru fiecare buton radio se ataşează câte un element input de tip radio. Pentru a avea un comportament de grup, în sensul că se poate bifa un singur buton, toate butoanele trebuie să aibă un singur nume, în exemplul de mai jos, Buton. Opţional, pentru un buton se poate folosi opţiunea checked şi efectul este acela că de la început butonul este bifat. La apăsarea butonului submit, butonul bifat va trimite valoarea ataşată lui. Exemplul 3.7. Utilizatorul trebuie să bifeze un singur buton radio, iar pe server “se va şti”, în funcţie de valoarea primită, care buton a fost apăsat: <html> <head> </head> <body> <form action="http://192.168.1.217/ exemplu.php" method ="post"> <p> Cumpar ziarul ... <br><br> <input name="Buton" type="radio" value=1 checked> zilnic <br> <input name="Buton" type="radio" value=2> saptamanal <br> <input name="Buton" type="radio" value=3> lunar<br><br> <input type="submit" value="Trimite"> </form> </body> </html> Script-ul PHP şi rezultatul afişat: <?php $b=$_POST['Buton']; echo $b; ?> 3.2.4. Tipul “checkbox” Acest tip se utilizează pentru ca în fereastra browser-ului să fie afişate butoane de marcare. Aceste butoane sunt utilizate pentru a permite utilizatorului să marcheze una, niciuna sau mai multe opţiuni. De reţinut: în acest caz, se transmit către server doar valorile butoanelor marcate. Exemplul 3.8. În continuare, observaţi modul în care facem ca browser-ul să afişeze butoanele de marcare şi script-ul PHP care depistează butoanele marcate: 98 Baze de date – curs introductiv <form action="http://192.168.1.217/exemplu.php" method="post"> <p>Urmaresc posturile TV:</p> <input NAME="buton1" type="checkbox" value=1 checked> CNN <br> <input NAME="buton2" type="checkbox" value=2> Antena 1 <br> <input NAME="buton3" type="checkbox" value=3> ProTV <br><br> <input type="submit" value="GO"> </form> Iar în exemplu.php vom avea: <?php foreach ($_POST AS $i=>$nume) if ($i=="buton1") echo "CNN"."<br>"; else if ($i=="buton2") echo "Antena 1" ."<br>"; else if ($i=="buton3") echo "ProTV"; ?> 3.2.5. Tipul “password” Componentele vizuale de acest tip se utilizează pentru introducerea parolelor, atât de necesare, de exemplu, pentru a avea acces la un site. Sunt asemănătoare cu cele de tip text, numai că, pentru fiecare caracter al parolei se afişează câte un “*”. Desigur, către server, datele sunt transmise corect. Exemplul 3.9. Se introduce o parolă, iar script-ul care o primeşte o afişează: <form action=" http://192.168.1.217/exemplu.php" method="post"> <p> Introduceti parola </p> <input name="parola" type="password"> <br><br> <input type="submit" value="GO"> </form> <?php echo $_POST['parola']; ?> Capitolul 3. Limbajul PHP 99 3.3. Elementul TEXTAREA Elementul TEXTAREA se utilizează pentru a introduce un text mai lung. El poate fi folosit în interiorul unui formular şi, la apăsarea butonului submit, conţinutul său este transmis către server. De remarcat este că pentru un element TEXTAREA se pot preciza numărul de linii (rows) şi de coloane (cols) pe care acesta să le afişeze. Exemplul 3.10. În fișierul primul.php vom scrie: ... <form action="http://192.168.1.217/exemplu.php" method="post"> <p>Adaugati comentariul dv.!</p> <textarea name="textul" rows="10" cols="20"></textarea><br><br> <input type="submit" value="Scrie"> </form> ... Iată şi script-ul PHP care afişează şirul trimis (exemplu.php): <?php echo $_POST['textul']; ?> 100 Baze de date – curs introductiv 3.4. Elementul SELECT Elementul SELECT se utilizează pentru ca browser-ul să afişeze o listă. Fiecare opţiune din listă este marcată de un element OPTION. Dacă este introdusă într-un formular, către server se va transmite opţiunea selectată. Exemplul 3.11. Mai jos este prezentat formularul care afişează lista din figura de mai sus şi script-ul PHP care afişează opţinea selectată din formular: Scriptul PHP este: Capitolul 3. Limbajul PHP 101 3.5. Constante. Variabile. Operatori 3.5.1. Constante În PHP se pot defini constante cu ajutorul funcţiei define. Forma generală a acesteia este: define("nume_constanta", valoare). Exemplul 3.12. În script-ul următor se defineşte constanta e şi se probează dacă este recunoscută. La apel, se afişează 5.42: 3.5.2. Variabile În PHP numele unei variabile începe cu $. În continuare, trebuie să fie o literă sau o liniuţă de subliniere. În rest, puteţi folosi şi cifre. Nu este necesar ca variabilele să fie declarate de la început. Ele se definesc atunci când sunt folosite. Mai mult, îşi schimbă tipul în funcţie de valoarea reţinută. Exemplul 3.13. Analizaţi script-ul următor: iniţial, variabila $x reţine 1. Apoi, i se atribuie un şir de caractere. Operaţia este posibilă şi în urma ei se reţine şirul, aşa cum rezultă din valoarea afişată: 102 Baze de date – curs introductiv În PHP este posibilă şi adresarea indirectă, aşa cum rezultă din exemplele următoare. Exemplul 3.14. Se afişează 3. Didactic, putem stabili ceea ce se afişează, astfel: $$x = $($x) = $y = 3. <?php $x="y"; $y=3; echo ($$x); ?> Exemplul 3.15. Se afişează "Un sir". Astfel, avem: $$$z = $$($z) = $$y = $($y) = $x = "Un sir". <?php $x="Un sir"; $y="x"; $z="y"; echo ($$$z); ?> 3.5.3. Operatori Mulţi dintre operatorii limbajului PHP sunt cunoscuţi din C++. Acesta este motivul pentru care vom prezenta doar anumite particularităţi specifice limbajului. Pentru început, prezentăm, în ordine descrescătoare, prioritatea operatorilor: 1. !, ++, --, (int), (double), (string); 2. *, %, /; 3. <. <=, >, >=; 4. ==, !=, ===, !=== ; 5. &; 6. ^; 7 &&; 8. ?:; 9. =, +=, -=, /=. *=, %=, &=, |=, ^=; 10. And; 11. Xor; 12. Or; 13 ,. Capitolul 3. Limbajul PHP 103 În PHP se pot folosi operatori de conversie explicită, cunoscuţi din C++. Ca şi în C++, ei se aplică prefixat. Astfel, există: (int) - conversie către o valoare întreagă, (string) - conversie către şir, iar (double) - conversie către real. Exemplul 3.16. Conversia unui număr real către un întreg are ca efect eliminarea zecimalelor. Script-ul următor afişează valoarea 7: <?php $x=1; $y=7.5; $x=(int)$y; echo ($x); ?> Exemplul 3.17. Conversia unui şir către un întreg reuşeşte chiar dacă şirul conţine şi caractere nenumerice! Caracterele numerice trebuie să ocupe primele poziţii! În exemplul următor, în urma conversiei, x va reţine 2 şi y va reţine 3. Se afişează suma, adică 5. <?php $x=" 2 siruri"; $y="3siruri"; $x=(int)$x; $y=(int)$y; echo ($x+$y); ?> Observaţie ! Uneori, conversiile se realizează implicit (fără utilizarea operatorului de conversie explicită). Exemplul 3.18. Script-ul următor afişează -1.76: <?php $x="-2.76"; // sir echo (1+$x); ?> Exemplul 3.19. Se afişează -1: <?php $x="-2.76"; // sir $y="1.76"; echo ($x+$y); ?> Exemplul 3.20. Se afişează ... tot –1: <?php $x="-2.76 tren"; $y="1.76masina"; echo ($x+$y); ?> 104 Baze de date – curs introductiv După efectuarea unei operaţii cu un operator din grupele 3 sau 4, se obţine o valoare logică: true (1) sau false (0). Operatorul === se referă la egalitate ca valoare şi ca tip, spre deosebire de == care se referă la egalitate doar ca valoare. Exemplele următoare vă vor convinge. Exemplul 3.21. Script-ul următor afişează “egali ca valoare”: <?php $x="3"; $y=3; if ($x===$y) echo(" egali ca valoare si tip"); else if ($x==$y) echo ("egali ca valoare"); else echo "inegali ca valoare"; ?> Înlocuiţi în script-ul anterior primele două atribuiri cu atribuirile de mai jos. Veţi observa că se afişează numai “egali ca valoare”. Aceasta demonstrează că, deşi valorile logice au reţin 1 sau 0, ele rămân, de fapt, valori logice! $x=2>1; $y=1; Observaţie ! Se poate deduce cu uşurinţă rolul operatorului !==. Operaţia pe care o efectuează returnează true, dacă este diferenţă între tip sau valoare şi false, în caz contrar. Este permisă atribuirea multiplă. Exemplul 3.22. Script-ul următor afişează “un sir”: <?php $x=1; $y=-2; $z="un sir"; $x=$y=$z; echo ($x); ?> Diferenţa dintre || şi Or, && şi And, ^ şi Xor este doar de prioritate! Capitolul 3. Limbajul PHP 105 3.6. Instrucţiunile limbajului PHP Instrucţiunile PHP sunt asemănătoare celor din C/C++. Din acest motiv, ne vom limita la o scurtă prezentare a lor şi la câteva exemple de utilizare. Pentru exemplificare, vom folosi următorul document HTML care trimite o valoare x către server. Pe server se va găsi un script, numit ”index.php”, care răspunde trimiţând o valoare către browser. <html> <head> </head> <body> <form action="http://localhost" method="post"> <input type="text" name="x"> <br><br> <input type="submit" value="GO"> </form> </body> </html> 3.6.1. Instrucţiunea expresie Se foloseşte pentru diverse calcule sau atribuiri. Exemplul 3.23. Analizaţi instrucţiunea: $x=$y*$y+1;. 3.6.2. Instrucţiunea IF Această instrucţiune are forma generală: if (expresie) instructiune1 else instructiune2 Dacă expresie produce la evaluare o valoare diferită de 0 (true), se execută instructiune1, altfel se execută instructiune2. Exemplul 3.24. Dacă valoarea este în intervalul [1,2], se transmite către browser valoarea (x+1)/2, altfel se transmite valoarea (x-1)/2: <?php if ($x>=1 && $x<=2) $v=($x+1)/2; else $v=($x-1)/2; echo ($v); ?> 106 Baze de date – curs introductiv 3.6.3. Instrucţiunea compusă Atunci când dorim ca mai multe instrucţiuni să fie interpretate ca una singură, instrucţiunile se trec între "{" şi "}". Instrucţiunea switch are forma generală: switch (expresie) case exp1: secventa instructiuni1; break; case exp2: secventa instructiuni2; break; ... case expn: secventa instructiunin; break; [default: secventa instructiunin+1]; unde expresie are semnificaţia: expresie de tip întreg, expi sunt expresii constante de tip întreg, iar instrucţiunii, o secvenţă oarecare de instrucţiuni. Principiul de executare este: se evaluează expresia; dacă aceasta produce o valoare egală cu cea produsă de expi, se execută, în ordine, instrucţiunii şi se trece la instrucţiunea următoare, altfel se execută numai secvenţa instrucţiunin+1. Observaţie ! Alternativa default este facultativă. În absenţă, în cazul în care nu există coincidenţă de valori, se trece la instrucţiunea următoare. Exemplul 3.25. Dacă valoarea transmisă pentru x este 1, 2, 3, 4, atunci valoarea respectivă este retransmisă către utilizator alfabetic, altfel, se va transmite un mesaj oarecare. <?php switch ($x) { case (1): case (2): case (3): case (4): default : } ?> echo(“unu”); break; echo(“doi”); break; echo(“trei”); break; echo(“patru”); break; echo (“Nu este 1, 2, 3 sau 4”); 3.6.4. Instrucţiunea FOR Instrucţiunea for are forma generală: for(expresieiniţializare;expresietest;expresieincrementare) instrucţiune Capitolul 3. Limbajul PHP 107 După cum se vede, între paranteze se găsesc 3 expresii: Expresieinitializare se foloseşte, de regulă, pentru iniţializarea variabilei de ciclare. Expresietest se foloseşte pentru a testa dacă se execută instrucţiunea subordonată - dacă expresia produce la evaluare o valoare diferită de 0 (true), instrucţiunea subordonată for se execută. Expresieincrementare se foloseşte pentru incrementarea variabilei de ciclare. Principiul de executare este: P1. Se evaluează expresieiniţializare. P2. Se evaluează expresiatest. În cazul în care aceasta produce o valoare diferită de 0, se execută instrucţiunea subordonată for; apoi se trece la P3, altfel se trece la instrucţiunea următoare (se termină executarea instrucţiunii for). P3. Se evaluează expresia de incrementare şi se revine la P2. Exemplul 3.26. Se calculează suma pătratelor primelor x numere naturale (x este număr natural): <?php $s=0; for ($i=1;$i<=$x;$i++) $s+=$i*$i; echo($s); ?> 3.6.5. Instrucţiunea WHILE Instrucţiunea while are forma generală: while (expresie) instructiune Principiul de executare este următorul: P1. Se evaluează expresia; P2. Dacă valoarea produsă de aceasta este diferită de 0, se execută instrucţiunea subordonată, apoi se revine la P1, altfel se trece la instrucţiunea următoare. Exemplul 3.27. Se calculează suma de la exemplul anterior: <? $s=0; $i=1; while ($i<=$x) { $s+=$i*$i; $i++; } echo($s); ?> Observaţie ! Script-ul oferă şi un exemplu de instrucţiune compusă. 108 Baze de date – curs introductiv 3.6.6. Instrucţiunea DO WHILE Forma generală a acestei instrucţiuni este următoarea: do instructiune while(expresie); Principiul de executare este următorul: P1. Se execută instrucţiunea subordonată; P2. Se evaluează expresia. În cazul în care valoarea produsă la evaluare este 0 (false), executarea instrucţiunii do se termină, altfel se trece la P1. Exemplul 3.28. Se calculează suma pătratelor primelor x numere naturale (x este natural): <?php $s=0; $i=1; do { $s+=$i*$i; $i++; } while ($i<=$x); echo($s); ?> 3.7. Funcţii în PHP În PHP se pot crea funcţii fără prea mari diferenţe faţă de ce ştim din C/C++. Exemplul 3.29. Mai jos este prezentat un script care utilizează o funcţie pentru a calcula aria unui triunghi, pe care apoi o afişează: <?php function aria_t($b, $h) { return $b*$h/2; } $x=3; $y=8; echo(aria_t($x,$y)); ?> Pentru a întoarce rezultate se utilizează, aşa cum suntem deja obişnuiţi, return. Forma generală este: return expresie. Transmiterea parametrilor se face prin valoare şi prin referinţă. Capitolul 3. Limbajul PHP 109 A) Dacă un parametru este transmis prin valoare, atunci valoarea sa rămâne neschimbată după executarea funcţiei. Prin valoare se pot transmite atât valori, cât şi conţinutul unor variabile. Exemplul 3.30. Script-ul următor afişează 5, chiar dacă în funcţie s-a încercat modificarea valorii transmise: <?php function f($x) { $x++; } $x=5; f($x); echo($x); ?> Exemplul 3.31. Script-ul următor exemplifică faptul că o funcţie poate fi apelată atât printr-o valoare care este conţinutul unei variabile, cât şi printr-o valoare oarecare. Se afişează 3 şi 7, deci 37. <?php function f($x) { echo($x); } $x=3; echo(f($x)); echo (f(7)); ?> B) Dacă un parametru este transmis prin referinţă atunci, în corpul funcţiei, se poate modifica valoarea sa. Evident, un parametru transmis prin valoare trebuie să fie o variabilă. Pentru a transmite un parametru prin referinţă, el trebuie precedat de caracterul &. Exemplul 3.32. În script-ul următor, se modifică valoarea parametrului transmis prin referinţă. Se va afişa 6, nu 5, cât era valoarea iniţială a variabilei $x: <?php function f(&$x) { $x++; } $x=5; f($x); echo($x); ?> O variabilă se numeşte globală dacă a fost definită în afara oricărei funcţii. Implicit, ea nu poate fi adresată din corpul unei funcţii. Totuşi, există o posibilitate de a face acest lucru: ataşăm cuvântul global înaintea acestei variabile! Exemplul 3.33. Funcţia din script-ul următor returnează 3. Aceasta arată că a fost recunoscută variabila globală $x. În absenţa cuvântului global, această valoare nu ar fi putut fi returnată: 110 Baze de date – curs introductiv <?php function f() { global $x; return $x; } $x=3; echo(f()); ?> Variabilele locale sunt variabile create în corpul unei funcţii sau cele create prin transmiterea parametrilor formali (din antetul funcţiilor). Ele nu sunt recunoscute în afara funcţiilor. Nimic nou... În PHP, funcţiile pot fi şi recursive. Exemplul 3.34. Script-ul de mai jos calculează n! prin utilizarea unei funcţii recursive: <?php function fact($n) { if ($n==0) return 1; else return $n*fact($n-1); } echo(fact(4)); ?> Desigur, atunci când scriem un script mai complicat, cu multe funcţii, este necesar să dispunem de un mecanism prin care să putem include funcţii pe care le-am scris mai demult, sau pe care le-am primit de undeva... Includerea unor funcţii, dar nu numai, se realizează prin funcţia require("nume_fisier"). Funcţiile trebuie să se găsească pe site, într-un fişier text. Observaţi faptul că textul funcţiei (funcţiilor inserate) trebuie să fie încadrat între <? şi ?>. Exemplul 3.35. Funcţia afis() se găseşte în fişierul functia.ex, aflat pe site. Script-ul care o utilizează conţine require(): <?php function afis() { echo("Eu sunt functia inserata"); } ?> <? require("functia.ex"); afis(); ?> Pe lângă faptul că putem să scriem diverse funcţii, limbajul PHP este înzestrat cu numeroase alte funcţii. Cele mai importante vor fi tratate pe parcurs. Capitolul 3. Limbajul PHP 111 3.8. Funcţii “matematice” Multe dintre funcţiile prezentate le-aţi întâlnit dacă aţi studiat limbajul C/C++. Tabelul următor le prezintă succint: Funcţia Ce realizează Exemple abs(numar) Returnează modulul numărului. abs(-7)=7; abs(-2.23)=2.23; abs(2.3)=2.3 sin(x), cos(x), tan(x) Sinusul, cosinusul şi tangenta unui unghi. Argumentul x este dat în radiani exp(x) Returnează ex . pow(x,y) Returnează xy. log10(x), log(x) max(x1, x2,... xn), min(x1, x2,... xn) ceil(x) round(x) floor(x) rand(min,max) . Returnează log 10 ( x) , respectiv log 2 ( x) . Returnează maximul, minimul dintr-un şir de argumente numerice. Returnează cel mai mic întreg mai mare sau egal cu x. Returnează întregul rezultat prin rotunjirea lui x. Returnează cel mai mare întreg mai mic sau egal cu x. Returnează o valoare întreagă aleatoare între valorile întregi min şi max (inclusiv). pi() Returnează numărul . sqrt(x) Returnează rădăcina pătrată a lui x. Tabelul 3.1. Funcţii “matematice” ceil(4.3)=5, ceil(-4.3)=-4 round(4.3)=4, round(4.7)=5. floor(4.3)=4, floor(-4.3)=-5 rand(1,10) poate returna orice valoare întreagă din [1,10]. sqrt(4)=2; 112 Baze de date – curs introductiv 3.9. Afişarea datelor - echo şi print Pentru a afişa datele pe browser se folosesc echo şi print. Acestea nu sunt funcţii în accepţiunea clasică a cuvântului, deşi pot fi utilizate asemenea funcţiilor. Ele sunt construcţii speciale PHP - ambele afişează şiruri de caractere. A) echo – datorită simplităţii, a fost utilizată de multe ori până acum. Pentru că poate fi utilizată în multe feluri, o vom prezenta prin exemple şi vom renunţa la prezentarea formei generale. Exemplul 3.36. Afişăm un şir de caractere folosind parantezele rotunde (apel ca în cazul funcţiilor): echo("Un mesaj"); Exemplul 3.37. Afişăm un şir de caractere fără să utilizăm parantezele rotunde. Pentru că este permisă o astfel de utilizare, spunem că echo nu este funcţie, ci construcţie specială. echo "Un mesaj"; Exemplul 3.38. În cazul în care nu se folosesc paranteze, se pot afişa mai multe şiruri de caractere: echo "Un mesaj ", "Alt mesaj"; Exemplul 3.39. Putem afişa şi conţinutul unor variabile, aşa cum rezultă din exemplul următor: <?php $x=6; echo ("Am $x mere"); // sau echo "Am $x mere"; ?> B) print – se utilizează la fel ca echo, dar, dacă utilizăm paranteze, se poate folosi valoarea întoarsă: true, dacă şirul a fost expediat sau false, în caz contrar. Exemplul 3.40. Priviţi exemplul de mai jos: print ("Un exemplu"); <?php $x=6; $y=8; print ("Ana are ?> $x mere si <br> $y pere"); Observaţie ! ”<br>” se utilizează pentru salt la linie nouă! Vezi HTML... Capitolul 3. Limbajul PHP 113 Observaţii foarte importante ! 1. În loc de “” se pot folosi şi ‘’. Diferenţa este dată de faptul că, în cazul folosirii ‘’, dacă şirul de caractere conţine numele unei variabile, nu se afişează conţinutul variabilei, ci se afişează numele variabilei. Exemplul 3.41. Dacă comanda este echo ‘Am $x mere’ se va afişa Am $x mere. 2. Nu se pot folosi “” incluse între “” şi nici ‘’ incluse între ‘’. Exemplul 3.42. Secvenţele de mai jos sunt incorecte: echo " Am “o multime” de prieteni"; echo ‘ Am 'o multime' de prieteni’; 3. În schimb, putem include între “” caracterele ‘’. Exemplul 3.43. Instrucţiunea următoare echo " Am 'o multime' de prieteni"; afişează Am ‘o multime’ de prieteni. 4. În schimb, putem include între ‘’ caracterele “”. Exemplul 3.44. Instrucţiunea următoare echo ‘ Am “o multime” de prieteni’; afişează Am “o multime” de prieteni. 3.10. Funcţii pentru prelucrarea şirurilor de caractere În PHP există un set puternic de funcţii, asemănătoare celor din C++, care lucrează cu şiruri de caractere. De asemenea, există şi un operator de concatenare a şirurilor. Şirurile se memorează ca o succesiune de caractere ASCII. Putem adresa fiecare caracter al şirului, aşa cum suntem obişnuiţi, prin indicele său. Primul caracter are indicele 0. Exemplul 3.45. În secvenţa următoare, se afişează caracterul “U”: $sir="Un exemplu"; echo ($sir[0]); 114 Baze de date – curs introductiv Operatorul de concatenare a şirurilor este punctul. Exemplul 3.46. Se afişează "Ana are 9 mere!". Observaţi faptul că valoarea numerică a fost convertită implicit către şir. $x=9; $sir="Ana ". "are ". echo $sir; $x. " mere!"; Funcţia strlen(sir) returnează lungimea şirului (numărul de caractere ale şirului). Exemplul 3.47. În secvenţa de mai jos se afişează 10 (se numără şi blank-ul): $sir="Un exemplu"; echo strlen($sir); funcţia strpos(sir1, sir2, [poz_start]) caută dacă sir2 este subşir al lui sir1. În caz afirmativ, returnează poziţia de început a acestuia, altfel returnează false. Al treilea parametru specifică funcţiei indicele caracterului de unde să înceapă căutarea. Dacă nu este completat, căutarea începe din poziţia 0. Exemplul 3.48. La executarea secvenţei următoare, se afişează 5: $sir="Un exemplu"; echo (strpos($sir, "em")); Funcţia se utilizează şi pentru a testa dacă un şir include sau nu un anumit subşir. Dacă subşirul este găsit, se returnează poziţia de început a acestuia în subşir, dacă nu, se returnează false. După cum ştim, valoarea lui false este, de fapt, 0. Ce ne facem în cazul în care subşirul se găseşte şi începe în poziţia 0? Cum facem distincţia între acest caz şi cel în care subşirul nu este găsit? Priviţi secvenţa din exemplul de mai jos, care rezolvă corect problema şi subliniază importanţa operatorului “===”. Exemplul 3.49. Secvenţa este următoarea: <?php $sir="Un exemplu"; $gasit=strpos($sir, "U"); if ($gasit===false) echo ("Nu este"); else echo ("Este"); ?> Valoarea întoarsă de funcţie este reţinută de variabila $gasit. Pentru a face distincţie între false şi 0, se foloseşte operatorul “===”, care testează coincidenţa atât ca valoare, cât şi ca tip. De altfel, acesta este şi rostul unui astfel de operator. Acest procedeu se poate folosi şi pentru alte funcţii. Capitolul 3. Limbajul PHP 115 Funcţia strstr(sir1,sir2) returnează sir1 din poziţia în care a fost găsit sir2, dacă sir2 este subşir pentru sir1 sau false, în caz contrar. Exemplul 3.50. În secvenţa următoare se afişează “exemplu”: $sir="Un exemplu"; $gasit=strstr($sir, "ex"); if ($gasit===false) echo ("Nu este in sir"); else echo ($gasit); Funcţia strcmp(sir1,sir2) compară lexicografic (alfabetic) sir1 cu sir2. Valoarea returnată este: <0, dacă sir1 < sir2; >0, dacă sir1 > sir2; 0, dacă sir1 = sir2. Exemplul 3.51. Priviţi comparaţiile de mai jos: strcmp ("mama", "tata"); returnează un rezultat <0. strcmp ("ab", "a"); returnează un rezultat >0. strcmp ("abc", "abc"); returnează 0. Observaţie ! Comparaţia face distincţie între literele mari şi cele mici! Funcţia substr(sir,ind,[lung]) returnează subşirul şirului sir, care începe în poziţia ind şi are lungimea lung. Dacă parametrul lung este absent, se returnează şirul care începe în poziţia ind şi ţine până la sfârşitul şirului sir. Exemplul 3.52. După executarea secvenţei, se afişează “exemplu”: $sir="Un exemplu"; echo (substr($sir,3)); Exemplul 3.53. După executarea secvenţei, se afişează “ex”: $sir="Un exemplu"; echo (substr($sir,3,2)); Funcţia substr_replace(sir1, sir2, ind, [lung]) returnează şirul rezultat prin înlocuirea în sir1, a subşirului care începe în poziţia ind şi are lungimea lung cu sir2. Dacă parametrul lung este absent, sir2 înlocuieşte subşirul care începe cu ind şi ţine până la sfârşitul şirului sir2. Exemplul 3.54. Secvenţa următoare afişează “Un templu”: $sir="Un exemplu"; echo (substr_replace($sir,"t",3,2)); Funcţia strtoupper(sir) returnează şirul convertit în litere mari, iar funcţia strtolower(sir) returnează şirul convertit în litere mici. 116 Baze de date – curs introductiv 3.11. Masive în PHP În PHP există o “libertate” extraordinară de lucru cu masive. Începem prin a spune că masivele nu se declară. Exemplul 3.55. În script-ul următor, se creează şi se afişează un vector cu 5 componente: <?php for ($i=0;$i<5;$i++) $x[$i]=$i; for ($i=0;$i<5;$i++) echo( "$x[$i] <br>" ); ?> Exemplul 3.56. Se creează şi se afişează o matrice cu 5 linii şi 5 coloane. Elementele din linia i şi coloana j reţin valorile i+j. <?php for ($i=0;$i<5;$i++) for ($j=0;$j<5;$j++) $mat[$i][$j]=$i+$j; for ($i=0;$i<5;$i++) { for ($j=0;$j<5;$j++) echo($mat[$i][$j]." "); echo("<br>"); } ?> În PHP, indicii pot fi şiruri de caractere. Exemplul 3.57. Probaţi script-ul următor, unde un vector de indici nume de persoane, reţine numerele lor de telefon. Acesta este un tablou asociativ: <?php $x["Ioana"]="0724xxx"; $x["Valentina"]="031xxx"; $x["Paul"]="0211xxx"; $x["Cristian"]="0232xxx"; print $x["Ioana"]."<br>"; print $x["Valentina"]."<br>"; print $x["Paul"]."<br>"; print $x["Cristian"]."<br>"; ?> Faptul că există posibilitatea utilizării unor astfel de indici creează, pe lângă avantaje, probleme. Imaginaţi-vă că vreţi să parcurgeţi şi să listaţi un astfel de vector. Care este numărul componentelor? Să zicem că pe acesta îl reţinem într-o variabilă, dar ce valori ia variabila de ciclare? Funcţia count(nume_vector) returnează numărul de componente ale vectorului. Capitolul 3. Limbajul PHP 117 Exemplul 3.58. Pentru vectorul din script-ul anterior, cu ajutorul expresiei echo(count($x));, se afişează 4. Instrucţiunea de mai jos are rolul de a ne permite o parcurgere uşoară a unui vector cu numele vector, cu variabila de ciclare indice, în care citirea se face în variabila variabila: foreach(vector as indice=>variabila). Exemplul 3.59. Se creează un vector şi se parcurge cu foreach(...), afişând rezultatele parcurgerii: <?php $x["Ioana"]="0724xxx"; $x["Valentina"]="031xxx"; $x["Paul"]="0211xxx"; $x["Cristian"]="0232xxx"; foreach($x as $i=>$nume) echo($i." ".$nume."<br>"); ?> Iată ce se afişează, alăturat! Există şi alte funcţii care permit accesarea cu uşurinţă a elementelor vectorului. Ideea de la care se pleacă este următoarea: există un pointer care întotdeauna este asupra unui element al vectorului. Funcţia current(vector); returnează valoarea reţinută de elementul din vector asupra căruia se găseşte pointer-ul. Funcţia key(vector); returnează indicele elementului din vector asupra căruia se găseşte pointer-ul. Funcţia next(vector); deplasează pointer-ul pe elementul următor din vector şi returnează valoarea reţinută de acesta. Funcţia prev(vector); deplasează pointer-ul pe elementul anterior al vectorului şi returnează valoarea reţinută de acesta. Exemplul 3.60. Analizaţi script-ul de mai jos: <?php $x["Ioana"]="0724xxx"; $x["Valentina"]="031xxx"; $x["Paul"]="0211xxx"; $x["Cristian"]="0232xxx"; echo(current($x)." ".key($x)."<br>"); echo(next($x)." ".key($x)."<br>"); echo(prev($x)." ".key($x)."<br>"); echo(count($x)."<br>"); ?> 118 Baze de date – curs introductiv Dacă, în general, sortarea unui vector (tablou) se poate efectua cu ajutorul unui algoritm oarecare de sortare, în cazul unui tablou asociativ, cum lesne vă puteţi da seama, problema este mai complicată. Din acest motiv, PHP-ul pune la dispoziţia programatorilor funcţii specifice. Exemplul 3.61. Pentru a testa modul în care acestea efectuează sortarea, vom folosi script-ul următor, în care linia “metoda de sortare“ se va înlocui cu funcţia respectivă: <?php $x["Ioana"]="0724xxx"; $x["Valentina"]="031xxx"; $x["Paul"]="0211xxx"; $x["Cristian"]="0232xxx";\ foreach($x as $i=>$nume) echo($i." ".$nume."<br>"); //metoda de sortare ?> Funcţia Asort(vector) sortează crescător vectorul după valoarea reţinută de fiecare element. Rezultatul este prezentat în figura alăturată. Funcţia Arsort(vector) sortează descrescător vectorul după valoarea reţinută de fiecare element. Rezultatul este prezentat alăturat. Funcţia Ksort(vector) sortează crescător vectorul după indici (vezi figura alăturată). Funcţia Krsort(vector) sortează descrescător vectorul după indici. Rezultatul este cel alăturat. Funcţiile sort() şi rsort() sortează crescător, respectiv descrescător, un masiv de indici 0, 1, ..., n. În cazul în care avem un masiv asociativ, vechile valori ale indicilor se pierd (vezi figura alăturată). Capitolul 3. Limbajul PHP 119 3.12. Variabile cookie Atunci când utilizarea Internet-ului a luat amploare, s-a pus următoarea problemă: cum trebuie procedat pentru ca server-ul să utilizeaze anumite opţiuni ale utilizatorului, astfel încât, atunci când acesta intră pe site, să nu mai piardă timpul selectându-le din nou? Mai mult, dacă se dispune de astfel de informaţii, pagina afişată pentru un anumit utilizator ar putea fi personalizată. Problema a fost rezolvată de cei de la firma Netscape în anul 1994. Mecanismul care stă la baza acestei probleme se bazează pe memorarea, pe calculatorul vizitatorului unui anumit site, a unor informaţii sub forma unor mici fişiere text. Operaţia poate fi comandată de pe server şi tot de pe server se poate comanda citirea, actualizarea sau ştergerea acestor mici fişiere, numite uzual, prin abuz de limbaj, variabile cookie. În PHP se poate lucra foarte uşor cu variabilele cookie. Pentru a crea o variabilă cookie se utilizează funcţia setcookie (nume_variabila, valoare, data_expirare) Exemplul 3.62. În script-ul de mai jos, se creează o variabilă cookie numită copac. Variabila reţine valoarea verde şi expiră într-o oră. <?php // creeaza o variabila cookie setcookie("copac", "verde", time()+3600); ?> Observaţii ! 1. Nu pot exista simultan mai mult de 20 de variabile cookie. Dacă se creează una în plus, prima creată este ştearsă automat. 2. Pentru a şterge o variabilă cookie se creează o alta cu acelaşi nume, dar cu data de expirare înaintea datei curente (de exemplu, time()-1). Pentru a citi o variabilă cookie se utilizează conţinutul unui masiv asociativ, numit $HTTP_COOKIE_VARS. Fiecare componentă a sa, de indice numele variabilei cookie, reţine valoarea variabilei cookie. Exemplul 3.63. În script-ul de mai jos se afişează valoarea variabilei cookie creată de script-ul anterior: <?php // afiseaza verde echo $HTTP_COOKIE_VARS["copac"]; ?> Analizaţi şi exemplele următoare … 120 Baze de date – curs introductiv Exemplul 3.64. În script-ul de mai jos se creează 20 de variabile cookie numite V1, V2, ..., V20 care reţin respectiv 1, 2, ..., 20: <?php for ($i=1;$i<=20;$i++) setcookie("V".$i, $i, time()+10000); echo "OK complet" ?> Exemplul 3.65. În script-ul de mai jos se afişează conţinutul variabilelor cookie existente pe calculatorul vizitatorului: <?php foreach($HTTP_COOKIE_VARS as $i=>$nume) echo $i." ".$nume."<br>"; ?> Exemplul 3.66. Script-urile care urmează exemplifică modul în care se pot reţine anumite informaţii pe care utilizatorul le-a tastat o dată. Apelat pentru prima dată, atunci când nu există variabila cookie nume, se cere numele vizitatorului. Acesta va introduce numele şi va apăsa butonul insereaza (vezi fig. din stânga). Dacă respectivul vizitator, reintră pe acel site, reapelând script-ul, acesta va identifica variabila cookie nume, va prelua numele memorat şi, în loc ca vizitatorului să i se ceară din nou numele, se va afişa mesajul (vezi fig. din dreapta). <?php $gasit=0; // se cauta daca exista variabila cookie nume foreach($HTTP_COOKIE_VARS as $i=>$v) if($i=="nume") $gasit=1; //daca exista se afiseaza un mesaj if ($gasit==1) echo " Bine ati venit pe site-ul nostru domnule: ".$HTTP_COOKIE_VARS["nume"]; // daca nu exista, se afiseaza cele necesare pentru // identificare si se creeaza variabila nume else echo "<FORM action='cc.php' method ='post'>"."Nume<br>". "<input name='Nume' type='text'>;". "<br>". "<input type='submit', value='insereaza'>"; ?> Capitolul 3. Limbajul PHP 121 3.13. Utilizarea în comun a limbajelor PHP şi MySQL 3.13.1. Crearea unei baze de date În acest paragraf prezentăm modul în care se utilizează împreună PHP şi MySQL. Vom presupune că server-ul care găzduieşte site-ul dispune de soft-ul necesar. Desigur, prezentarea aplicaţiilor se va face în continuare pe propriul calculator, prin utilizarea pachetului EasyPHP. Orice script PHP va fi scris în folder-ul www, inclus în folder-ul EasyPHP (versiunea pe care o aveam în momentul scrierii cărţii pe calculatorul nostru este 14). Orice script PHP care lucrează cu MySQL trebuie să se conecteze la acesta. Pentru conectare, se utilizează funcţia mysql_connect() sub forma: resursa = mysql_connect("nume_gazda","nume_utilizator", "parola") În cazul nostru, numele gazdei este http://localhost:3388, iar numele utilizatorului şi parola sunt cele utilizate în Anexa 1, atunci când am creat parola pentru root. În cazul în care conectarea a reuşit se returnează resursa, altfel, se returnează FALSE. 3388 este portul folosit pentru a comunica cu serverul. Pentru a da o comandă MySQL dintr-un script PHP se utilizează funcţia mysql_query(), care primeşte instrucţiunea sub forma unui şir de caractere. Şi aici, în caz că operaţia a reuşit, se returnează o valoare diferită de 0, altfel se returnează 0. resursa = mysql_query(instructiune_mysql) Exemplul 3.67. Se cere să se scrie un script, cre_baza.php, care creează baza de date, numită baza. Practic, script-ul va realiza conexiunea la MySQL, după care va da comanda de creare a bazei de date. În ambele operaţii, în caz de nereuşită, se va da un mesaj de eroare lămuritor. <?php // conectarea la MySQL $msql=mysql_connect("localhost:3388","root","parolamea"); if(!$msql) { echo "Nu s-a realizat conectarea la MySQL!"; exit; } // crearea bazei de date numita baza $rezultat=mysql_query("CREATE DATABASE baza"); if (!$rezultat) echo "Nu s-a reusit crearea bazei de date..."; else echo "Baza de date a fost creată cu succes!" ?> 122 Baze de date – curs introductiv Pentru a rula script-ul, se dă următoarea comandă browser-ului: http://192.168.1.217/cre_baza.php Dacă dorim ca mesajele de eroare să ne ajute mai mult în depanare, vom utiliza două funcţii MySQL. Funcţia string mysqlerrno() returnează codul numeric al ultimei erori. Lista parametrilor este vidă. Funcţia string mysqlerror() returnează mesajul asociat ultimei erori. Şi în cazul ei, lista parametrilor este vidă. Exemplul 3.68. Înlocuiţi ultimul echo din scriptul anterior cu: echo mysql_errno().":".mysql_error(); În cazul în care veţi rula din nou script-ul, va apărea o eroare care se datorează faptului că se încearcă crearea unei baze de date care există deja. Iată cum este afişată eroarea în acest caz: 1007: Can’t create database ‘baza’; database exists Codul arătă acum ca mai jos: <?php //conectarea la MySQL $msql=mysql_connect("localhost:3388","root","parola23"); if(!$msql) { echo "Nu s-a realizat conectarea la MySQL!"; echo mysql_errno()." : ".mysql_error(); exit; } //crearea bazei de date numita baza $rezultat=mysql_query("CREATE DATABASE baza"); if (!$rezultat) echo "Nu s-a reusit crearea bazei de date."; else echo "Baza de date a fost creata cu succes!"; ?> Bineînțeles, acesta va fi apelat o singură dată deoarece ulterior vom obține o eroare – baza de date deja există, nu? Capitolul 3. Limbajul PHP 123 3.13.2. Accesul la o bază de date şi crearea unui tabel în baza de date Pentru a avea acces la o bază de date, se utilizează funcţia de mai jos, care returnează, aşa cum suntem obişnuiţi, o valoare diferită de 0 în caz de reuşită şi 0, în caz contrar. bool mysql_select_db(nume_baza) După realizarea accesului, putem crea tabelul prin utilizarea instrucţiunii mysql_query(). Exemplul 3.69. Se cere să se scrie un script, cre_tabel.php, prin care să se creeze un tabel, numit vizitatori, care reţine, pentru fiecare persoană trecută în tabel, numele, vârsta şi adresa de e-mail. <?php // conectarea la MySQL $msql=mysql_connect("localhost:3388","root","parolamea"); if(!$msql) { echo "Nu s-a realizat conectarea la MySQL."; exit; } // conectarea la baza de date $baza=mysql_select_db("baza"); if (!$baza) { echo mysql_errno().":".mysql_error(); exit; } // crearea tabelului $interogare="CREATE TABLE vizitatori (nume CHAR(30), varsta INT, email CHAR(20) PRIMARY KEY)"; $rezultat=mysql_query($interogare); if (!$rezultat) echo mysql_errno().":".mysql_error(); ?> Testaţi script-ul apelând: http://192.168.1.217/cre_tabel.php. UTIL În toate operaţiile pe care le vom face de acum înainte cu această bază de date vom avea nevoie, în mod evident, de conectarea la MySQL şi accesul la baza de date. Pentru ca să nu repetăm această secvenţă, se scrie script-ul de mai jos, numit conectare.php(): <?php // conectarea la MySQL $msql=mysql_connect("localhost","root","parolamea"); 124 Baze de date – curs introductiv if(!$msql) { echo "Nu s-a realizat conectarea la MySQL..."; exit; } // conectarea la baza de date $baza=mysql_select_db("baza"); if (!$baza) { echo mysql_errno().":".mysql_error(); exit; } ?> Pentru ca un script să includă un altul şi să-l execute se foloseşte funcţia MySQL de mai jos, care are ca parametru numele script-ului care urmează să fie inclus: require_once(nume_script); Exemple de utilizare ale acestui script veţi găsi în paragrafele următoare. 3.13.3. Inserarea liniilor în tabel Am creat o bază de date, numită baza, cu un tabel, numit vizitatori. Rămâne să introducem date în acest tabel. Vom presupune că fiecare utilizator al bazei de date se autoînregistrează, scriind datele în nişte edit-uri afişate de pagină. Problema se rezolvă în două părţi. A) Se creează o pagină, s-o numim i_date.php, care conţine edit-urile prin care se introduc datele şi butonul la apăsarea căruia datele sunt transmise către server. Modul de creare a unei astfel de pagini a fost descris, prin urmare prezentăm numai codul ei şi tabelul pe care-l afişează. La apăsarea butonului inserează se apelează script-ul insereaza.php aflat pe server (în cazul nostru, în folder-ul www). Pagina va fi apelată prin comanda următoare: http:// 192.168.1.217/cre_tabel.php. Codul sursă pentru pagina i_date.php este: <html> <head></head> <body> <form action="insereaza.php" method ="post"> Nume <br> <input name="nume" type="text">; <br> Varsta <br> <input name="varsta" type="text">; <br> Adresa e-mail <br> <input name="email" type="text">; <br> <input type="submit", value="insereaza"> </form> </body> </html> Capitolul 3. Limbajul PHP 125 B) După ce vizitatorul paginii respective îşi completează datele şi apasă butonul insereaza, acestea sunt transmise către server şi sunt preluate de către script-ul insereaza.php. Acesta preia datele şi le introduce în tabelul vizitatori. <?php require_once("conectare.php"); $nume= $_POST['nume']; $varsta= $_POST['varsta']; $email= $_POST['email']; $cerere="INSERT INTO vizitatori (nume,varsta,email) VALUES('$nume',$varsta,'$email')"; $rezultat=mysql_query($cerere); if (!$rezultat) echo mysql_errno().":".mysql_error(); ?> Observaţii ! 1. Pentru câmpurile nume şi email datele sunt de tip şir de caractere. Ele sunt memorate de variabilele $nume şi $email. Pentru a le introduce ca şiruri, vom trece variabilele între apostrofuri: ‘$nume’, ‘$email’. 2. După introducerea unui rând, testaţi dacă acesta a fost sau nu folosind comanda SQL sau direct din phpmyadmin: SELECT * FROM vizitatori. Este foarte important ca datele introduse să fie validate. Astfel, numele trebuie să fie nevid, varsta trebuie să fie o valoare numerică mai mare sau egală ca 10 şi mai mică sau egală cu 90 şi adresa de e-mail trebuie să conţină caracterul ‘@’. Vom rescrie script-ul, de această dată cu validări. Observaţie ! Atunci când se depistează o eroare, se afişează un mesaj lămuritor şi se reafişează formularul de introducere a datelor! <?php require_once("conectare.php"); $nume= $_POST['nume']; if (strcmp($nume,"")==0) {echo "Nu ati scris numele"; require_once("i_date.php"); exit; } $varsta= $_POST['varsta']; if (!is_numeric($varsta) || $varsta<10 {echo "Varsta incorecta"; require_once("i_date.php"); exit; } ||$varsta>90) 126 Baze de date – curs introductiv $email= $_POST['email']; if (!strpos($email,"@")) { echo "Adresa de e-mail gresita"; require_once("i_date.php"); exit; } $cerere="INSERT INTO vizitatori (nume,varsta,email) VALUES('$nume',$varsta,'$email')"; $rezultat=mysql_query($cerere); if (!$rezultat) echo mysql_errno().":".mysql_error(); ?> 3.13.4. Afişarea unui tabel În acest paragraf vom arăta modul în care se listează rezultatul unei interogări. După cum am văzut, o interogare se realizează cu mysql_query(). Ea returnează o valoare care, în cazul în care interogarea a reuşit, este diferită de 0. În cazul în care rezultatul unei interogări este alcătuit din mai multe rânduri, ca de exemplu în cazul listării unui tabel, nu se afişează direct rezultatul, aşa cum suntem obişnuiţi. În astfel de cazuri, pornind de la valoarea returnată de mysql_query(), se afişează tabelul rând cu rând. Funcţia mysql_fetch_array() returnează un rând al unei interogări (în cazul de faţă, al unui tabel) sub forma unui tablou asociativ. După returnarea rândului, automat se trece la rândul următor. array mysql_fetch_array(resursa) Pornind de la tabloul asociativ returnat, se poate accesa orice câmp al interogării. Exemplul 3.70. Analizaţi script-ul următor, unde se afişează datele introduse în tabel. Rezultatul îl puteţi observa în figura alăturată. <?php require_once("conectare.php"); $cerere="SELECT * FROM vizitatori"; $rezultat=mysql_query($cerere); if (!$rezultat) echo mysql_errno().":".mysql_error(); // pentru fiecare rand al tabelului while($rand=mysql_fetch_array($rezultat)) echo $rand["nume"]." ".$rand["varsta"]." ". $rand["email"]."<br>"; ?> Capitolul 3. Limbajul PHP 127 Desigur, datorită formei inestetice, această afişare a tabelului nu ne convine. Din acest motiv, vom afişa tabelul, aşa cum îl observaţi alăturat. Pentru aceasta, vom rescrie script-ul afisare_tabel: <?php require_once("conectare.php"); $cerere="SELECT * FROM vizitatori"; // in urma interogarii, se returneaza tabelul $rezultat=mysql_query($cerere); if (!$rezultat) { echo mysql_errno().":".mysql_error(); exit; } // afisam datele sub forma unui tabel echo "<table border=5>"; // afisam capul de tabel Echo "<tr><td align='center'> <b>Nume</b><td><b>Varsta</b><td><b>Adresa e-mail</b>"; // Pentru fiecare rand al tabelului while($rand=mysql_fetch_array($rezultat)) { echo "<tr>"; echo "<td>".$rand["nume"]. "<td align='right'>".$rand["varsta"]."<td>". "<a href=mailto:".$rand["email"].">".$rand["email"]."</a>"; echo "</tr>"; } echo "</table>"; ?> Apelaţi script-ul prin: http:// 192.168.1.217/afisare_tabel.php. 128 Baze de date – curs introductiv 3.14. Aplicaţii 3.14.1. Inscrierea pe un site În unele site-uri se cere ca, în prealabil, vizitatorii respectivului site să se înscrie în baza de date a acestuia. De regulă, utilizatorului i se cere ID-ul (nickname-ul) şi parola. Acestea sunt memorate într-un tabel şi de câte ori utilizatorul doreşte să acceseze din nou site-ul, ele sunt cerute pentru identificare. Accesul la site se realizează numai atunci când ID-ul şi parola se regăsesc în baza de date. A) Pentru început, este necesar ca să se creeze o bază de date, de exemplu, inscrisi, care conţine un singur tabel, parole. Script-ul următor, btab.php, realizează cele de mai sus: <?php //conectarea la MySQL $msql=mysql_connect("localhost:3388","root","parola"); if(!$msql) { echo "Nu s-a realizat conectarea la MySQL "; exit; } //crearea bazei de date numita baza $rezultat=mysql_query("CREATE DATABASE inscrisi"); if (!$rezultat) { echo "Nu s-a reusit crearea bazei de date..."; exit; } $baza=mysql_select_db("inscrisi"); $interogare="CREATE TABLE parole ( nume CHAR(30) PRIMARY KEY, parola CHAR(10))"; $rezultat=mysql_query($interogare); if (!$rezultat) echo mysql_errno().":".mysql_error(); ?> În cele ce urmează, vom presupune că am rulat script-ul btab.php, deci baza de date şi tabelul sunt create: Capitolul 3. Limbajul PHP 129 B) Dacă un vizitator accesează site-ul, este afişată pagina alăturată, în care i se cere, fie să se identifice, în cazul în care este înscris deja, fie să se înscrie, în caz contrar. Pentru a realiza pagina respectivă, se foloseşte un simplu fişier HTML (sau PHP), vedeţi mai jos (index.php). În fapt, acesta conţine două ancore, către cele două script-uri PHP: identificare.php sau inscriere.php. Vizitatorul poate opta pentru oricare dintre ele: 130 Baze de date – curs introductiv <html> <title> Autentificare </title> <body> <h1>Bine ati venit pe site-ul...</h1> <a href="http://192.168.1.217/identificare.php"> Identificare</a> <br><br> <a href="http://192.168.1.217/inscriere.php">Inscriere</a> </body> </html> C) Dacă utilizatorul optează pentru înscriere, atunci este rulat script-ul inscriere.php, care afişează fereastra alăturată. Vizitatorului i se cere să introducă numele (ID-ul) şi parola. Atunci când se introduce parola, se afişează * pentru fiecare caracter introdus. Pentru a evita introducerea greşită a parolei, aceasta trebuie să fie introdusă de două ori. După introducerea datelor, vizitatorul va apăsa butonul “inscrie-te”. Efectul va fi acela de trimitere a datelor către server şi de apel al script-ului inscrie.php. <html> <body> <form action="inscrie.php" method ="post"> Nume<br> <input name="nume" type="text">; <br> Parola <br> <input name="parola1" type="password">; <br> Reintroduceti parola <br> <input name="parola2" type="password">; <br> <input type="submit", value="inscrie-te!"> </form> </body> </html> C1) Script-ul inscrie.php validează datele şi, în caz că acestea sunt valide, le înscrie în tabelul parole: - se testează dacă numele este nevid; dacă cele două parole coincid; dacă nu mai există un vizitator cu aceeaşi parolă (parola este cheie primară). În cazul în care una dintre aceste condiţii nu este îndeplinită, se afişează un mesaj de eroare corespunzător şi se reafişează formularul de introducere a datelor. Capitolul 3. Limbajul PHP <?php // conectare la mysql $msql=mysql_connect("localhost:3388","root","parola"); if(!$msql) { echo "Nu s-a realizat conectarea la MySQL "; exit; } // declarare baza folosita $baza=mysql_select_db("inscrisi"); //Citirea numelui - trebuie sa fie nevid $nume= $_POST['nume']; if (strcmp($nume,"")==0) {echo "Nu ati scris numele"; require_once("inscriere.php"); exit; } // citirea parolelor - trebuie sa fie identice $parola1= $_POST['parola1']; $parola2= $_POST['parola2']; if (strcmp($parola1,$parola2)!=0) {echo "parola incorecta"; require_once("inscriere.php"); exit;} // se incearca inscrierea in tabel $cerere="INSERT INTO parole (nume,parola) VALUES('$nume','$parola1')"; $rezultat=mysql_query($cerere); if (!$rezultat) { echo "Nume existent in baza de date"; require_once("inscriere.php"); } else echo "OK! Ati fost introdus in baza noastra de date"; ?> D) În cazul în care vizitatorul este deja înscris, va apela script-ul identificare.php prin intermediul ancorei respective. Acesta va cere numele şi parola, iar la apăsarea butonului “Conecteaza-ma” va trimite datele către server. Acestea sunt preluate de script-ul “verif.php”. <html> <body> <form action="verif.php" method ="post"> Nume<br> <input name="nume" type="text">; <br> Parola <br> <input name="parola" type="password">; <br> <input type="submit", value="Conecteaza-ma"> </form> </body> </html> 131 132 Baze de date – curs introductiv D1) Se testează dacă în tabelul parole există înregistrarea cu ID-ul şi parola introdusă de utilizator. În caz că există, se rulează, de exemplu, un script php, în acest caz, rezultat.php, altfel se cere ca datele să fie reintroduse: <?php // conectare la mysql $msql=mysql_connect("localhost:3388","root","parola"); if(!$msql) { echo "Nu s-a realizat conectarea la MySQL "; exit; } // declarare baza folosita $baza=mysql_select_db("inscrisi"); // citirea numelui si parolei $nume= $_POST['nume']; $parola= $_POST['parola']; // selectez, daca exista in tabel, randul corespunzator $cerere="SELECT nume FROM parole WHERE nume='$nume' AND parola='$parola'"; $rezultat=mysql_query($cerere); $rand=mysql_fetch_array($rezultat); //echo $rand['nume']; if ($rand["nume"]!=$nume) {echo "Numele si/sau parola sunt eronate"; require_once("identificare.php"); } else require_once("rezultat.php"); ; ?> Fişierul rezultat.php este: <html> <body> <h1> pagina ce ar trebui afisata... </body> </html> 3.14.2. Determinarea adresei IP şi a browser-ului utilizatorului Uneori se doreşte obţinerea unor informaţii despre vizitatorii unui site. Astfel, se poate afla adresa IP a vizitatorului, browser-ul utilizat de acesta sau sistemul de operare pe care acesta îl utilizează. Toate acestea pot fi stocate în baze de date. Informaţiile astfel dobândite sunt extrem de importante pentru aplicaţii precum: - Aplicaţii prin care vizitatorii votează răspunsul la o anumită întrebare. Pentru a evita ca un vizitator să voteze de mai multe ori într-o zi, într-un tabel se va memora adresa IP a acestuia. De la o anumită adresă IP se poate vota o singură dată sau o singură dată pe zi, caz în care se va reţine şi ziua votării. Capitolul 3. Limbajul PHP 133 - Aplicaţii prin care se contorizează vizitatorii unui site. Dacă o persoană vizitează site-ul într-o zi de mai multe ori, dar cu aceeaşi adresă IP, aceasta este numărată ca vizitator o singură dată. - Determinarea browser-ului are şi ea mai multe utilizări. Se ştie că, din păcate, diferite browser-e afişează uneori diferit acelaşi fişier download-at de pe server. Pentru a nu mai avea astfel de probleme, dacă se cunoaşte browser-ul utilizatorului, atunci server-ul expediază către calculatorul vizitatorului fişierul corespunzător. - Informaţii precum cele de mai sus pot fi folosite de site-urile care monitorizează vizitele la diferite alte site-uri şi întocmesc clasamentul acestora în funcţie de numărul de vizitatori. În acest paragraf vom învăţa să determinăm adresa IP şi browser-ul vizitatorului. $_SERVER este o variabilă predefinită de tip masiv asociativ. Cu ajutorul ei, script-ul PHP poate accesa o mulţime de informaţii. Pentru a afla adresa IP a vizitatorului se utilizează $_SERVER["REMOTE_ADDR"] şi pentru a afla informaţii despre browser se utilizează $_SERVER["HTTP_USER_AGENT"]. Exemplul 3.71. Mai multe informaţii puteţi obţine analizând script-ul următor care afişează browser-ul utilizatorului şi adresa IP a acestuia (vezi figura de mai jos): Rezultatele script-ului propus ca exemplu 1. Creaţi un contor pentru un site care să numere vizitatorii site-ului respectiv. Un vizitator cu o anumită adresă IP se numără o singură dată într-o zi, indiferent de numărul de vizite ale site-ului pe care acesta le efectuează. 134 Baze de date – curs introductiv 2. Creaţi un site care afişează o anumită întrebare, la care vizitatorii sunt invitaţi să răspundă alegând o opţiune dintre mai multe, afişate cu ajutorul unor butoane radio. Un vizitator cu o anumită adresă IP poate vota o singură dată într-o zi. În situaţia în care acesta încearcă să voteze de mai multe ori, se va afişa un mesaj prin care acesta este anunţat că a votat deja. Observaţie ! În ambele cazuri veţi crea o bază de date cu un singur tabel, care reţine adresa IP a vizitatorului şi (eventual) data efectuării vizitei respective. 3.14.3. Cum se creează un forum? De câte ori n-am văzut sau am scris în diverse forum-uri... Unele sunt pe marginea unor articole din ziare, altele au scopuri precise, cu discuţii pe anumite teme (limbaje de programare, politică, literatură, etc.). În cele ce urmează ne propunem să creăm un forum mult simplificat, în care cei care doresc scriu mesaje, iar toţi ceilalţi le citesc. De ce avem nevoie pentru a realiza un astfel de forum? A) În primul rând, este necesară o bază de date, s-o numim forum şi un tabel, să-l numim mesaje. Un rând al tabelului va conţine un mesaj. În acest sens, va exista un număr de ordine pentru mesajul respectiv (nr), id-ul celui care a scris mesajul (id), titlul mesajului (titlu) şi textul propriu-zis al acestuia (textul). Script-ul următor, să-l numim f1.php, creează baza de date şi tabelul. Iniţial, tabelul este vid. <?php // conectarea la MySQL $msql=mysql_connect("localhost:3388","root","parola"); if(!$msql) { echo "Nu s-a realizat conectarea la MySQL..."; exit; } // crearea bazei de date numita baza $rezultat=mysql_query("CREATE DATABASE forum"); if (!$rezultat) { echo "Nu s-a reusit crearea bazei de date..."; exit; } $baza=mysql_select_db("forum"); $interogare="CREATE TABLE mesaje(nr INT UNIQUE KEY AUTO_INCREMENT, id CHAR(20), titlu CHAR(100), textul LONGTEXT)"; $rezultat=mysql_query($interogare); if (!$rezultat) echo mysql_errno().":".mysql_error(); ?> Capitolul 3. Limbajul PHP 135 B) Acum este necesar un script prin care utilizatorul scrie un mesaj: Practic, acesta trebuie să introducă mesajul, adică informaţiile cerute de tabel şi la apăsarea butonului “Adauga mesajul” să fie trimis către server. <html> <head></head> <body> <form action="insereaza.php" method ="post"> ID <br> <input name="id" type="text"> <br> Titlu <br> <input name="titlu" type="text"> <br><br> Mesajul dvs. <br> <textarea name="mesajul" rows="10" cols="30"></textarea> <br> <input type="submit", value="Adauga mesajul"> </form> </body> </html> 136 Baze de date – curs introductiv C) Pe server, informaţiile transmise de către cel care a scris mesajul sunt preluate de către script-ul insereaza.php. Practic, se verifică dacă fiecare câmp în parte este nevid, după care mesajul este memorat în tabel: <?php // conectarea la MySQL $msql=mysql_connect("localhost:3388","root","parola23"); if(!$msql) echo "Nu s-a realizat conectarea la MySQL "; // conectarea la baza de date $baza=mysql_select_db("Forum"); if (!$baza) echo mysql_errno().":".mysql_error(); $id= $_POST['id']; if (strcmp($id,"")==0) { echo "Nu ati introdus ID-ul"; require_once("f2.php"); exit; } $titlu= $_POST['titlu']; if (strcmp($titlu,"")==0) { echo "Nu ati scris titlul"; require_once("f2.php"); exit; } $mesajul= $_POST['mesajul']; if (strcmp($mesajul,"")==0) { echo "Nu ati scris mesajul"; require_once("f2.php"); exit; } $cerere="INSERT INTO mesaje (id,titlu,textul) VALUES('$id','$titlu','$mesajul')"; $rezultat=mysql_query($cerere); if (!$rezultat) echo mysql_errno().":".mysql_error(); ?> D) O persoană care doreşte să vizualizeze mesajele scrise de alţii, va apela script-ul următor, f3.php. Practic, acesta afişează tabelul mesajelor. După cum observaţi, pentru un mesaj, nu este afişat textul propriu-zis: Pentru a-l vizualiza pe acesta, va trebui să se execute clic pe titlul mesajului. De aici rezultă că titlul va trebui afişat ca un link. Aici intervine ceva interesant. Atunci când se execută clic, se va apela un alt script, f4.php, care va afişa mesajul cerut. Problema este că va trebui să fie cunoscut numărul curent al mesajului respectiv. Prin urmare, este necesar un link care să transmită şi un parametru. Este necesar un apel de forma: f4.php?nr_mesaj. De exemplu, dacă numărul mesajului este 3, vom scrie: f4.php?3. În rest, script-ul f3 afişează datele într-un tabel, aşa cum suntem deja obişnuiţi. <?php $msql=mysql_connect("localhost:3388","root","parola"); Capitolul 3. Limbajul PHP 137 if(!$msql) {echo "Nu s-a realizat conectarea la MySQL "; exit; } //conectarea la baza de date $baza=mysql_select_db("forum"); if (!$baza) {echo mysql_errno().":".mysql_error(); exit;} $cerere="SELECT * FROM mesaje"; // in urma interogarii, se returneaza tabelul $rezultat=mysql_query($cerere); if (!$rezultat) { echo mysql_errno().":".mysql_error(); exit; } echo "<table border=5>"; // afisam capul de tabel echo "<tr> <td align='left'><b>nr.curent</b> <td align='center'><b>ID</b><td><b>Titlu mesaj</b>"; while($rand=mysql_fetch_array($rezultat)) { echo "<tr>"; echo "<td align='right'>".$rand["nr"]. "<td>".$rand["id"]."<td>"."<a href=http://localhost/f4.php?".$rand["nr"]. ">".$rand["titlu"]."</a>"; echo "</tr>"; } echo "</table>"; ?> E) Script-ul de mai jos, f4.php, apelat de f3.php, primeşte ca parametru de intrare numărul mesajului. Practic, apelează MySQL-ul, deschide baza de date şi afişează corpul mesajului (textul propriu-zis al acestuia), indentificându-l cu ajutorul unei interogări: <?php $nr= $_SERVER["QUERY_STRING"]; $msql=mysql_connect("localhost","root","parolamea"); if(!$msql) {echo "Nu s-a realizat conectarea la MySQL"; exit;} // conectarea la baza de date $baza=mysql_select_db("forum"); if (!$baza) {echo mysql_errno().":".mysql_error(); exit;} $cerere="SELECT textul FROM mesaje WHERE nr=$nr"; // in urma interogarii, se returneaza tabelul $rezultat=mysql_query($cerere); $rand=mysql_fetch_array($rezultat); echo $rand["textul"]; ?> 138 Baze de date – curs introductiv 1. Adăugaţi script-ului f3.php o secvenţă în urma căreia, la apăsarea unui buton, să se poată introduce un mesaj (practic, să fie apelat script-ul f2.php). 2. Reproiectaţi aplicaţia astfel încât să existe posibilitatea unui răspuns la un mesaj. 3. Dezvoltați aplicația astfel încât să arate ca un mic forum pe un subiect ales la alegere. Permiteți doar utilizatorilor autentificați să poată posta un subiect nou. 3.15. Proiectarea paginilor web În acest moment, puteţi spune că aveţi cunoştinţe solide astfel încât să puteţi dezvolta o aplicaţie hipermedia. Definirea modelului conceptual vă ajută la etapa de proiectare a unei baze de date, normalizarea acesteia asigură robusteţe şi siguranţă, iar prin intermediul limbajului MySQL o puteţi implementa. Limbajele HTML şi CSS oferă nenumărate posibilităţi de realizare a interfeţei cu utilizatorul final, iar PHP-ul, accesul la baza de date stocată pe server. Observaţie ! Recitiţi Capitolul 1 pentru a vă reaminti noţiunile fundamentale care privesc proiectarea bazelor de date! O planificare riguroasă a proiectului este imperativă şi de cele mai multe ori, ducerea la bun sfârşit a acestuia necesită o întreagă echipă care să lucreze coordonat. Astfel, în cazul în care nu realizaţi proiectul singur, trebuie să formaţi o echipă care să-şi distribuie sarcinile cât mai eficient, în funcţie de aptitudini. Puteţi revedea subcapitolul 1.4. care descrie managementul de proiect. Nimeni nu “pleacă la drum” fără un plan bine pus la punct. De aceea, înainte de toate, trebuie să fixaţi clar toate etapele pe care trebuie să le parcurgeţi. 1. Trebuie să ştiţi cui i se adresează site-ul pe care doriţi să-l dezvoltaţi şi bineînţeles, discutaţi cu beneficiarul acestuia pentru a obţine toate informaţiile de care aveţi nevoie. În funcţie de datele pe care acesta doreşte să le cunoască despre vizitatorii site-ului sau pe cele care vrea să le reţină în urma interacţiunii cu aceştia, puteţi realiza modelul conceptual al bazei de date. 2. Analizaţi cu atenţie modelul conceptual, iar dacă este cazul, utilizaţi tehnica de normalizare a datelor pentru a preveni eventualele anomalii. 3. Utilizând modelul relaţional al bazelor de date, implementaţi modelul conceptual, iar cu ajutorul limbajului MySQL, creaţi baza de date! 4. Interfaţa grafică cu utilizatorul trebuie să fie bine structurată, simplu de utilizat, iar datele de intrare şi cele de ieşire trebuie alese corespunzător. Capitolul 3. Limbajul PHP 139 Nu uitaţi ! Realizarea interfeţei grafice reprezintă o etapă esenţială pentru că paginile web sunt unicul loc unde utilizatorul interacţionează în mod direct cu aplicaţia dvs. Upload-aţi pe server fişierele html astfel formate şi testaţi-le cu atenţie! 5. Realizaţi formularele care asigură colectarea şi trimiterea datelor către server. Multe aplicaţii hipermedia conţin formulare care au câmpuri opţionale, permiţând colectarea unor informaţii suplimentare despre utilizatori. 6. Creaţi script-urile PHP care să prelucreze datele introduse de utilizator şi care să răspundă comenzilor recepţionate. 7. La final, testaţi aplicaţia pentru a elimina anumite erori care pot apărea! Observații De multe ori, codurile sursă pot avea sute de linii de cod, la care trebuie să adăugaţi comentarii sugestive pentru a putea vizualiza rapid secţiuni din cadrul acestora. Corectitudinea datelor introduse de către utilizatori trebuie să fie asigurată prin “mesajele ajutătoare” sau de eroare care trebuie să se regăsească în cadrul paginilor web – se evită astfel introducerea unor informaţii eronate în baza de date. Cu ajutorul variabilelor cookie, puteţi oferi posibilitatea personalizării site-ului, în funcţie de preferinţele utilizatorului (de exemplu: diverse teme – care au la bază câte un stil – vezi CSS; afişarea informaţiilor de interes – listă cu diverse categorii, etc.). Nicio aplicaţie nu poate fi considerată în totalitate terminată, ea fiind deschisă la eventuale îmbunătăţiri. De asemenea, pentru a fi asigurat succesul site-ului web, actualizaţi frecvent informaţiile pentru a ţine la curent vizitatorii fideli! Important! Pentru ca posesorul site-ului să poată vizualiza informaţiile reţinute în baza de date, realizaţi o interfaţă (o pagină web) specială, stabilind de comun acord care date trebuie să fie afişate în respectivele rapoarte, grafice, etc. 140 Baze de date – curs introductiv 1. Care dintre afirmaţiile de mai jos sunt adevărate şi care sunt false? a) b) c) d) Un script PHP este executat de către browser. PHP-ul se instalează pe server. Un fişier HTML poate conţine un script PHP. Formularele se descriu în HTML. 2. “Variantă simplificată de test online”. Reluaţi problema 1. Veţi crea o pagină HTML care afişează problema şi oferă vizitatorului ca, acolo unde consideră că răspunsul este adevărat, să bifeze un buton de marcare. Datele se trimit către server. Pe server, răspunde un script PHP care, acolo unde vizitatorul a răspuns corect, afişează OK, iar unde nu a răspuns corect, afişează răspunsul corect şi o explicaţie. 3. Dintre afirmaţiile de mai jos, una singură este falsă. Care este? a) b) c) d) Funcţiile PHP admit parametri transferaţi prin referinţă. Funcţiile apelate de un script PHP trebuie să fie conţinute de acesta. Funcţiile PHP admit parametri transferaţi prin valoare. În PHP nu este necesar să declarăm tipul unei variabile. 4. Cum se testează online problema 3 (vedeţi problema 2)? 5. ”Validarea datelor”. Scrieţi un fişier HTML care conţine un formular prin intermediul căruia se pot trimite către server numele vizitatorului şi vârsta sa. Pe server, se va găsi un script care testează dacă numele este alfabetic şi dacă vârsta este între 5 şi 90 de ani. Dacă aceste condiţii sunt îndeplinite, browser-ul va afişa OK, altfel se vor afişa mesaje de eroare corespunzătoare. 6. ”Semnalarea erorilor găsite la validare”. Se cere să se scrie un formular prin care vizitatorul trimite către server un număr întreg din intervalul [1,10] şi un script PHP care verifică valoarea introdusă. În cazul în care valoarea introdusă este corectă, se va afişa OK, altfel se va da un mesaj de eroare, după care se va afişa din nou formularul (vezi figurile de mai jos). a) Formular iniţial b) Mesaj de eroare Capitolul 3. Limbajul PHP 141 7. ”Verificare parolă”. Se cere să se scrie un formular prin care i se cere vizitatorului să introducă o parolă. Pe server răspunde un script PHP care verifică parola. În caz de coincidenţă, se va afişa o pagină HTML, la alegerea dvs., iar altfel se va afişa un mesaj de eroare. 8. ”Imagini la alegere”. Să se creeze un formular care conţine o listă. Vizitatorul selectează, după nume, o anumită fotografie şi trimite selecţia sa către server. Acolo, un script PHP afişează imaginea, dar şi lista, pentru ca vizitatorul să poată selecta o altă imagine. 9. ”Afişarea unui tabel atunci când nu se cunosc de la început datele”. Să se creeze un formular care trimite 4 valori către server. Acolo se va găsi un script care afişează cele 4 valori sub forma unui tabel. Vedeţi mai jos. a) Formular iniţial b) Tabelul obţinut Figura 5.34. Pagini propuse pentru problema 9 (pentru verificare, rezolvările se găsesc la pag. 383) 142 Baze de date – curs introductiv Formaţi echipe de 2 - 4 elevi, alegeţi-vă o temă de proiect, fie din temele propuse în această secţiune, fie una propusă chiar de voi. Atenţie! Repartizaţi sarcinile în mod echitabil în cadrul echipei de proiect. Puteţi chiar organiza împreună cu cadrul didactic, un concurs la nivelul clasei sau al şcolii, la care să invitaţi şi alţi profesori din şcoală. Nu uitaţi să documentaţi fiecare presupunere pe care aţi făcut-o în realizarea proiectului! 1. “Şeful clasei”. Este de preferat ca orice clasă să aibă ales un elev care să se ocupe de problemele “administrative”. Dacă aveţi sau nu unul, este momentul unui nou scrutin! Nominalizaţi-vă un număr de 4-5 elevi care să candideze pentru acest titlu şi realizaţi o aplicaţie hipermedia care să permită sistemul “votului electronic”. Bineînţeles, problema poate fi extinsă: fiecare candidat să aibă o pagină separată, unde să poată să-şi facă o “campanie electorală“ cât mai interesantă, rezultatele parţiale sau finale să poată fi vizualizate on-line, etc. Mult succes! 2. “Forum”. Fie că este vorba de maşini sau calculatoare, probleme personale sau profesionale, tuturor ne place să “discutăm” pe Internet! Păreri sunt multe şi de multe ori ne place să ne amuzăm sau să deschidem un nou “topic” pe forum, în speranţa că măcar virtual să ne facem “auziţi”. Realizaţi un astfel de site web! 3. “Vânzare/Cumpărare”. Toată lumea a auzit de succesul site-ului ebay, recunsocut la nivel mondial în lumea comerţului electronic. Chiar dacă nu atât de complex, realizaţi şi voi un site care să permită: posibilitatea creării unui cont care să identifice unic fiecare vizitator; introducerea anunţurilor de tip vânzare/cumpărare; vizualizarea produselor existente în funcţie de categorie, preţ, etc.; afişarea unor rapoarte care să evidenţieze cele mai vândute produse, preferinţele obiectelor cumpărate în funcţie de vârstă, etc.; trimiterea unor mesaje între utilizatori – simularea unei căsuţe poştale. ANEXA 1 Instalarea mediului de dezvoltare EasyPHP Pentru a putea lucra cu MySQL și php, avem nevoie de un server de test. Cu ajutorul pachetului EasyPHP putem realiza toate acestea ușor pe calculatorul nostru, având apoi posibilitatea de a le studia chiar dacă nu avem acces la Internet. În ipoteza în care am învăţat să le folosim şi dorim să construim un site web, atunci vom închiria spaţiu pe server şi acolo vom transfera script-urile realizate pe propriul calculator. EasyPHP este open-source, deci gratuit, iar pachetul conține cam tot ce ne trebuie pentru moment a.î. să creăm propriul nostru webhosting server, adică Apache, MySQL, php, Nginx, Dev Tools. Adresa de unde poate fi descărcat este: https://www.easyphp.org/ În cazul nostru, vom alege opțiunea Webserver, versiunea 14.1 (sistemul de operare al autorului este Windows 8.1 Pro): 144 Baze de date – curs introductiv Efectuâm clic pe buton și se va deschide o nouă fereastră de unde se descarcă un executabil care, apelat, va instala programele din pachet (alegeţi opţiunile implicite). Executabilul downloadat de noi este ”EasyPHP-Webserver-14.1b2setup.exe”: Urmăm pașii la instalare cu atenție, alegând opțiunile potrivite pentru noi: Observăm faptul că avem nevoie de cel puțin 256.7MB spațiu pe disc, apoi programul se va instala în folderul menționat în mod automat de program. Nu modificăm nimic și apăsăm butonul Next. Anexa 1. Instalarea EasyPHP 145 Fereastra următoare ne indică numele programului accesibil din Start Menu, iarăși ales implicit și apăsăm din nou Next: Pe pagina următoare nu avem decât să alegem opțiunea Install: 146 Baze de date – curs introductiv Dacă totul a fost efectuat cu succes, va apărea mesajul următor: Suntem întrebați prin acel checkbox dacă dorim să lansăm în executare imediat serverul EasyPHP. În cazul meu sunt de acord și apăs butonul Finish. ... Aparent, nu s-a întâmplat nimic, însă putem observa faptul că pe bara de jos a sistemului de operare Windows, în dreapta, a apărut o nouă pictogramă ce ne indică faptul că programul nostru este deja funcțional: Efectuăm clic dreapta din curiozitate și apare următorul meniu: Alegem apoi prima opțiune, adică Open Dashboard. Anexa 1. Instalarea EasyPHP 147 Prima oară când apelăm această comandă va deschide o fereastră nouă a browser-ului nostru curent la adresa http://127.0.0.1:10000/ și ne va întreba dacă suntem de acord cu termenii și condițiile de utilizare pe care trebuie să le acceptăm apăsând butonul Agree: Se va afișa următoarea pagină web: 148 Baze de date – curs introductiv Observ niște informații despre noul nostru server, apoi citesc faptul că pentru a instala anumite Module, trebuie mai întâi să pornesc serverul HTTP: Am apăsat acest buton ce mă duce la zona de setări de unde instalez serverul HTTP Apache, apoi îl activez: Mai sus el este deja pornit.Super! Adresa la care vom accesa paginile noastre web de acum înainte este, în cazul meu, cea locală (URL) și anume: http://192.168.1.217:888/. Dacă o deschidem în browser, vom observa faptul că nu există implicit un fișier index, ci doar unul de test numit hello.php: Anexa 1. Instalarea EasyPHP 149 Precum putem citi din pagina de administrare, folderul care conține acest prim fișier este următorul: C:\Program Files\EasyPHP-Webserver-14.1b2\www mai exact, rădăcina website-ului (root) nostru local. În cadrul acestui director va trebui să reținem pe disc fișiere noastre care vor forma proiectul nostru web (imagini, fisiere html și php, etc.). ALTE CONFIGURĂRI Nu am terminat încă... În pagina de configurare observăm că există mai multe secțiuni, cum ar fi System, PHP, DB SERVER. Accesăm System și se va deschide următoarea pagină: Pentru a putea să avem acest server repornit automat atunci când deschidem calculatorul, trebuie să instalăm Serviciul Windows care se ocupă de acest lucru, așadar vom alege opțiunea install service. Dacă totul a fost efectuat cu succes, veți fi anunțat în acest sens. 150 Baze de date – curs introductiv Alegând apoi secțiunea DB SERVER, vom instala similar și serverul pentru bazele de date: Apoi, apăsați butonul start service. PHPMYADMIN În zona de administrare (Dashboard) care se găsește la adresa http://127.0.0.1:10000/ avem instalat inițial un singur modul și anume PhpMyAdmin care ne permite să vizualizăm bazele noastre de date. Pentru a deschide fereastra modulului, apăsați butonul Open: Anexa 1. Instalarea EasyPHP 151 Vom fi rugați să introducem username-ul (root) și parola de acces (nu o are setată) și se va deschide apoi fereastra principală: Evident, nu există nicio bază de date la început... Apăsați comanda Modifică parola pentru a introduce una nouă astfel încât accesul să fie restricționat: După ce ați ales una potrivită și complexă, apăsați butonul Execută. De acum accesul la phpMyAdmin va fi posibil doar cu ajutorul autentificării. 152 Baze de date – curs introductiv CONCLUZII Am reușit ușor să instalăm pe calculator EasyPHP. Bineînțeles că există o sumedenie de alte opțiuni care pot fi impuse unui server, care conduc spre o adevărată meserie, bazată pe multă experiență, noțiuni ce nu privesc direct documentul prezent. Spre exemplu, nu am rezolvat problema https vs. http sau instalarea unui modul precum cPanel, ș.a.m.d. Informatica necesită mult studiu individual, precum căutarea optimă a informației pe Internet folosind cuvinte cheie potrivite, analizând soluțiile oferite de alte persoane, ca și tine. De multe ori postezi o întrebare care are un răspuns rapid la doar un clic distanță. Spre exemplu, pentru a putea activa SSL trebuie să accesezi fișierul php.ini și să elimini “ ; ” din fața liniei de cod: ;extension=php_openssl.dll Trebuie doar să ștergi caracterul punct și virgulă pentru ca extensia să nu mai fie comentată din cod. Unde se găsește acest fișier? Păi, îl cauți unde a fost instalat programul EasyPHP, nu? În practică, noi reținem adesea datele pe un server extern, apelând la serviciile de găzduire dedicate promovate de diverse companii, unde vom avea acces facil la cPanel, phpMyAdmin, precum și alte unelte utile – transfer FTP, File Manager, alegerea versiunii limbajului php, diverse statistici și ... multe, multe altele. Mai exact, un Univers și – o meserie. Pentru moment, avem acces la serverul nostru folosind adresa: http://192.168.1.217:888/. Dacă dorim să modificăm portul 888 a.î. să nu mai fie necesară specificarea acestuia (portul 80), va trebui să modificâm o line de cod din fișierul httpd.conf care în cazul nostru se găsește în folderul: C:\Program Files\EasyPHP-Webserver-14.1b2\binaries\httpserver\conf Căutați linia de cod care conține ServerName 192.168.1.217:888/ și înlocuiți cu ServerName 192.168.1.217:80/ Va fi necesară oprirea și repornirea serverului HTTP din panoul de administrare (Dashboard) de la adresa: Anexa 1. Instalarea EasyPHP http://127.0.0.1:10000/ Oprim apoi repornim: De acum avem noile valori potrivite pentru IP-urile de acces: 153 Indicaţii / Răspunsuri Capitolul 1. Baze de date 1.1. Barem de corectare şi notare pentru testul 1: Răspunsurile corecte sunt: 1-b, 2-c, 3-b, 4-d, 5-b, 6-c 6 x 1.5 = Oficiu 1p Total 10 p 9p Barem de notare pentru testul 2: 1. 3 x 0,2 = 0,6 p 2. 8 relaţii x 0,30 p = 2,4 p (0,15 puncte pentru cardinalitatea relaţiei şi 0,15 puncte pentru opţionalitate) 3. 2 x 2 p = 4 p (pentru fiecare relaţie se acordă 4x0,15p=0,60 p; pentru opţionalitatea relaţiilor, 4x0,15p=0.60 p pentru cardinalitatea relaţiilor, 0,3 p pentru alegerea entităţii de intersecţie, 0,3 p pentru atributele entităţii de intersecţie, 0,2 p pentru UID). 4. 2 x 0.5 = 1 p Oficiu 2p Total 10 p 1.3. Barem de corectare şi notare pentru testul de autoevaluare: Răspunsurile corecte sunt: 1-c, 2-b, 3-b, 4-a. Răspunsul corect pentru punctul 5 este prezentat în figura alăturată: Se acordă: 1.5 p x 4 (pentru întrebările 1, 2, 3, 4) 2 p (pentru întrebarea 5) Oficiu 2 p Total 10 p Indicaţii / Rezolvări 155 Capitolul 2. MySQL Rezolvările problemelor propuse: 1. 1.1. SELECT nume_m FROM materiale WHERE depozit=’Depozit 2’; 1.2. SELECT depozit FROM materiale WHERE nume_m LIKE ‘%Material 3%’; 1.3. SELECT SUM(cantitate*pret_u) AS valoare_totsla_materiale FROM materiale; 1.4. SELECT depozit, SUM(cantitate*pret_u) AS valoare_totala FROM materiale GROUP BY depozit; 1.5. UPDATE materiale SET pret_u=1.1 pret_u WHERE nume_m=’material 1’; 1.6. DELETE from materiale WHERE nume_m=’material 3’; 2. 2.1. SELECT nume FROM imprumuturi WHERE nume LIKE ‘%Banca 1%’; 2.2. SELECT banca, SUM(suma) AS suma_imprumutata FROM imprumuturi GROUP BY banca; 2.3. SELECT nume, COUNT(suma) AS nr_imprumuturi FROM imprumuturi GROUP BY nume; 2.4. Pentru simplitate, descompunem problema în mai multe subprobleme. Ideea este de a crea un tabel de manevră, să-l numim ”pers”, care să reţină pentru fiecare persoană totalul sumei pe care a împrumutat-o. a) Creăm tabelul de manevră ”pers”: 156 Baze de date – curs introductiv CREATE TABLE pers AS SELECT nume, SUM(suma) AS suma_imprumutata FROM imprumuturi GROUP BY nume; b) Selectăm răspunsul la problemă, pornind de la tabelul ”pers”: SELECT nume, suma_imprumutata AS suma_maxima FROM pers WHERE suma_imprumutata=(SELECT MAX(suma_imprumutata) pers); FROM c) Întrucât nu mai este necesar, tabelul de manevră pers este şters. DROP TABLE pers; 2.5. a) Rezolvare cu subinterogare: SELECT DISTINCT nume FROM imprumuturi WHERE banca IN (SELECT banca FROM imprumuturi WHERE nume=’Ionescu Grigore’); b) Rezolvare utilizând mecanismul auto-uniunii: SELECT DISTINCT B.nume FROM imprumuturi AS A INNER JOIN imprumuturi AS B ON A.banca=B.banca AND A.nume=’Ionescu Grigore’; 3. 3.1. SELECT nume, COUNT(tip_p) AS nr_proprietati FROM proprietari, proprietati WHERE cod=cod_proprietar GROUP BY cod_proprietar; 3.2 SELECT nume FROM proprietari WHERE cod NOT IN (SELECT cod_proprietar FROM proprietati) 3.3. SELECT cod_proprietar FROM proprietati WHERE cod_proprietar NOT IN (SELECT cod FROM proprietari); 3.4. SELECT nume, SUM(valoare) AS valoare_proprietati FROM proprietari, proprietati WHERE cod=cod_proprietar GROUP BY cod_proprietar ORDER BY valoare_proprietati DESC; Indicaţii / Rezolvări 4 4.1. SELECT B.nume FROM baieti AS A, fete AS B, prieteni AS L WHERE A.nume=”mihai’ AND A.varsta=18 AND A.cod=L.cod_b AND L.cod_f=B.cod; 4.2. SELECT nume FROM fete WHERE cod NOT IN (SELECT cod_f FROM prieteni); 4.3. SELECT nume, COUNT(cod_f) AS nr_prietene FROM baieti, prieteni WHERE cod=cod_b GROUP BY cod_b; 4.4. SELECT nume, COUNT(cod_b) AS nr_prieteni FROM fete,prieteni WHERE cod=cod_f GROUP BY cod_f ORDER BY nr_prieteni DESC LIMIT 1; 4.5. SELECT AVG(A.varsta) AS medie_varsta_baieti, AVG (B.varsta) AS medie_varsta_fete FROM baieti AS A, fete AS B; Capitolul 3. PHP Rezolvările problemelor propuse: 1. a) F, b) A, c) A, d) A. 3.b). 6. Fişierul PHP care afişează formularul este: <html><head></head> <body> <form action="http://localhost:3388/testare.php" method="post"> Introduceti valoarea <br> <input type="text" name="valoarea"><br> <input type="submit" value="GO"> </form></body></html> 157 158 Baze de date – curs introductiv Script-ul este dat mai jos. Observaţi utilizarea lui require_once() care include în corpul script-ului fişierul care conţine formularul: <? $v=$_POST["valoarea"]; if ($v>=1 && $v<=10 ) echo "OK"; else {echo "Data introdusa este eronata"; require_once( "test_er.php");} ?> 9. <form action="http://localhost:3388/testare.php" method="post"> a11<input type="text" name="a11"><br> a12<input type="text" name="a12"><br> a21<input type="text" name="a21"><br> a22<input type="text" name="a22"><br> <input type="submit" value="GO"> </form> <? $a11=$_POST["a11"]; $a12=$_POST["a12"]; $a21=$_POST["a21"]; $a22=$_POST["a22"]; echo "<table border=5>"; echo " <tr> <td> $a11 </td><td> $a12 </td></tr>"; echo " <tr> <td> $a21 </td><td> $a22 </td></tr>"; echo "</table>"; ?> Indicaţii / Rezolvări 159 160 Baze de date – curs introductiv Copyright 2022 Editura L&S SOFT Toate drepturile asupra acestei lucrǎri aparţin editurii L&S SOFT. Reproducerea integralǎ sau parţialǎ a textului din aceastǎ carte este posibilǎ doar cu acordul în scris al editurii L&S SOFT. ISBN: 978-606-95233-4-6 https://ebooks.infobits.ro