-- Gdybym podczas tworzenia tabeli użył nazwy "cytowanej",
-- to utworzona tabela będzie miała dokładnie taką wielkość liter, jakiej użyłem.
CREATETABLE"TesT2"("KOLUMNA"varchar2(10));
INSERTINTO"TesT2"VALUES('XYZ');
-- Teraz jedynym sposobem poprawnego odwołania się do tej tabeli jest użycie nazwy cytowanej "TesT2"
SELECT*FROM"TesT2";
-- Bo gdy użyjemy nazwy niecytowanej, to niezależnie od wielkości liter, zostanie ona zamieniona na DUZE LITERY
SELECT*FROMtest2;-- źle
SELECT*FROMtEst2;-- źle bo Postgres szuka tabeli "test2", a tabela nazywa się "TesT2"
-- Nazwy "cytowane" mogą się przydać, jeśli chcemy zachować wielkość liter, użyć nazwy zarezerwowanej jako słowo kluczowe, użyć w nazwie znaków specjalnych, w tym spacji.
CREATETABLE"Roczne zarobki"("wartość w złotych"numeric(10,2));
INSERTINTO"Roczne zarobki"VALUES(1234.56);
SELECT*FROM"Roczne zarobki";
DROPTABLEtest1;
DROPTABLE"TesT2";
DROPTABLE"Roczne zarobki";
-- Ja osobiście piszę nazwy bez cudzysłowów i małymi literami.
-- A słowa kluczowe SQL piszę wielkimi.
SELECT*FROMemployees;
-- Ale konwencje w świecie SQL bywają różne. "Oraclowcy" często piszą odwrotnie
select*fromEMPLOYEES;
--* Polecenia modyfikacji danych (DML) - podstawy *--
--* Polecenia modyfikacji danych (DML) - podstawy *--
-- INSERT - wstawia zupełnie nowy rekord do tabeli (takie id nie może wcześniej występować)
-- INSERT - wstawia zupełnie nowy rekord do tabeli (takie id nie może wcześniej występować)
...
@@ -520,4 +604,1215 @@ SELECT * FROM employees
...
@@ -520,4 +604,1215 @@ SELECT * FROM employees
LIMIT10;
LIMIT10;
--* JOIN i zapytania do wielu tabel *--
SELECT*FROMemployeesORDERBY1;
SELECT*FROMdepartmentsORDERBY1;
SELECT*FROMlocationsORDERBY1;
-- Zgodnie z najlepszymi praktykami dane w bazach rozdziela się do osobnych tabel i wiąże za pomocą "kluczy obcych".
-- Często w zapytaniach chcemy jednak uzyskać rekordy z jednej tabeli wraz z powiązanymi danymi z innych tabel.
-- Można we FROM podać więcej niż jedną tabelę:
SELECT*FROMemployees,departments;
-- W takiej sytuacji baza danych zwraca wszystkie możliwe kombinacje rekordów z jednej tabeli z rekordami z drugiej tabeli
-- To jest tzw. "iloczyn kartezjański".
SELECTcount(*)FROMemployees,departments;
SELECTcount(*)FROMemployees;-- 107
SELECTcount(*)FROMdepartments;-- 27
SELECT107*27;
-- Aby zobaczyć tylko prawidłowo dopasowane rekordy, można dodać warunek w WHERE:
-- Jeśli używamy składni opartej o JOIN, możemy jednak ten problem rozwiązać.
-- Możemy wybrać rodzaj złączenia i w ten sposób powiedzieć co ma się stać z rekordami, które są w jednej tabeli, a nie mają dopasowania w drugiej tabeli.
-- Złączenia domyślnie są wewnętrzne, tzn. niepasujące rekordy nie są wyświetlane.
-- 106 wyników, nie ma K.Grant, nie też departamentów, w których nikt nie pracuje, np. Payroll:
-- 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