Edit this page Backlinks This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== Tema 2 PP 2023 ====== <note important> **DEADLINE '' TODO '' ** * Temele trebuie trimise pe curs.upb.ro, în assignment-ul numit ''Tema 2''. * Temele ce nu sunt acceptate de validatorul de arhive **NU** vor fi punctate. * Vă sugerăm ca, inainte de trimitere, sa testați arhiva voastra cu validatorul de arhive. </note> **TODO**: ar mai trebui sa avem partea de inserare, stergere avand in vedere ca o sa avem doar query-uri de tip SELECT si JOIN? In aceasta tema veti implementa un **query engine** minimal, care operează pe tabele, pentru a facilita selectarea, inserarea, stergerea datelor existente dintr-un **tabel**. **TODO**: as propune urmatoarea descriere: În această temă veți implementa un **query engine** minimal, care operează pe **tabele**, pentru a facilita filtrarea(**SELECT**) si alăturarea lor(**JOIN**). Având aceste funcționalități, veti putea implementa **funcții statistice**, care să lucreze pe acestea. ==== Ce este un tabel? ==== Formatul unui tabel pentru acesta tema va fi CSV. De exemplu, pentru tabela **people**, un exemplu de conținut ar fi următorul: <code csv> id,name,age,sex,positionId 1,Turing,43,M,2xf 2,Curry,27,M,9cc 3,Radia,29,F,2xf, 4,Chomsky,94,M,7af </code> Pe prima linie sunt definite denumirile coloanelor (id,name,age,sex,positionId). Pe următoarele linii se află intrările din tabelă. Pentru a putea face operații pe o tabelă, aceasta va trebui sa aibe o reprezentare **internă** care să ajute acest fapt. O soluție simplă este ca aceasta să fie transformată dintr-un simplu String **CSV** într-o **matrice** de String-uri, de următorul tip: <code scala> type Table = List[List[String]] </code> ==== Tipuri de query-uri ==== * **SELECT**: are rolul de a filtra o tabelă pe baza unei condiții date legată de o coloană, precum si alegerea coloanelor care vor participa în rezultat. * **JOIN**: combină înregistrări din două tabele pe baza unor coloane comune. Cu ajutorul lor se vor putea implementa **funcții statistice**, cu ajutorul cărora se pot număra linii specifice din tabelă, se poate face media unui atribut etc. ==== Task 1: SELECT query ==== În prima etapă, va trebui implementată funcționalitate de **SELECT**. Cererea de tip **SELECT** lucrează pe o singură tabelă, având rolul de a filtra linile din aceasta, precum și de a alege ce coloane se vor afla în rezultat. <hidden Mai multe explicații> Pentru a avea o înțelegere mai bună a acesteia, urmează un exemplu din **SQL*plus**: <code sql> SELECT name, age FROM people WHERE age < 30; </code> * Pe prima linie este clauza //SELECT//, unde se specifică coloanele care se vor afla în rezultat. * Pe a doua linie este clauza //FROM//, unde se află tabela pe care va avea loc operația descrisă. * Pe ultima linie se află clauza //WHERE//, urmată de o condiție de filtrare a linilor tabelei **people**. În concluzie, rezultatul va fi o nouă tabelă, formată doar din persoanele mai tinere de 30 ani, având ca informații numele și vârsta. </hidden> Acesta va avea următoarea signatură: <code scala> def select(table: Table, columnsToExtract: List[String], columnToCompare: String, condition: (String => Boolean)): Table </code> Fiecare parametru are următoarea semnificație: * **table**: tabela primită, parsată din formatul CSV * **columnsToExtract**: lista cu denumirea coloanelor care vor fi prezente în rezultat * **columnToCompare**: coloana pe baza căreia va trebuie sa se facă filtrarea * **condition**: funcția de filtrare a tabelei Valoarea de **return** este tot o tabelă parsată, rezultată din filtrarea si proiecția celei primite ca input. ** Exemplu: ** <code scala> // pentru acest exemplu se va folosi // tabela din prima secțiune (people) select(people, List("name", "age"), "age", (age: String => age.toInt < 30)) </code> Rezultatul va trebui să fie tot o tabelă parsată, având linile din **people** cu coloana **age** mai mică decât 30. De asemenea, se vor păstra doar coloanele **name** și **age**: Reprezentarea rezultatului în format CSV: <code csv> name,age Curry,27 Radia,29 </code> ==== Task 2: JOIN query ==== Query-ul **join** combină două tabele, alăturând coloanele din acestea, be baza egalității între valorile din coloanele cu semnificație comună, specificate în cerere, sau pe baza unui predicat mai complex, care primește valori din cele două coloane. <code scala> // column1 este o coloană din primul tabel // column2 este o coloană din al doilea tabel // condition este predicatul în baza căruia se realizează operația def join(table1: Table, table2: Table, column1: String, column2: String, condition: (String, String) => Boolean): Table </code> Exemplu: <code scala> // Folosind tabela people, definită anterior, împreună cu tabela companies: companyId,jobId,roleType,isFilled 1,7,2XFF,yes 2,3,2XFF,no 3,5,abcd,yes O cerere de join între cele două poate arăta astfel: join(people, companies, "positionId", "roleType", (p: String, r: String) => p.equalsIgnoreCase(r)) Rezultatul conține toate coloanele din primul tabel, urmate de toate coloanele din al doilea tabel, mai puțin column2 </code> ==== Task 3: Statistici ==== În această etapă, vom folosi etapele anterioare pentru realizarea de statistici pentru datele existente. Statisticile vor reprezenta, în mare, o singură valoare care corespunde răspunsului unei cerințe. **Exemplu**: ''Care este media vârstei persoanelor din departamentul IT?'' Funcția asociată acestei întrebări trebuie să se folosească de etapele anterioare: * folosirea unui join query pentru a prelua persoanele din fiecare departament, * urmată de o procesare a listei rezultat pentru a returna doar oamenii din departamanetul ''IT'', * urmată de o procesasre pentru calcularea mediei vârstei. **TODO**: schimbare pseudocod astfel incat sa se conformeze la noua signatura a functilor <hidden> pseudocod pentru realizare: <code scala> val join = joinQuery("JOIN positions, persons ON id, positionId") val itPeople = join.filter( x => x._1 == "IT" ).head._2 val sumAge = itPeople.map(x => x.age).sum return sumAge / itPeople.size </code> </hidden> **Listă statistici de implementat**: - Care este **media** vârstei persoanelor de pe //<department_name>//? - Care este vârsta **medie** a persoanelor care au terminat, în medie, mai mult de //<percentage>// din task-uri? - **Câți** bărbați căștigă mai mult de //<monthly_salary>// pe lună? - Care este **raportul** dintre numărul de femei și numărul de bărbați care lucrează în cadrul departamentului de //<department_name>//? - Care este bonusul anual **mediu** al persoanelor mai tinere de //<age>// ani? ==== Alte informații ==== <note warning> **Folosiți un stil de programare funcțional. NU se vor accepta:** * **Efecte laterale** (de exemplu modificarea parametrilor dați ca input la funcție) * Folosirea colecților aflate în **scala.collection.mutable** * **var** (**val** este ok!) </note> <note>Scheletul se poate găsi la: {{'' TODO ''}}</note> <note> Validatorul de arhive se poate găsi la: {{ ''TODO''}} \\ Formatul arhivelor este: * '' TODO '' * ID.txt - acest fișier va conține o singură linie, formată din ID-ul unic al fiecărui student Numele arhivelor trebuie să fie de forma **<Nume>_<Prenume>_<Grupa>_T2.zip** (daca aveti mai multe prenume sau nume, le puteți separa prin '-') </note>