Садржај
2.8 SQL: Сложенији угњеждени упити - Додатно
3.0 SQL: Промена садржаја базе
3.2 SQL: Ажурирање података у табелама
3.3 SQL: Брисање података из табела
6.0 Библиотека Flask - пројектни задатак

Врсте спајања

У базама података постоји неколико различитих оператора спајања: INNER JOIN, CROSS JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, који се углавном разликују по томе шта се дешава у ситуацији када у једној од спојених табела не постоји ред који би требало спојити. Оператор JOIN који смо приказали је заправо оператор INNER JOIN (тзв. унутрашње спајање). То је најчешће коришћена врста спајања, па се и назнака о којој врсти спајања је реч може изоставити.

Природно је очекивати да се у табели изостанака налазе само они идентификатори ученика за које у табели ученика постоје одговарајући подаци. То је осигурано постављањем страног кључа између колоне id_ucenik у табели изостанака и колоне id у табели ученика.

Међутим, сасвим је могуће да се вредност колоне id из неких редова табеле ученика уопште не појављује у колони id_ucenik у табели изостанака (јер могу да постоје ученици који немају ниједан изостанак). Размотримо тада следећи задатак.

Приказати списак свих ученика са њиховим изостанцима.

Ако бисмо извршили обично (унутрашње) спајање табеле ученика и табеле изостанака, добили бисмо податке само о оним ученицима који имају изостанке.

SELECT *
FROM ucenik u
     JOIN izostanak i ON u.id = i.id_ucenik;

На следећој слици може да се види овај упит у систему SQLite Studio. Види се само првих неколико редова и информација о томе да има укупно 10194 редова, односно толико има евидентираних изостанака у овој бази података.

../_images/vrste_spajanja.png

Ако желимо да се прикажу подаци и о ученицима који немају изостанке, потребно је да применимо тзв. лево спољашње спајање које се постиже оператором LEFT OUTER JOIN или скраћено LEFT JOIN.

SELECT *
FROM ucenik u
     LEFT JOIN izostanak i ON u.id = i.id_ucenik;

Табела са ученицима се налази лево од речи JOIN у упиту. Уколико се табеле наведу у другачијем редоследу, онда је потребно употребити десно спајање.

SELECT *
FROM izostanak i
  RIGHT JOIN ucenik u ON u.id = i.id_ucenik;

На следећој слици може да се види овај упит у систему SQLite Studio. Види се само првих неколико редова и информација о томе да има укупно 10200 редова.

../_images/vrste_spajanja2.png

До разлике у броју редова које враћају претходна два упита, дошло је због тога што има ученика који немају изостанке и које први упит није приказао.

Код ученика који имају изостанака, за сваки изостанак постоји одговарајућа врста у резултату. Код ученика који немају изостанака у пољима који описују изостанак стоје специјалне недостајуће вредности (NULL). Ово је илустровано на следећој слици на којој видимо управо тих 6 редова за које се разликују резултати првог и другог упита.

../_images/vrste_spajanja3.png

Иако на први поглед делује да ово нема пуно смисла, кључна ствар је то што смо у табели задржали податке о свим ученицима, што нам омогућава да у извештајима о укупном броју изостанака пријавимо и податке о ученицима који нису правили изостанке. Илуструјмо то наредним паром примера.

Приказати укупан број изостанака за сваког ученика (укључујући и оне ученике који немају изостанака). Списак уредити опадајуће по укупном броју изостанака.

Кључни елемент решења је то да агрегатне функције приликом агрегирања елемената задате колоне потпуно игноришу недостајуће вредности у тој колони. Тако агрегатна функција COUNT броји само оне врсте које не садрже вредности NULL. Зато се применом груписања по идентификатору ученика и применом функције COUNT на табелу добијену левим спољашњим спајањем табеле ученика и изостанака лако може генерисати извештај који садржи податке о свим ученицима, при чему ће се код оних који немају изостанака појавити број 0.

SELECT u.id, u.ime, u.prezime, COUNT(status) AS broj_izostanaka
FROM ucenik u
     LEFT JOIN izostanak i ON u.id = i.id_ucenik
GROUP BY u.id
ORDER BY broj_izostanaka DESC;

Извршавањем упита добија се следећи резултат:

id

ime

prezime

