COMMENTONTABLEregionsIS'Regions table that contains region numbers and names. Contains 4 rows; references with the Countries table.';
COMMENTONCOLUMNregions.region_idIS'Primary key of regions table.';
COMMENTONCOLUMNregions.region_nameIS'Names of regions. Locations are in the countries of these regions.';
COMMENTONTABLElocationsIS
'Locations table that contains specific address of a specific office,
warehouse, and/or production site of a company. Does not store addresses /
locations of customers. Contains 23 rows; references with the
departments and countries tables. ';
COMMENTONCOLUMNlocations.location_idIS
'Primary key of locations table';
COMMENTONCOLUMNlocations.street_addressIS
'Street address of an office, warehouse, or production site of a company.
Contains building number and street name';
COMMENTONCOLUMNlocations.postal_codeIS
'Postal code of the location of an office, warehouse, or production site
of a company. ';
COMMENTONCOLUMNlocations.cityIS
'A not null column that shows city where an office, warehouse, or
production site of a company is located. ';
COMMENTONCOLUMNlocations.state_provinceIS
'State or Province where an office, warehouse, or production site of a
company is located.';
COMMENTONCOLUMNlocations.country_idIS
'Country where an office, warehouse, or production site of a company is
located. Foreign key to country_id column of the countries table.';
COMMENTONTABLEdepartmentsIS
'Departments table that shows details of departments where employees
work. Contains 27 rows; references with locations, employees, and job_history tables.';
COMMENTONCOLUMNdepartments.department_idIS
'Primary key column of departments table.';
COMMENTONCOLUMNdepartments.department_nameIS
'A not null column that shows name of a department. Administration,
Marketing, Purchasing, Human Resources, Shipping, IT, Executive, Public
Relations, Sales, Finance, and Accounting. ';
COMMENTONCOLUMNdepartments.manager_idIS
'Manager_id of a department. Foreign key to employee_id column of employees table. The manager_id column of the employee table references this column.';
COMMENTONCOLUMNdepartments.location_idIS
'Location id where a department is located. Foreign key to location_id column of locations table.';
COMMENTONTABLEjob_historyIS
'Table that stores job history of the employees. If an employee
changes departments within the job or changes jobs within the department,
new rows get inserted into this table with old job information of the
employee. Contains a complex primary key: employee_id+start_date.
Contains 25 rows. References with jobs, employees, and departments tables.';
COMMENTONCOLUMNjob_history.employee_idIS
'A not null column in the complex primary key employee_id+start_date.
Foreign key to employee_id column of the employee table';
COMMENTONCOLUMNjob_history.start_dateIS
'A not null column in the complex primary key employee_id+start_date.
Must be less than the end_date of the job_history table. (enforced by
constraint jhist_date_interval)';
COMMENTONCOLUMNjob_history.end_dateIS
'Last day of the employee in this job role. A not null column. Must be
greater than the start_date of the job_history table.
(enforced by constraint jhist_date_interval)';
COMMENTONCOLUMNjob_history.job_idIS
'Job role in which the employee worked in the past; foreign key to
job_id column in the jobs table. A not null column.';
COMMENTONCOLUMNjob_history.department_idIS
'Department id in which the employee worked in the past; foreign key to deparment_id column in the departments table';
COMMENTONTABLEcountriesIS
'country table. Contains 25 rows. References with locations table.';
COMMENTONCOLUMNcountries.country_idIS
'Primary key of countries table.';
COMMENTONCOLUMNcountries.country_nameIS
'Country name';
COMMENTONCOLUMNcountries.region_idIS
'Region ID for the country. Foreign key to region_id column in the departments table.';
COMMENTONTABLEjobsIS
'jobs table with job titles and salary ranges. Contains 19 rows.
References with employees and job_history table.';
COMMENTONCOLUMNjobs.job_idIS
'Primary key of jobs table.';
COMMENTONCOLUMNjobs.job_titleIS
'A not null column that shows job title, e.g. AD_VP, FI_ACCOUNTANT';
COMMENTONCOLUMNjobs.min_salaryIS
'Minimum salary for a job title.';
COMMENTONCOLUMNjobs.max_salaryIS
'Maximum salary for a job title';
COMMENTONTABLEemployeesIS
'employees table. Contains 107 rows. References with departments,
jobs, job_history tables. Contains a self reference.';
COMMENTONCOLUMNemployees.employee_idIS
'Primary key of employees table.';
COMMENTONCOLUMNemployees.first_nameIS
'First name of the employee. A not null column.';
COMMENTONCOLUMNemployees.last_nameIS
'Last name of the employee. A not null column.';
COMMENTONCOLUMNemployees.emailIS
'Email id of the employee';
COMMENTONCOLUMNemployees.phone_numberIS
'Phone number of the employee; includes country code and area code';
COMMENTONCOLUMNemployees.hire_dateIS
'Date when the employee started on this job. A not null column.';
COMMENTONCOLUMNemployees.job_idIS
'Current job of the employee; foreign key to job_id column of the
jobs table. A not null column.';
COMMENTONCOLUMNemployees.salaryIS
'Monthly salary of the employee. Must be greater
than zero (enforced by constraint emp_salary_min)';
COMMENTONCOLUMNemployees.commission_pctIS
'Commission percentage of the employee; Only employees in sales
department elgible for commission percentage';
COMMENTONCOLUMNemployees.manager_idIS
'Manager id of the employee; has same domain as manager_id in
departments table. Foreign key to employee_id column of employees table.
(useful for reflexive joins and CONNECT BY query)';
COMMENTONCOLUMNemployees.department_idIS
'Department id where employee works; foreign key to department_id
-- 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"varchar(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 *--
-- INSERT - wstawia zupełnie nowy rekord do tabeli (takie id nie może wcześniej występować)
-- 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