Једноставни упити: упити над једном табелом Наћи имена и презимена студената који су са смера R. 3 SELECT ime, prezime 1 FROM Studenti 2 WHERE smer = 'R'; 2 рестрикција = избор редова у табели 3 пројекција = избор колона у табели SELECT ime, prezime FROM Studenti WHERE smer = 'R'; Queries = упити Query Design Close SQL View File> Options > Object Designers > Query Design Font цела табела: -- FROM део SELECT * FROM Studenti; -- рестрикција (1. начин без џокера за колоне) SELECT ime, prezime, indeks, smer, status FROM Studenti WHERE smer = 'R'; -- рестрикција (2. начин са џокером за колоне) SELECT * FROM Studenti WHERE smer = 'R'; -- пројекција SELECT ime, prezime FROM Studenti WHERE smer = 'R'; Типови података и изрази Тип података је скуп вредности и операција које се могу изводити над тим вредностима. Целобројни тип вредности: некакав коначни подскуп целих бројева операције: +, -, *, \, mod, ^ Реални тип вредности: некакав коначни подскуп рационалних бројева (разломака) са коначним бројем децимала операције: +, -, *, /, ^ Логички тип вредности: True False операције: AND OR NOT Текстуални тип вредности: ниске (константне ниске се пишу под апострофима и њихов садржај, за разлику од остатка SQL-а, осетљив је на велика и мала слова). операције: & (дописивање) SELECT 5 + 4 * 3 AS test; AS у линији SELECT служи за (пре)именовање колона. Expr1000 17 test 17 SELECT ime AS Име, prezime AS Презиме, indeks AS 'број индекса' FROM Studenti; SELECT ime AS Име, prezime AS Презиме, indeks AS [број индекса] FROM Studenti; select ime as Име, prezime as Презиме from studenti; целобројни количник (у Пајтону 7 // 3 = 2) SELECT 7 \ 3 AS [целобројни количник]; 2 целобројни остатак (у Пајтону 7 % 3 = 1) SELECT 14 mod 8 AS [целобројни остатак]; 1 степеновање SELECT 2 ^ 3 AS [степен]; 8 SELECT 7 / 3 AS [реални количник]; 2,333333 SELECT 16 ^ 0.5 AS [реални степен]; корен од 16 (4) Исписати имена и презимена студената са смера N чији је статус R. SELECT ime, prezime FROM Studenti WHERE smer = 'N' AND status = 'R'; Исписати имена и презимена студената са смерова H, R и S. SELECT ime, prezime FROM Studenti WHERE smer = 'H' OR smer = 'R' OR smer = 'S' ; SELECT ime, prezime FROM Studenti WHERE smer IN ('H', 'R', 'S') ; Исписати све различите смерове на којима има студената. DISTINCT бира различите редове, никако колоне! SELECT DISTINCT smer FROM Studenti; Исписати различита имена и презимена студената који се презивају But (ако има два пута Teri But, исписује се једном). 1. SELECT ime, prezime FROM Studenti WHERE prezime = 'But'; 2. ПОГРЕШНО! SELECT DISTINCT ime, DISTINCT prezime FROM Studenti WHERE prezime = 'But'; 3. ТАЧНО! SELECT DISTINCT ime, prezime FROM Studenti WHERE prezime = 'But'; Исписати табелу са две колоне, прва колона је име и презиме студента, а друга његов индекс. Прву колону назвати ime. SELECT ime & prezime AS Име, indeks FROM Studenti; Скаларне функције SELECT ROUND(7.333333333, 2); ROUND(број који заокружујемо, број децимала на који заокружујемо) LEN(niska) = дужина ниске LEFT(niska, dužina prefiksa) = издваја префикс задате дужине RIGHT(niska, dužina sufiksa) = издваја суфикс задате дужине MID(niska, početna_pozicija, dužina) = издваја подниску задате дужине која почиње на задатој позицији. 1234567 MID('Emilija', 2, 4) = 'mili' броји се од 1, не од нуле! TRIM(niska) = брише белине са почетка и краја ниске ' pera perić ' TRIM(' pera perić ') = 'pera perić' Исписати у једној колони имена и презимена студената у формату П. Петровић (ако се студент зове Пера Петровић). Колону назвати "скраћено име". SELECT LEFT(ime, 1) & '. ' & prezime AS [скраћено име] FROM Studenti; Домаћи: исписати две колоне, у првој колони су иницијали студента у формату П. П. (ако се студент зове Пера Петровић) и колону назвати "скраћено име". У другој колони исписати његову годину уписа (искористити број индекса). Претпоставити да су све године уписа биле у периоду 1900--1999 и исписати годину уписа као четвороцифрен број. SELECT LEFT(TRIM(ime), 1) & '. ' & LEFT(TRIM(prezime), 1) & '.' AS [скраћено име], '19' & RIGHT(TRIM(indeks), 2) AS [година уписа] FROM Studenti ; Изрази аритметички 2*(ocena + LEN(indeks)) SELECT 2*(ocena + LEN(indeks)) FROM Ispiti; текстуални '19' & RIGHT(TRIM(indeks), 2) smer = 'R' smer <> 'R' ocena <> 5 ocena < 9 ocena > 8 ocena <=8 ocena >= 6 prezime < 'R' лексикографски поредак на карактерском скупу Unicode Ćuprija Ćustendil Đakovica Đerdap Šabac Žabari Логички изрази True False рел. изрази су лог. изрази SELECT израз1 AS надимак1, израз2 AS надимак2,... FROM табела, табела,... WHERE логички_израз Оператор LIKE и џокер знаци Имена и презимена студената који су уписали факултет 1998. године. SELECT ime, prezime FROM Studenti WHERE RIGHT(TRIM(indeks), 2) = '98'; ИСПРАВНО! Индекс је налик обрасцу "нешто коса црта 98 нешто", при чему нешто (*) може да буде и празна ниска SELECT ime, prezime FROM Studenti WHERE indeks LIKE '*/98*'; ПОГРЕШНО! Буквална једнакост SELECT ime, prezime FROM Studenti WHERE indeks = '*/98*'; * је џокер знак који мења 0 или више произвољних карактера, али користи се углавном у Microsoft-овим имплементацијама SQL-а јер исти џокер знак користе DOS и Windows. По стандарду SQL-а одговарајући џокер знак би био % У MySQL-у, на пример, горњи упит би изгледао SELECT ime, prezime FROM Studenti WHERE indeks LIKE '%/98%'; Microsoft користи и џокер знак ? који мења један, било који карактер. По стандарду се користи као одговарајући џокер знак _ SELECT ime, prezime FROM Studenti WHERE indeks LIKE '??/*'; У MySQL-у, на пример, горњи упит би изгледао SELECT ime, prezime FROM Studenti WHERE indeks LIKE '__/*'; Исписати различите индексе студената који на испиту имају оцену између 6 и 8. SELECT DISTINCT indeks FROM Ispiti WHERE ocena > 5 AND ocena < 9; SELECT DISTINCT indeks FROM Ispiti WHERE ocena > 5 AND ocena <= 8; SELECT DISTINCT indeks FROM Ispiti WHERE ocena >= 6 AND ocena < 9; ИСПРАВНО SELECT DISTINCT indeks FROM Ispiti WHERE ocena >= 6 AND ocena <= 8; ЧЕСТА ГРЕШКА!! WHERE ocena >= 6 AND <= 8; SELECT DISTINCT indeks FROM Ispiti WHERE ocena BETWEEN 6 AND 8; Исписати имена и презимена редовних студената (статус R) који студирају на неком од смерова N и G. SELECT ime, prezime FROM Studenti WHERE status = 'R' AND smer IN ('N', 'G'); SELECT ime, prezime FROM Studenti WHERE status = 'R' AND (smer = 'N' OR smer = 'G'); Имена и презимена студената који нису уписали факултет 1998. године. SELECT ime, prezime FROM Studenti WHERE NOT (indeks LIKE '*/98*'); SELECT ime, prezime FROM Studenti WHERE indeks NOT LIKE '*/98*'; = <> <> = < >= > <= <= > >= < Исписати имена и презимена редовних студената (статус R) који не студирају ни на једном од смерова N и G. SELECT ime, prezime FROM Studenti WHERE status = 'R' AND smer NOT IN ('N', 'G'); SELECT ime, prezime FROM Studenti WHERE status = 'R' AND NOT (smer = 'N' OR smer = 'G'); SELECT ime, prezime FROM Studenti WHERE status = 'R' AND smer <> 'N' AND smer <> 'G'; ==================================================== Упити над више табела Исписати имена и презимена студената који имају бар једну осмицу. 1. техника: спојити табеле у једну велику која садржи све неопходне податке (колоне) * само кад немамо другог избора * спајамо што мање табела да би се упит брже извршио * спајање лакше за писање упита него 2. техника 2. техника (на конкретном примеру): у табели Испити анализирамо ред по ред (испит по испит) и кад год је у реду (на испиту) оцена = 8, забележимо број индекса студента који је полагао тај испит (број индекса у том реду). На тај начин добијамо листу бројева индекса оних студената који имају 8 на неком испиту. У табели Студенти такође анализирамо ред по ред и кад год се број индекса студента нађе у листи (списку) који смо претходно направили анализом табеле Испити, испишемо име и презиме тог студента. Рон Весли Перси Весли Хана Абот Невил Лонгботом SELECT ime, prezime 1 FROM Studenti, Ispiti WHERE ocena = 8; Декартов производ, сваки ред се спаја са сваким SELECT * FROM Studenti, Ispiti; ----------------------------- Природно спајање SELECT * FROM Studenti, Ispiti WHERE Studenti.Indeks = Ispiti.Indeks; Додатна рестрикција SELECT * FROM Studenti, Ispiti WHERE Studenti.Indeks = Ispiti.Indeks AND ocena = 8; Пројекција SELECT ime, prezime FROM Studenti, Ispiti WHERE Studenti.Indeks = Ispiti.Indeks AND ocena = 8; Решење са алиасима (надимцима) табела SELECT DISTINCT ime, prezime FROM Studenti AS s, Ispiti AS i WHERE s.Indeks = i.Indeks AND ocena = 8; SELECT DISTINCT s.ime, s.prezime FROM Studenti AS s, Ispiti AS i WHERE s.Indeks = i.Indeks AND i.ocena = 8; а) Исписати различита имена и презимена студената који су бар један испит полагали код наставника са катедре Грифиндор. б) Исписати различита имена и презимена студената који су бар један испит положили код наставника са катедре Грифиндор. а) Перси Весли Џини Весли Рон Весли Хари Потер Решење за а) SELECT DISTINCT s.ime, s.prezime FROM Studenti AS s, Ispiti AS i, Nastavnici AS n WHERE s.indeks = i.indeks AND i.sifra_nastavnika = n.sifra_nastavnika AND n.katedra = 'Grifindor'; Решење за б) SELECT DISTINCT s.ime, s.prezime FROM Studenti AS s, Ispiti AS i, Nastavnici AS n WHERE s.indeks = i.indeks AND i.sifra_nastavnika = n.sifra_nastavnika AND n.katedra = 'Grifindor' AND i.ocena > 5; ======================================================== Исписати имена и презимена студената који имају бар једну осмицу. 2. техника (на конкретном примеру): у табели Испити анализирамо ред по ред (испит по испит) и кад год је у реду (на испиту) оцена = 8, забележимо број индекса студента који је полагао тај испит (број индекса у том реду). На тај начин добијамо листу бројева индекса оних студената који имају 8 на неком испиту. У табели Студенти такође анализирамо ред по ред и кад год се број индекса студента нађе у листи (списку) који смо претходно направили анализом табеле Испити, испишемо име и презиме тог студента. Рон Весли Перси Весли Хана Абот Невил Лонгботом 2. техника користи подупите (упити у оквиру упита) SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena = 8); Табела са једном колоном indeks 100/97 103/98 22/96 301/98 може да се замишља и користи као листа вредности ('100/97', '103/98', '22/96', '301/98') "Испод хаубе" се дешава ово, али тако никада нећемо писати. Пустићемо SQL да сам израчуна помоћну листу вредности која нам је потребна да бисмо одрадили главни упит. SELECT ime, prezime FROM Studenti WHERE indeks IN ('100/97', '103/98', '22/96', '301/98'); ========================= а) Исписати различита имена и презимена студената који су бар један испит полагали код наставника са катедре Грифиндор. б) Исписати различита имена и презимена студената који су бар један испит положили код наставника са катедре Грифиндор. Шифра наставника са катедре Грифиндор Решење а) SELECT DISTINCT ime, prezime FROM Studenti WHERE indeks IN (SELECT indeks FROM ispiti WHERE sifra_nastavnika IN (SELECT sifra_nastavnika FROM nastavnici WHERE katedra = 'Grifindor')); SELECT DISTINCT ime, prezime FROM Studenti WHERE indeks IN (SELECT indeks FROM ispiti WHERE sifra_nastavnika IN (SELECT sifra_nastavnika FROM nastavnici WHERE katedra = 'Grifindor') AND ocena > 5 ); Имена и презимена студената који имају бар једну осмицу и нису ништа полагали код наставника са катедре Грифиндор Дакле, у питању је разлика два скупа, зовимо их A и B. A = студенти са бар једном осмицом B = студенти који су полагали код неког наставника са катедре Грифиндор A \ B дефиниција разлике x IN A \ B <=> (x IN A) AND (x NOT IN B) SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT indeks FROM ispiti WHERE ocena = 8) AND indeks NOT IN (SELECT indeks FROM ispiti WHERE sifra_nastavnika IN (SELECT sifra_nastavnika FROM nastavnici WHERE katedra = 'Grifindor')); SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT indeks FROM ispiti WHERE ocena = 8) AND indeks NOT IN (SELECT indeks FROM ispiti AS i, nastavnici AS n WHERE i.sifra_nastavnika = n.sifra_nastavnika AND katedra = 'Grifindor'); SELECT ime, prezime FROM Studenti AS s, ispiti AS i WHERE s.indeks = i.indeks AND ocena = 8 AND s.indeks NOT IN (SELECT indeks FROM ispiti AS i, nastavnici AS n WHERE i.sifra_nastavnika = n.sifra_nastavnika AND katedra = 'Grifindor'); ============================================================= Присутни: Вуковић Јована Коконешевић Невена Тодоровић Андријана Тодоровић Душанка Димитријевић Марина Мартић Емилија Орлић Теодора Радовић Сања Товарницки Јелена а) Исписати имена и презимена студената који су полагали предмет Историја магије. б) Исписати имена и презимена студената који су полагали бар један предмет (тј. нису бруцоши) в) Исписати имена и презимена бруцоша (тј. студената који нису полагали ниједан испит до сада) г) Исписати имена и презимена студената који су положили предмет Историја магије. д) Исписати имена и презимена студената који су полагали бар један предмет (тј. нису бруцоши), али нису полагали Историју магије. ђ) Исписати имена и презимена студената који су полагали предмет Историја магије, али нису полагали предмет Хербологија. Решење а) спајање SELECT s.ime, s.prezime FROM Studenti AS s, Ispiti AS i, Predmeti AS p WHERE s.indeks = i.indeks AND i.sifra_predmeta = p.sifra_predmeta AND p.naziv = 'Istorija magije'; подупити SELECT DISTINCT ime, prezime FROM Studenti WHERE indeks IN (SELECT indeks FROM Ispiti WHERE sifra_predmeta IN (SELECT sifra_predmeta FROM predmeti WHERE naziv = 'Istorija magije')); Решење б) SELECT s.ime, s.prezime FROM Studenti AS s, Ispiti AS i WHERE s.indeks = i.indeks; SELECT DISTINCT ime, prezime FROM Studenti WHERE indeks IN (SELECT indeks FROM Ispiti); Решење в) не може без подупита SELECT DISTINCT ime, prezime FROM Studenti WHERE indeks NOT IN (SELECT indeks FROM Ispiti); Решење г) спајање SELECT s.ime, s.prezime FROM Studenti AS s, Ispiti AS i, Predmeti AS p WHERE s.indeks = i.indeks AND i.sifra_predmeta = p.sifra_predmeta AND p.naziv = 'Istorija magije' AND i.ocena > 5; подупити SELECT DISTINCT ime, prezime FROM Studenti WHERE indeks IN (SELECT indeks FROM Ispiti WHERE sifra_predmeta IN (SELECT sifra_predmeta FROM predmeti WHERE naziv = 'Istorija magije') AND ocena > 5 ); SELECT DISTINCT ime, prezime FROM Studenti WHERE indeks IN (SELECT indeks FROM Ispiti WHERE ocena > 5 AND sifra_predmeta IN (SELECT sifra_predmeta FROM predmeti WHERE naziv = 'Istorija magije')); Решење д) SELECT s.ime, s.prezime FROM Studenti AS s, Ispiti AS i WHERE s.indeks = i.indeks AND i.indeks NOT IN ( SELECT indeks FROM Ispiti AS i2, Predmeti AS p WHERE i2.sifra_predmeta = p.sifra_predmeta AND p.naziv = 'Istorija magije'); SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT indeks FROM ispiti) AND indeks NOT IN (SELECT indeks FROM ispiti WHERE sifra_predmeta IN (SELECT sifra_predmeta FROM Predmeti WHERE naziv = 'Istorija magije')); ============================================= "Загревање": Имена и презимена студената који су полагали бар један испит SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti); Имена и презимена студената који су положили бар један испит SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena > 5); "Задатак са осмицама" (случајеви) O1: Имена и презимена студената који имају бар једну осмицу. SELECT DISTINCT ime, prezime FROM Studenti AS s, Ispiti AS i2 WHERE s.indeks = i.indeks AND i.ocena = 8; SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena = 8); O2: Имена и презимена студената који имају бар једну оцену која није осмица. SELECT DISTINCT ime, prezime FROM Studenti AS s, Ispiti AS i2 WHERE s.indeks = i.indeks AND i.ocena <> 8; SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena <> 8); O3: Имена и презимена студената који су полагали бар један испит и немају ниједну осмицу. SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti) AND indeks NOT IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena = 8) ; O4: Имена и презимена студената који су полагали бар један испит и на сваком испиту су добили осмицу. SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti) AND indeks NOT IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena <> 8) ; NOT (ocena = 8) O1: "бар један јесте" O2: "бар један није" О3: Негација (ниједан није) = Негација ("сваки није") = "бар један јесте" (О1) О4: Негација (сваки јесте) = "бар један није" (O2) Домаћи: 1. Називи предмета из кога је сваки студент који је положио тај предмет добио оцену већу од 8. O4: негација(сваки студент који је полагао тај предмет добио је оцену већу од 8) = бар један студент који је полагао тај предмет није добио оцену већу од 8 (NOT ocena > 8) = бар један студент који је полагао тај предмет је добио оцену 8 или мању (ocena <= 8) SELECT naziv FROM predmeti WHERE sifra_predmeta IN (SELECT sifra_predmeta FROM Ispiti) AND sifra_predmeta NOT IN (SELECT sifra_predmeta FROM Ispiti WHERE ocena > 5 AND ocena <= 8); WHERE ocena BETWEEN 6 AND 8); 2. Имена и презимена наставника чији испит није успео да положи ниједан студент који је тај испит полагао. Имена и презимена наставника на чијем сваком испиту је оцена 5, а који су одржали бар један испит. SELECT ime, prezime FROM nastavnici WHERE sifra_nastavnika IN (SELECT sifra_nastavnika FROM nastavnici) AND sifra_nastavnika NOT IN (SELECT sifra_nastavnika FROM nastavnici WHERE ocena > 5); ==================================================== Исказ је реченица која има тачно једну истинитосну вредност, тј. или је тачна или нетачна. Примери: 1) 2 = 2 2) 2 <> 3 3) Данас је понедељак. 4) Сутра је петак. 5) ocena > 5. 1) је исказ (тачан), 2) је исказ (нетачан), док 3)--5) нису искази јер њихова вредност зависи од вредности променљивих Данас, Сутра, ocena. Реченице 3)--5) зваћемо предикатима. Истинитосна вредност предиката није константна нити одређена све док променљиве од којих зависи не добију конкретну вредност. Број променљивих од којих зависи предикат зове се дужина или арност предиката. Предикати који зависе од једне, две, три, n променљивих називају се тим редом унарни, бинарни, тернарни, n-арни предикати. Предикати се могу посматрати као уопштења: * исказа (кад променљиве од којих зависе добију конкретну вредност, претварају се у исказ); * релација (њима се успоставља веза између елемената истог или различитих скупова) Предикат се може посматрати и као логичка функција (израз) јер када његове променљиве добију вредност, цео предикат добија одређену логичку вредност (тачно или нетачно). Предикатске формуле првог реда (у даљем тексту PRF^1) дефинишу се на следећи начин: (i) Предикати су PRF^1 (тзв. атомичне PRF^1). (ii) Ако су F_1, F_2 PRF^1, онда су то и њихова конјункција, дисјункција, импликација, еквиваленција, негација и формуле у заградама, тј. F_1 \land F_2, F_1 \lor F_2, F_1 \Rightarrow F_2, F_1 \Leftrightarrow F_2, \lnot F_1, \lnot F_2, (F_1), (F_2) (iii) Ако је x променљива и F PRF^1, онда су PRF^1 (\forall x) F (за свако x важи F) (\exists x) F (постоји x тако да важи F). **************************************** Пример 1: A = {2, 8, 12, 18, 24} Запишимо реченицу "Сваки елемент скупа A је паран број." математичком формулом. Поступно: прво природним језиком (2 је паран број) и (8 је паран број) и (12 је паран број) и (18 је паран број) и (24 је паран број). Уводимо унарни предикат P(x) са значењем "x је паран број". Пошто за све елементе скупа истовремено треба да важи иста особина (да су парни бројеви), користимо конјункцију: P(2) \land P(8) \land P(12) \land P(18) \land P(24). Овакав запис је гломазан ако скуп A има много чланова, а неупотребљив је ако је скуп A бесконачан. Према томе, уводимо универзални квантификатор којим се истовремено обухватају сви елементи скупа A. При томе разликујемо два случаја: 1. случај Користимо променљиву x која може да узме произвољну вредност. (\forall x)(x \in A \Rightarrow P(x)) и читамо: "за свако x, ако је x елемент скупа A, онда је P(x)" 2. случај Користимо променљиву x која сме да узме вредност искључиво из скупа A = {2, 8, 12, 18, 24}. Тада је формула једноставнија: (\forall x) P(x) и читамо: "за свако x важи P(x)", при чему подразумевамо да је x вредност из скупа А. Пошто је ово скраћени запис за једну дугачку конјункцију чији је сваки део тачан, онда је и цела конјункција, односно PRF^1 такође тачна. **************************************** Пример 2: Сваки елемент скупа A је мањи од 20. Природним језиком (2 је мање од 20) и (8 је мање од 20) и (12 је мање од 20) и (18 је мање од 20) и (24 је мање од 20). Уводимо бинарни предикат M(x, y) са значењем "x је мањи од y". М(2, 20) \land М(8, 20) \land М(12, 20) \land М(18, 20) \land М(24, 20) Поново разликујемо два случаја: 1. случај Користимо променљиву x која може да узме произвољну вредност. (\forall x)(x \in A \Rightarrow М(x, 20)) и читамо: "за свако x, ако је x елемент скупа A, онда је М(x, 20)" 2. случај Користимо променљиву x која сме да узме вредност искључиво из скупа A = {2, 8, 12, 18, 24}. Тада је формула једноставнија: (\forall x) М(x, 20) и читамо: "за свако x важи М(x, 20)", при чему подразумевамо да је x вредност из скупа А. Пошто је ово скраћени запис за једну дугачку конјункцију чији је последњи део, M(24, 20), није тачан, онда и цела конјункција, односно PRF^1 такође није тачна. **************************************** Пример 3: Неки елемент скупа A није дељив са 3. Природним језиком (2 није дељиво са 3) или (8 није дељиво са 3) или (12 није дељиво са 3) или (18 није дељиво са 3) или (24 није дељиво са 3) Уводимо бинарни предикат D(x, y) са значењем "x је дељив са y". \lnot D(2, 3) \lor \lnot D(8, 3) \lor \lnot D(12, 3) \lor \lnot D(18, 3) \lor \lnot D(24, 3) Поново разликујемо два случаја: 1. случај Користимо променљиву x која може да узме произвољну вредност. (\exists x)(x \in A \land \lnot D(x, 3)) и читамо: "постоји x, x је елемент скупа A и није D(x, 3)" 2. случај Користимо променљиву x која сме да узме вредност искључиво из скупа A = {2, 8, 12, 18, 24}. Тада је формула једноставнија: (\exists x) \lnot D(x, 3) и читамо: "постоји x тако да није D(x, 3)", при чему подразумевамо да је x вредност из скупа А. Пошто је ово скраћени запис за једну дугачку дисјункцију чији су прва два дела тачна, а преостала три нетачна, тада је и цела дисјункција, односно PRF^1 такође тачна (довољно је да један део буде тачан, да би цела дисјункција била тачна). ******************************************** Непразан скуп из ког променљива сме да узме вредност називаћемо домен променљиве. Тип података који смо спомињали у SQL-у је заправо домен променљиве заједно са операцијама које се могу изводити на том домену. ============================================================= Оператор EXISTS O1: Имена и презимена студената који имају бар једну осмицу. Имена и презимена студената таквих да у табели Испити за сваког од њих постоји ред који представља испит т(ражен)их студената и оцена на том испиту је 8. Старо решење (природно спајање): SELECT DISTINCT ime, prezime FROM Studenti AS s, Ispiti AS i2 WHERE s.indeks = i.indeks AND i.ocena = 8; Старо решење (оператор IN): SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena = 8); Ново решење (оператор EXISTS): SELECT s.ime, s.prezime FROM studenti AS s WHERE EXISTS (SELECT * FROM ispiti AS i WHERE i.indeks = s.indeks AND i.ocena = 8); (SELECT * FROM ispiti AS i WHERE i.indeks = '10/98 ' AND i.ocena = 8) (SELECT * FROM ispiti AS i WHERE i.indeks = '100/97' AND i.ocena = 8) O2: Имена и презимена студената који имају бар једну оцену која није осмица. Старо решење (природно спајање): SELECT DISTINCT ime, prezime FROM Studenti AS s, Ispiti AS i2 WHERE s.indeks = i.indeks AND i.ocena <> 8; Старо решење (оператор IN): SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena <> 8); Ново решење (оператор EXISTS): SELECT s.ime, s.prezime FROM studenti AS s WHERE EXISTS (SELECT * FROM ispiti AS i WHERE i.indeks = s.indeks AND i.ocena <> 8); O3: Имена и презимена студената који су полагали бар један испит и немају ниједну осмицу. Старо решење (оператор IN): SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti) AND indeks NOT IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena = 8); Ново решење (оператор EXISTS): SELECT s.ime, s.prezime FROM studenti AS s WHERE EXISTS (SELECT * FROM ispiti AS i WHERE i.indeks = s.indeks) AND NOT EXISTS (SELECT * FROM ispiti AS i WHERE i.indeks = s.indeks AND i.ocena = 8); O4: Имена и презимена студената који су полагали бар један испит и на сваком испиту су добили осмицу. Старо решење (оператор IN): SELECT ime, prezime FROM Studenti WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti) AND indeks NOT IN (SELECT DISTINCT indeks FROM ispiti WHERE ocena <> 8); Ново решење (оператор EXISTS): SELECT s.ime, s.prezime FROM studenti AS s WHERE EXISTS (SELECT * FROM ispiti AS i WHERE i.indeks = s.indeks) AND NOT EXISTS (SELECT * FROM ispiti AS i WHERE i.indeks = s.indeks AND i.ocena <> 8); У истом упиту могу се комбиновати IN и EXISTS SELECT s.ime, s.prezime FROM studenti AS s WHERE indeks IN (SELECT DISTINCT indeks FROM ispiti) AND NOT EXISTS (SELECT * FROM ispiti AS i WHERE i.indeks = s.indeks AND i.ocena <> 8); Решење које користи EXISTS може да се примени увек, али је најмање ефикасно јер се сваки корелисани подупит извршава онолико пута колико табела из главног упита има редова (у овом примеру корелисани подупит се извршава 15 пута јер се уместо s.indeks користи 15 различитих вредности из табеле Студенти, пошто та табела има 15 редова). Решење које користи IN је најефикасније јер се подупит извршава само једном, али то решење не може да се примени у свим ситуацијама. Решење које користи природно спајање није толико ефикасно јер се своди на Декартов производ и рестрикцију сувишних спојева, али је најлакше за писање. Међутим, ни оно не може да се примени у свим ситуацијама. Горњи примери показују да се природно спајање не може применити увек, да се понекад може заменити применом оператора IN, али да једино EXISTS може увек да се примени, а понекад је то и једино могуће решење када не можемо да искористимо ни природно спајање, ни оператор IN. ==================================================== Скаларне функције и тип података DATE Tип података DATE је специјалан тип података намењен раду са датумима. Наиме, испоставља се (што илуструје и база са евиденцијом студената) да је лоша пракса чувати информације о датумима у облику ниски. Зато се уводи и посебан тип чије константе у SQL-у имају специфичан формат: * константни датум се пише између знакова # (као што се ниска пише између апострофа) из * константни датум се увек наводи као да се користи енглески правопис, дакле, прво месец, па дан, па година, при чему се као сепаратор користи коса црта /. Дакле, данашњи датум (22.11.2024.) би у SQL-у био записан овако: #11/22/2024# Скаларне функције за рад са датумом (YEAR, MONTH, DAY, DATE, DATEDIFF) Позив функције Резултат позива функције YEAR(#11/22/2024#) 2024 MONTH(#11/22/2024#) 11 DAY(#11/22/2024#) 22 Разлика датума у годинама DATEDIFF('yyyy', #1/29/1976#, #11/22/2024#) as starost_godine месецима DATEDIFF('m', #1/29/1976#, #11/22/2024#) as starost_meseci данима DATEDIFF('y', #1/29/1976#, #11/22/2024#) as starost_dani SELECT YEAR(#11/22/2024#) AS godina, MONTH(#11/22/2024#) AS mesec, DAY(#11/22/2024#) AS dan, DATE() AS [tekući dan], DATEDIFF('yyyy', #1/29/1976#, #11/22/2024#) as starost_godine ; SELECT ime, prezime, Switch( status = 'R', 'redovan', status = 'V', 'vanredan', status = 'S', 'samofinansirajući' ) AS nazivStatusa FROM studenti; ================================================ Агрегатне функције COUNT(*) пребројава редове у резултату (преостале после рестрикције, тј. логичког услова у WHERE) SUM(kolona) збир вредности из колоне kolona (оних вредности које су преостале после рестрикције, тј. логичког услова у WHERE) AVG(kolona) просек вредности из колоне kolona (оних вредности које су преостале после рестрикције, тј. логичког услова у WHERE) AVG(kolona) = SUM(kolona) / COUNT(*) MIN(kolona) најмања међу вредностима из колоне kolona (оним вредностима које су преостале после рестрикције, тј. логичког услова у WHERE) MAX(kolona) највећа међу вредностима из колоне kolona (оним вредностима које су преостале после рестрикције, тј. логичког услова у WHERE) Исписати колико има студената у евиденцији. SELECT COUNT(*) AS [broj studenata] FROM Studenti; Исписати колико има студената у евиденцији који су на смеру R. SELECT COUNT(*) AS [broj studenata na smeru R] FROM Studenti WHERE smer = 'R'; Исписати збир прелазних оцена, број прелазних оцена, просек оцена, најмању и највећу прелазну оцену студента са индексом 103/98. SELECT SUM(i.ocena) AS [Zbir ocena], COUNT(*) AS [broj ocena], AVG(i.ocena) AS prosek, MIN(i.ocena) AS najmanja_ocena, MAX(i.ocena) AS najveca_ocena FROM Studenti AS s, Ispiti AS i WHERE i.indeks = s.indeks AND i.indeks = '103/98' AND i.ocena > 5; Исписати име и презиме студента са индексом 103/98 и његов просек оцена на две децимале. ROUND(broj, koliko_decimala) SELECT ROUND(AVG(i.ocena), 2) AS prosek FROM Studenti AS s, Ispiti AS i WHERE i.indeks = s.indeks AND i.indeks = '103/98' AND i.ocena > 5; Исписати имена и презимена и индексе студената који имају највећу оцену на предмету са шифром 1A01. SELECT s.ime, s.prezime, s.indeks FROM Studenti AS s, Ispiti AS i WHERE s.indeks = i.indeks AND i.sifra_predmeta = '1A01' AND ocena = (SELECT MAX(ocena) FROM Ispiti WHERE sifra_predmeta = '1A01'); ====================================== Груписање (GROUP BY) За све студенте исписати њихово име, презиме, индекс и просечну оцену. Први корак: (нећемо име и презиме, да би нам била довољна табела Ispiti, а индекс је свакако примарни кључ, па се име и презиме могу накнадно додати). За све студенте исписати њихов индекс и просечну оцену. ПОГРЕШНО: SELECT indeks, AVG(ocena) FROM Ispiti WHERE ocena > 5; ИСПРАВНО: (MS ACCESS: Save As proseci) SELECT indeks, AVG(ocena) AS prosek FROM Ispiti WHERE ocena > 5 GROUP BY indeks; SELECT s.ime, s.prezime, s.indeks, ROUND(prosek, 2) AS [prosecna ocena] FROM Studenti AS s, proseci AS pr WHERE s.indeks = pr.indeks; За студенте који су положили бар један испит и имају просечну оцену бар 8.50 исписати њихово име, презиме и просечну оцену. ПРЕПОРУЧЕНИ НАЧИН: Помоћни: (MS ACCESS: Save As proseci) SELECT indeks, AVG(ocena) AS prosek FROM Ispiti WHERE ocena > 5 GROUP BY indeks; Главни: SELECT s.ime, s.prezime, s.indeks, ROUND(prosek, 2) AS [prosecna ocena] FROM Studenti AS s, proseci AS pr WHERE s.indeks = pr.indeks AND pr.prosek >= 8.50; НЕПРЕПОРУЧЕНИ НАЧИН: Помоћни: (MS ACCESS: Save As proseci) SELECT indeks, AVG(ocena) AS prosek FROM Ispiti WHERE ocena > 5 GROUP BY indeks HAVING AVG(ocena) >= 8.50; Главни: SELECT s.ime, s.prezime, s.indeks, ROUND(prosek, 2) AS [prosecna ocena] FROM Studenti AS s, proseci AS pr WHERE s.indeks = pr.indeks; Сортирање резултата по просеку опадајуће, по презимену и имену растуће SELECT s.ime, s.prezime, s.indeks, ROUND(prosek, 2) AS [prosecna ocena] FROM Studenti AS s, proseci AS pr WHERE s.indeks = pr.indeks ORDER BY [prosecna ocena] DESC, prezime, ime; DESC = descending ASC = ascending Комплетан костуре SQL-упита са редоследом извршавања 6 SELECT 1 FROM 2 WHERE 3 GROUP BY 4 HAVING 5 ORDER BY У оквиру самосталног рада код куће, студент треба да направи упите који исписују: 1. Назив и шифру предмета, као и просечну оцену на положеним испитима за сваки предмет понаособ. Помоћни упит (Save As predmeti_proseci) SELECT sifra_predmeta, ROUND(AVG(ocena), 2) AS prosek FROM Ispiti WHERE ocena > 5 GROUP BY sifra_predmeta; Главни упит: SELECT p.naziv, p.sifra_predmeta, pp.prosek FROM predmeti_proseci as pp, Predmeti AS p WHERE pp.sifra_predmeta = p.sifra_predmeta; 2. Исписати смерове на којима студирају студенти заједно са бројем студената по смеру. Резултат сортирати опадајуће по броју студената, а потом по називу смера. Исписати статусе студената заједно са бројем студената по статусу. Резултат сортирати опадајуће по броју студената, а потом по називу статуса. Име, презиме и шифру наставника и просечну оцену на свим положеним испитима код тог наставника за сваког наставника понаособ. Исписати за сваки предмет његов назив као и највећи број полагања тог предмета од стране неког студента.