broj_izostanaka

185

Угљеша

Милановић

71

61

Елена

Радивојевић

69

40

Емилија

Рељин

68

220

Михајло

Гајић

67

24

Ивана

Пејчев

63

Приказати број неоправданих изостанака за сваког ученика одељења I2 (укључујући и оне ученике који немају неоправданих изостанака). Списак уредити опадајуће по броју неоправданих изостанака.

Овај пример само проширује претходни тиме што се захтева додатно филтрирање.

SELECT u.id, u.ime, u.prezime, COUNT(status) AS broj_neopravdanih
FROM ucenik u LEFT JOIN
     izostanak i ON u.id = i.id_ucenik AND
     u.razred = 1 AND u.odeljenje = 2 AND status = 'неоправдан'
GROUP BY u.id
ORDER BY broj_neopravdanih DESC

Извршавањем упита добија се следећи резултат:

id

ime

prezime

broj_neopravdanih

52

Сташа

Ивановић

5

57

Маша

Гузина

5

45

Николај

Кнежевић

4

47

Душанка

Петровић

4

53

Анђелка

Вељковић

3

344

Тијана

Илић

0

345

Виолета

Урошевић

0

346

Адам

Пауновић

0

Уколико покренемо упит без левог спајања, тј. ако изоставимо реч LEFT, добићемо 20 редова, што може да се види на следећој слици.

../_images/vrste_spajanja4.png

Покренут упит са левим спајањем приказује 346 редова, што одговара укупном броју ученика чије податке чувамо у овој бази, а на дну списка се налазе они који немају неоправдане изостанке.

../_images/vrste_spajanja5.png

Приметимо да смо услов селекције (филтрирања) навели у склопу услова спајања, а не у склопу клаузуле WHERE, јер бисмо издвајањем само неоправданих изостанака након спајања изгубили податке о ученицима који немају изостанака (пошто у колони статус не пише neopravdan, већ NULL). Алтернативно, услов филтрирања у клаузули WHERE би се могао записати као status = 'неоправдан' OR status IS NULL, чиме би се задржале врсте које одговарају неоправданим изостанцима ученика који их имају, као и врсте које одговарају ученицима који немају изостанке.

Као што смо рекли, десно спољашње спајање је симетрично левом (задржавају се све колоне друге уместо прве табеле). Детаљније изучавање осталих врста спајања превазилази градиво овог предмета и тиме се нећемо бавити у наставку. У већини примера ћемо користити унутрашње спајање (JOIN), а с времена на време ћемо употребити и лево спољашње спајање (LEFT JOIN). Резимирајмо на једном малом примеру како та два спајања функционишу. Претпоставимо да спајамо следеће две табеле (autor и knjiga).

id

ime

prezime

1

Петар

Јовановић

2

Лазар

Бошковић

3

Милена

Шумановић

id_knjiga

knjiga

id_autor

1

Увод у HTML

1

2

Стилизовање веб-страна

1

3

Програмирање у Python-у

3

Као што видимо, аутор 1, Петар Јовановић је написао две књиге, аутор 2 није написао ни једну, док је ауторка 3 написала једну књигу.

Унутрашњим спајањем (по услову autor.id = knjiga.id_autor) добили бисмо следећу табелу.

id

ime

prezime

id_autor

књига

1

Петар

Јовановић

1

Увод у HTML

1

Петар

Јовановић

1

Стилизовање веб-страна

3

Милена

Шумановић

3

Програмирање у Python-у

Левим спољашњим спајањем (по услову autor.id = knjiga.id_autor) добили бисмо следећу табелу.

id

ime

prezime

id_autor

књига

1

Петар

Јовановић

1

Увод у HTML

1

Петар

Јовановић

1

Стилизовање веб-страна

2

Лазар

Бошковић

NULL

NULL

3

Милена

Шумановић

3

Програмирање у Python-у

Вежба

Покушај да самостално напишеш наредни упит.

За сваког ученика приказати просек свих уписаних оцена. Табела треба да прикаже имена, презимена и просечне оцене свих ученика заокружене на две децимале (код оних који немају ниједну уписану оцену уместо просечне оцене треба да буде уписана недостајућа вредност).



(Created using Swinx, RunestoneComponents and PetljaDoc)
© 2022 Petlja
A- A+