-- Działa dla INNER JOIN oraz LEFT JOIN gdy tabela jest po lewej, dla RIGHT JOIN gdy tabela po prawej, nie działa dla FULL JOIN
---- Dodatkowe możliwości GROUP BY ----
-- (temat zaawansowany; jesli wydaje się trudny, to przeskocz i przejdź do JOINów)
-- wprowadzenie: można grupować wg kilku kryteriów, ale wtedy podsumowania są robione dla wszystkich kombinacji wartości
SELECTdepartment_id,job_id,count(*),avg(salary)
FROMemployees
GROUPBYdepartment_id,job_id
ORDERBY1,2;
-- W Oracle istnieją specjalne dodatki do GROUP BY do tworzenia częściowych podsumowań
-- W PostgreSQL też ;-)
-- Najczęściej używa się ROLLUP. Pozostałe: CUBE i GROUPING_SETS
SELECTdepartment_id,job_id,count(*),avg(salary)
FROMemployees
GROUPBYROLLUP(department_id,job_id)
ORDERBY1,2;
-- Jeśli grupujemy po jednym kryterium, to ROLLUP i CUBE dopisują jeden wiersz podsumowujący wszystkie dane (tak jakbyśmy policzyli funkcje agregujące bez wcześniejszego grupowania).
-- W miejsce kolumn, po których grupowaliśmy, w wierszu podsumowania wstawiany jest NULL, a funkcja grouping(kolumna) zwraca 1.
-- ntile(N) podaje info, w ktorej czesci znalazlby sie wynik, gdybysmy posortowane wyniki podzielili na N rownych czesci
-- Jesli uzyjemy tego z polaczeniu z PARTITION BY, to kazda grupe dzieli na N czesci i infromuje w ktorej czesci znajduje sie konkretny rekord
-- Tutaj dowiemy sie czy pracownik nalezy do bogatszej polowy (1) na swoim stanowiksu, czy do biednejszej polowy (2)
-- Przy czym to dziala "technicznie" i w przypadku rownych pensji (zob. AD_VP) pracownicy o tej samej pensji moga trafic do roznych grup, bo akurat tu przebiega granica
SELECTfirst_name,last_name,salary,job_id,
ntile(2)OVER(PARTITIONBYjob_idORDERBYsalaryDESC)
FROMemployees;
-- Tutaj dzielimy pracownikow na 10 czesci wg pensji.
SELECTfirst_name,last_name,salary,
ntile(10)OVER(ORDERBYsalaryDESC)
FROMemployees;
SELECTfirst_name,last_name,salary,
ntile(10)OVER(ORDERBYsalaryDESC)
FROMemployees
ORDERBYlast_name,first_name;
-- odwolanie do wartosci, ktora wystapila wczesniej, (do "sasiada")
-- Przykład: znajdź pracowników, w których departamencie istnieje
-- inny pracownik zarabiający tyle samo
SELECTfirst_name,last_name,salary,department_id
FROMemployeeszewn
WHERE(salary,department_id)IN
(SELECTsalary,department_id
FROMemployeeswewn
WHEREzewn.employee_id<>wewn.employee_id);
-- Wypisz departamenty, w których ktoś zarabia między 9 a 10 tys.
SELECTdepartment_id,department_name
FROMdepartmentsdep
WHEREEXISTS(SELECTemployee_idFROMemployees
WHEREdepartment_id=dep.department_id
ANDsalaryBETWEEN9000AND10000);
-- Dla operatora EXISTS liczy się tylko to, czy podzapytanie zwróciło jakikolwiek wynik.
-- Dlatego obowiązuje konwencja pisania SELECT 1 w podzapytaniach używanych w ramach EXISTS
-- To może zwiększyć wydajność. A EXISTS i tak jest wydajnym rozwiazaniem, bo wyniki podzapytania nie musza byc wczytywane do konca, wystarczy ze pojawi sie pierwszy wynik.
-- Czesto EXISTS odwoluje sie tylko do indeksow, a nie musi czytac docelowej tabeli.
SELECTdepartment_id,department_name
FROMdepartmentsdep
WHEREEXISTS(SELECT1FROMemployees
WHEREdepartment_id=dep.department_id
ANDsalaryBETWEEN9000AND10000);
-- Dodatkowe wyjaśnienia:
-- To podzapytanie czasami zwraca jakieś rekordy -> to wtedy EXSISTS da wynik TRUE
SELECTemployee_idFROMemployees
WHEREdepartment_id=80
ANDsalaryBETWEEN9000AND10000;
SELECT1FROMemployees
WHEREdepartment_id=80
ANDsalaryBETWEEN9000AND10000;
-- A czasami wynik jest pusty -> wtedy EXISTS daje wynik FALSE