# Upiti nad više tabela ## ER dijagram: ![proizvodi baza v2](https://hackmd.io/_uploads/rywYRVbSp.png) ## Upiti: ### 1. Naći proizvod kojeg na lageru ima manje od 600, i čiji opis vrste proizvoda sadrži tekst *official logos and insignias*. ::: info ℹ️ Uputstvo Postoji više načina da se rade upiti nad više tabela. U rješenju ćemo pokazati svaki od njih, ali kasnije ćemo koristiti isključivo ***JOIN***. ::: ::: spoiler Rješenje 1. Za korišćenje više tabela u jednom upitu, moguće je navesti svaku od njih u **FROM** klauzuli, pri čemu u **WHERE** klauzuli moramo navesti vezu između stranog ključa jedne tabele sa primarnim ključem druge tabele. ``` SQL SELECT * FROM proizvodi, vrste_proizvoda WHERE proizvodi.vrsta_proizvoda_id = vrste_proizvoda.id AND vrste_proizvoda.opis LIKE "%official logos and insignias%"; ``` 2. Kada navodimo tabele, svakoj od njih možemo dati neki alias (obično prvo slovo) po kojem kasnije pozivamo tu tabelu, da ne bismo svaki put pisali puni naziv. ``` SQL SELECT * FROM proizvodi p, vrste_proizvoda vp WHERE p.vrsta_proizvoda_id = vp.id AND vp.opis LIKE "%official logos and insignias%"; ``` 3. Rješenje koje ćemo koristiti u ostalim primjerima je upotrebom ***JOIN***-a. Krećemo od jedne tabele kojoj pridružujemo (***JOIN***) drugu tabelu, pri čemu ponovo moramo navesti na koji način (***ON***) su te dvije tabele povezane. ``` SQL SELECT * FROM proizvodi p JOIN vrste_proizvoda vp ON p.vrsta_proizvoda_id = vp.id WHERE vp.opis LIKE "%official logos and insignias%"; ``` ::: warning ⚠️ Napomena Koji god način da se koristi, u svakom slučaju se podacima iz jedne tabele pridružuju odgovarajući podaci iz druge tabele i time se proširuje početni skup rezultata. Nakon toga se proširena tabela rezultata filtrira u zavisnosti od uslova. ::: ### 2. Naći broj radnika koji imaju kao nadređenog radnika sa emailom *abow@classicmodelcars.com*. ::: info ℹ️ Uputstvo Pošto je ovdje riječ o rekurziji, jednoj tabeli pridružujemo ponovo istu tu tabelu. Da bismo ih razlikovali, daćemo im odgovarajuće aliase. ::: ::: spoiler Rješenje ``` SQL SELECT * FROM zaposleni zap JOIN zaposleni nadr ON zap.nadredjeni_id = nadr.id WHERE nadr.email LIKE "abow@classicmodelcars.com"; ``` ::: info ℹ️ U ovom primjeru smo podacima iz tabele zaposleni, koji ima samo ID svog nadređenog, pridružili sve podatke o tom nadređenom (koji je isto zaposleni). Iz tih proširenih rezultata smo filtrirali samo one zaposlene kod kojih nadređeni ima traženi email. ::: warning ⚠️ Napomena Kada koristimo ***JOIN***, podrazumijeva se ***INNER JOIN***, a osim ***INNER JOIN***-a imamo i ***LEFT JOIN***, ***RIGHT JOIN*** i ***CROSS JOIN***. * ***(INNER) JOIN*** prikazuje samo one zaposlene koji imaju nadređenog, tj. kojima polje nadredjeni_id nije prazno, tj. one zaposlene koji su nadređeni makar nekome. * ***LEFT JOIN*** takođe prikazuje sve zaposlene koji imaju nadređenog, ali će prikazati i one koji nemaju nadređenog. Zove se ***LEFT*** jer uz podatke koji imaju vezu između dvije tabele, prikazuje i podatke iz "lijeve" tabele, a to je ona tabela koja je prva pozvana. * ***RIGHT JOIN*** takođe prikazuje sve zaposlene koji imaju nadređenog, ali će prikazati i one zaposlene koji nisu nadređeni nikome. Zove se ***RIGHT*** jer uz podatke koji imaju vezu između dvije tabele, prikazuje i podatke iz "desne" tabele, a to je ona tabela koja je druga pozvana. * ***CROSS JOIN*** prikazuje sve zaposlene koji imaju nadređenog, ali i one zaposlene koji nemaju nadređenog, kao i one koji nisu nadređeni nikome. ::: ### 3. Naći najmanji i najveći limit kredita kupaca sa kojima posluje radnik čiji email ima 31 karakter, a koji radi u poslovnici čiji broj telefona počinje sa +1 i čija ulica ne sadrži riječ Street. ::: info ℹ️ Uputstvo U ovom slučaju moramo da koristimo 3 tabele - kupci, zaposleni i poslovnice. ::: ::: spoiler Rješenje ``` SQL SELECT min(k.limit_racuna) as najmanji, max(k.limit_racuna) as najveci FROM kupci k JOIN zaposleni z ON z.id = k.odgovorni_zaposleni_id JOIN poslovnice P on p.id = z.poslovnica_id WHERE LENGTH(z.email) = 31 AND p.telefon LIKE "+1%" AND p.adresa NOT LIKE "%street%"; ``` ::: ### 4. Koliko porudžbina je u kojem statusu? ::: info ℹ️ Uputstvo Za brojanje rezultata smo već koristili funkciju ***COUNT()***, ali da bismo brojali *po određenom kriterijumu*, koristimo ***GROUP BY***, da grupišemo rezultate. ::: ::: spoiler Rješenje ``` SQL SELECT status, count(*) as broj_narudzbi FROM narudzbe GROUP BY status; ``` ::: warning ⚠️ Napomena Sve rezultate koje vrati upit ``` SQL SELECT * FROM narudzbe; ``` grupišu se pomoću GROUP BY klauzule, a sve agregatne funkcije (sum, count, avg) koje pozivamo u SELECT klauzuli se izvršavaju nad grupisanim podacima, za svaku od tih grupa pojedinačno. ::: ### 5. Koji su sve mogući statusi porudžbi? ::: info ℹ️ Uputstvo Možemo koristiti ***GROUP BY***, ali bolje rješenje je ***DISTINCT***, koje vraća samo jedinstvene rezultate za određenu kolonu. ::: ::: spoiler Rješenje ``` SQL SELECT status FROM narudzbe GROUP BY status; ``` ili ``` SQL SELECT DISTINCT status FROM narudzbe; ``` ::: ### 6. Prikaži ukupna plaćanja po datumu, sa nazivima kolona “Datum” i “Ukupna placanja”. ::: info ℹ️ Uputstvo Potrebno je grupisati rezultate po datumu, a onda sabrati sve plaćene iznose za te grupisane rezultate. ::: ::: spoiler Rješenje ``` SQL SELECT datum as Datum, SUM(iznos) as "Ukupna placanja" FROM placanja GROUP BY datum; ``` ::: ### 7. Prikaži proizvode naručene u januaru. ::: info ℹ️ Uputstvo Potrebno je povezati proizvode i narudzbe preko međutabele proizvodi_narudzbe. ::: ::: spoiler Rješenje ``` SQL SELECT DISTINCT p.naziv FROM proizvodi p JOIN proizvodi_narudzbe pn ON pn.proizvod_id = p.id JOIN narudzbe n ON n.id = pn.narudzba_id WHERE MONTH(n.datum_narucivanja) = 1; ``` ::: ### 8. Koji proizvod se nalazi na najvise porudžbi? ::: info ℹ️ Uputstvo Potrebno je prvo izračunati koliko narudžbi je imao koji proizvod, a onda uzeti samo jedan koji ih ima najviše. ::: ::: spoiler Rješenje Krećemo od spajanja proizvoda sa narudžbama, jer su to dvije tabele koje će nam trebati u zadatku (proizvodi_narudzbe je treća tabela koju moramo koristiti da bismo povezali ove dvije, iako nam ona ne treba za prikaz). ``` SQL SELECT * FROM proizvodi p JOIN proizvodi_narudzbe pn ON pn.proizvod_id = p.id JOIN narudzbe n ON n.id = pn.narudzba_id; ``` Nakon toga moramo izračunati koliko narudžbi je bilo za koji proizvod, koristeći ***GROUP BY***. ``` SQL SELECT p.*, count(n.id) as broj_narudzbi FROM proizvodi p JOIN proizvodi_narudzbe pn ON pn.proizvod_id = p.id JOIN narudzbe n ON n.id = pn.narudzba_id GROUP BY p.id DESC; ``` Zatim sortiramo rezultate u opadajućem poretku po tom broju narudžbi, i uzimamo samo jedan, najbolji proizvod. ``` SQL SELECT p.*, count(n.id) as broj_narudzbi FROM proizvodi p JOIN proizvodi_narudzbe pn ON pn.proizvod_id = p.id JOIN narudzbe n ON n.id = pn.narudzba_id GROUP BY p.id DESC ORDER BY broj_narudzbi LIMIT 1; ``` ::: warning ⚠️ Napomena Kada se traži jedno, najbolje ili najgore rješenje, prva pomisao će vam možda biti da koristite ***MIN()*** i ***MAX()*** funkcije. Te funkcije će svakako vratiti najmanju, tj. najveću vrijednost određene kolone za date rezultate, **ali ukoliko je potrebno osim te same vrijednosti prikazati bilo koji drugi podatak, tu će doći do greške.** Greška je u tome što ove funkcije izvuku najmanju, tj. najveću vrijednost jedne kolone iz svih rezultata, ali **ostale kolone koje se prikazuju, uzimaće vrijednost prvog reda iz rezultata**, a ne odgovarajućeg reda kako bi trebalo. ::: ### 9. Koji proizvodi imaju manje od 25 porudžbi? Prikazati kod i ime proizvoda. ::: info ℹ️ Uputstvo Potrebno je prvo izračunati koliko narudžbi je imao koji proizvod, a onda prikazati samo one koji ih imaju manje od 25. ::: ::: spoiler Rješenje Krećemo od spajanja proizvoda sa narudžbama, a onda računamo koliko narudžbi je bilo za koji proizvod, koristeći ***GROUP BY***, kao u prethodnom primjeru. ``` SQL SELECT p.*, count(n.id) as broj_narudzbi FROM proizvodi p JOIN proizvodi_narudzbe pn ON pn.proizvod_id = p.id JOIN narudzbe n ON n.id = pn.narudzba_id GROUP BY p.id DESC; ``` Dobijene rezultate filtriramo koristeći ***HAVING***. ``` SQL SELECT p.*, count(n.id) as broj_narudzbi FROM proizvodi p JOIN proizvodi_narudzbe pn ON pn.proizvod_id = p.id JOIN narudzbe n ON n.id = pn.narudzba_id GROUP BY p.id DESC HAVING broj_narudzbi < 25; ``` ::: warning ⚠️ Napomena Kako je u zadatku rečeno da se traže proizvodi sa manje od 25 narudžbi, to predstavlja uslov, za koji bi možda pomislili da treba da ide u ***WHERE*** klauzuli. Razlog zbog kojeg se to ne može tako uraditi jeste što mi broj narudžbi za proizvode dobijamo tek nakon što odradimo ***GROUP BY***, a možete primijetiti da se ***WHERE*** klauzula uvijek piše prije njega, što znači da mi u ***WHERE*** klauzuli još uvijek nemamo taj podatak. Da bismo filtrirali podatke koje dobijamo nakon ***GROUP BY*** (to su uvijek rezultati agregatnih funkcija), koristimo ***HAVING*** koje se piše nakon njega. ::: ### 10. Prikaži ukupan iznos svih porudžbi grupisanih po kupcu. Potrebno je prikazati ime kupca i iznos. Iznos se računa kao količina * cijena. ::: info ℹ️ Uputstvo Potrebno je prvo spojiti narudžbe sa odgovarajućim kupcima, a onda sabrati sve iznose narudžbi po kupcu. Iznos narudžbe se računa kao zbir iznosa (količina * cijena) za svaki proizvod koji se nalazi na toj narudžbi. ::: ::: spoiler Rješenje ``` SQL SELECT * FROM narudzbe n JOIN kupci k ON k.id = n.kupac_id; ``` Iznos se računa iz tabele proizvodi_narudzbe, pa moramo pridružiti i nju. ``` SQL SELECT k.*, n.*, pn.kolicina * pn.cijena as iznos FROM narudzbe n JOIN kupci k ON k.id = n.kupac_id JOIN proizvodi_narudzbe pn ON pn.narudzba_id = n.id; ``` Sada grupišemo rezultate po kupcu i sabiramo iznose narudžbi koristeći ***SUM()***. ``` SQL SELECT k.ime, k.prezime, SUM(pn.kolicina * pn.cijena) as ukupan_iznos FROM narudzbe n JOIN kupci k ON k.id = n.kupac_id JOIN proizvodi_narudzbe pn ON pn.narudzba_id = n.id GROUP BY k.id; ``` ::: ### 11. Prikazati broj i iznose porudžbi koje su otkazane kao ‘Iznos’. Iznos se računa kao količina * cijena. ::: info ℹ️ Uputstvo Potrebno je povezati tabele narudzbe i proizvodi_narudzbe, a onda grupisati rezultate po narudžbama i sabrati iznose. ::: ::: spoiler Rješenje ``` SQL SELECT n.*, pn.cijena * pn.kolicina as iznos FROM narudzbe n JOIN proizvodi_narudzbe pn ON pn.narudzba_id = n.id GROUP BY n.id; ``` ::: ### 12. Prikazati sumu iznosa svih porudžbi grupisanih po statusu. Iznos se računa kao količina * cijena. ::: info ℹ️ Uputstvo Kao u prethodnom primjeru, samo ne grupišemo po pojedinačnoj narudžbi, nego po statusu narudžbe. ::: ::: spoiler Rješenje ``` SQL SELECT n.status, pn.cijena * pn.kolicina as iznos FROM narudzbe n JOIN proizvodi_narudzbe pn ON pn.narudzba_id = n.id GROUP BY n.status; ``` :::