Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
J
javab_20230617
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Patryk Czarnik
javab_20230617
Commits
f46626bc
Commit
f46626bc
authored
Jul 01, 2023
by
Patryk Czarnik
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Druga część wykładu z SQL
parent
23e021ad
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
0 additions
and
188 deletions
+0
-188
wyklad_kompleksowy_1.sql
PC22-BazyDanychMaven/sql/zajecia/wyklad_kompleksowy_1.sql
+0
-188
wyklad_kompleksowy_2.sql
PC22-BazyDanychMaven/sql/zajecia/wyklad_kompleksowy_2.sql
+0
-0
No files found.
PC22-BazyDanychMaven/sql/zajecia/wyklad_kompleksowy_1.sql
View file @
f46626bc
...
...
@@ -828,191 +828,3 @@ WHERE e.department_id = d.department_id(+);
-- Wszystkie złączenia zewnętrzne dałoby się zastąpić odpowiednim użyciem operatora zbiorów UNION ALL - po prostu byłoby więcej pisania.
--* Koniec tematu JOIN *--
--* Funkcje agregujące *--
SELECT
*
FROM
employees
;
-- W SQL istnieją funkcje "zwykłe" oraz "agregujące".
-- Zwykła funkcja dla każdego rekordu wejściowego zwraca osobny wynik:
SELECT
round
(
salary
)
FROM
employees
;
SELECT
sqrt
(
salary
)
FROM
employees
;
-- Funkcja agregująca zbiera ("kompresuje") wiele rekordów wejściowych i zwraca jeden łączny wynik:
SELECT
avg
(
salary
)
FROM
employees
;
-- O tym, że z wielu rekordów robi się jeden wynik, decyduje sam fakt, że użyliśmy funkcji, która jest "agregująca".
-- Nie widać tego w żaden sposób w strukturze zapytania: porównajmy zapisy z sqrt i avg - są identyczne.
-- Istnieje 5 klasycznych ("kanonicznych") funkcji agregujacych SQL:
SELECT
count
(
salary
),
sum
(
salary
),
avg
(
salary
),
min
(
salary
),
max
(
salary
)
FROM
employees
;
-- count można używać na kilka sposobów:
-- count(*) - ilość rekordów, które są agregowane
-- count(kolumna) , ogólnie count(wyrażenie) - ilość nie-NULL-owych wartości danej kolumny/wyrażenia
-- count(DISTINCT wartość) - ilość różnych nie-NULL-owych wartości
SELECT
count
(
*
),
count
(
department_id
),
count
(
DISTINCT
department_id
)
FROM
employees
;
-- Brakującym rekordem jest Kimberely Grant, której department_id jest równy NULL
SELECT
*
FROM
employees
WHERE
department_id
IS
NULL
;
-- Dodatkowe funkcje agregujące dostęþne w PostgreSQL
-- https://www.postgresql.org/docs/14/functions-aggregate.html
-- Kilka przykładów
SELECT
avg
(
salary
),
stddev
(
salary
),
variance
(
salary
)
FROM
employees
;
SELECT
every
(
length
(
street_address
)
>
length
(
city
))
FROM
locations
;
SELECT
*
FROM
locations
WHERE
length
(
street_address
)
<=
length
(
city
);
SELECT
string_agg
(
city
,
'; '
)
FROM
locations
;
SELECT
json_agg
(
city
)
FROM
locations
;
-- Łącząc funkcje agregujace z warunkiem WHERE możemy obliczyć statystyki tylko dla wybranych rekordów.
-- Ilu jest programistów i jaka jest ich średnia pensja?
SELECT
count
(
*
)
AS
ilu
,
avg
(
salary
)
AS
srednia
FROM
employees
WHERE
job_id
=
'IT_PROG'
;
-- Zauważmy, że gdy stosujemy funkcje agregujące, nie możemy już odwoływać się do wartości pojedynczych rekordów.
-- SELECT count(*) AS ilu, avg(salary) AS srednia, job_id
-- FROM employees
-- WHERE job_id = 'IT_PROG';
-- Np. takie zapytanie jest niepoprawne w SQL
-- i nie działa w PostgreSQL, Oracle, ale działa w SQLite
-- SELECT min(salary), first_name, last_name FROM employees;
--* Klauzule GROUP BY i HAVING *--
-- Powyżej za pomocą WHERE obliczyliśmy ilość i średnią dla rekordów należących do jednej grupy;
-- wszystkich, których pole job_id miało wartość 'IT_PROG'.
-- A gdybyśmy chcieli w jednym zapytaniu obliczyć takie dane dla wszystkich możliwych grup (różnych wartości job_id)?
-- Gdy chcemy obliczyć funkcje agregujące dzieląc dane na kategorie (grupy), to stosujemy właśnie klauzulę GROUP BY.
-- Porównajmy...
-- Wszystkie pensje osobno - 107 wyników
SELECT
salary
FROM
employees
;
-- Jedna liczba - średnia wszystkich - 1 wynik
SELECT
avg
(
salary
)
FROM
employees
;
-- Dla każdej grupy osobno liczona średnia - 19 wyników, bo istnieje 19 różnych wartości job_id
SELECT
avg
(
salary
)
FROM
employees
GROUP
BY
job_id
;
-- W praktyce wypisujemy także kolumnę, ze względu na którą grupowaliśmy, aby wyniki były czytelne:
SELECT
job_id
,
count
(
*
)
AS
ilu
,
avg
(
salary
)
AS
srednia
FROM
employees
GROUP
BY
job_id
;
-- W PostgreSQL, inaczej niż w Oracle, a podobnie jak np. w MySQl, w GROUP BY można użyć aliasu kolumny
SELECT
substring
(
last_name
,
1
,
1
)
AS
litera
,
count
(
*
)
AS
ilosc
FROM
employees
GROUP
BY
litera
ORDER
BY
litera
;
-- W Oraclu trzeba tak:
SELECT
substring
(
last_name
,
1
,
1
)
AS
litera
,
count
(
*
)
AS
ilosc
FROM
employees
GROUP
BY
substring
(
last_name
,
1
,
1
)
ORDER
BY
litera
;
-- Jeśli chcemy przefiltrować całe grupy, to warunek ich dotyczący wpisujemy w klauzulę HAVING.
-- Przykład: wypisz stanowiska, na których pracuje co najmniej 10 osób:
SELECT
job_id
,
count
(
*
)
AS
ilu
,
avg
(
salary
)
AS
srednia
FROM
employees
GROUP
BY
job_id
HAVING
count
(
*
)
>=
10
;
-- WHERE - warunki dot. pojedynczych rekordów stosowane przed grupowaniem
-- HAVING - warunki dotyczące całych rekordów, po grupowaniu
-- Bierzemy pracowników, którzy zarabiają < 6 tys, grupujemy wg stanowisk
-- i następnie wyliczamy średnią tylko dla tych pracowników.
-- Zauważmy, że:
-- 1) wypisuje się stanowisko ST_MAN, bo jeden z ST_MANów zarabia < 6 tys.
-- 2) dla stanowiska IT_PROG średnia wynosi 4600, bo uwzględniamy tylko tych 3 programistów, którzy zarabiają < 6tys.
SELECT
job_id
,
count
(
*
)
AS
ilu
,
avg
(
salary
)
AS
srednia
,
min
(
salary
)
AS
min
,
max
(
salary
)
AS
max
FROM
employees
WHERE
salary
<
6000
GROUP
BY
job_id
;
-- Wszystkich pracowników grupujemy wg stanowisk i wyliczamy średnią dla całych grup.
-- A następnie wyświetlamy tylko te grupy (te stanowiska), na których średnia pensja > 6 tys.
-- Zauważmy, ze:
-- 1) NIE wypisuje się stanowisko ST_MAN, bo średnia ST_MANów wynosi 7280 i nie przechodzi przez HAVING
-- 2) dla stanowiska IT_PROG średnia wynosi 5760, bo to średnia wszystkich programistów
SELECT
job_id
,
count
(
*
)
AS
ilu
,
avg
(
salary
)
AS
srednia
,
min
(
salary
)
AS
min
,
max
(
salary
)
AS
max
FROM
employees
GROUP
BY
job_id
HAVING
avg
(
salary
)
<
6000
;
-- Wypisz te stanowiska, na których co najmniej 5 osób zarabia co najmniej 5 tys.
-- Zauważmy, ze nie ma tu programistów, bo tylko 3 zarabia >= 5tys.
SELECT
job_id
,
count
(
*
)
AS
ilu
,
avg
(
salary
)
AS
srednia
,
min
(
salary
)
AS
min
,
max
(
salary
)
AS
max
FROM
employees
WHERE
salary
>=
5000
GROUP
BY
job_id
HAVING
count
(
*
)
>=
5
;
-- Przy okazji: Zamiast HAVING można też umieści zapytanie z GROUP BY jako podzapytanie
-- w zewnętrznym zapytaniu, a w tym zewnętrznym użyć WHERE.
SELECT
*
FROM
(
SELECT
job_id
,
count
(
*
)
AS
ilu
,
avg
(
salary
)
AS
srednia
,
min
(
salary
)
AS
min
,
max
(
salary
)
AS
max
FROM
employees
GROUP
BY
job_id
)
podzapytanie
WHERE
srednia
<
6000
;
-- Jeśli stosujemy grupowanie, to w klauzulach SELECT oraz HAVING nie możemy odwoływać się bezpośrednio do tych kolumn,
-- które nie są wymienione w GROUP BY.
-- Dla pozostałych musimy użyć funkcje agregującej.
-- O ile takie zapytanie nie ma żadnego sensu:
-- źle
SELECT
first_name
,
last_name
,
job_id
,
avg
(
salary
)
FROM
employees
GROUP
BY
job_id
;
-- To takie zapytania (z użyciem min i max) mają sens i zadziałają np. w SQLite,
-- ale PostgreSQL (jak i Oracle) dla zasady zabrania odczytywania pojedynczych wartości, jeśli stosujemy grupowanie i agregacje.
-- źle
SELECT
first_name
,
last_name
,
job_id
,
min
(
salary
)
FROM
employees
GROUP
BY
job_id
;
SELECT
first_name
,
last_name
,
job_id
,
max
(
salary
)
FROM
employees
GROUP
BY
job_id
;
-- Dalsze rozważania: po czym grupować?
-- Grupowanie po danych tekstowych, które w tabeli nie są zindeksowane jest mało wydajne.
SELECT
department_name
,
count
(
employee_id
)
AS
ilu
,
round
(
avg
(
salary
),
2
)
AS
srednia
FROM
employees
FULL
JOIN
departments
USING
(
department_id
)
GROUP
BY
department_name
;
-- Bardziej wydajne będzie grupowanie po ID, które mamy zindeksowane
-- tutaj można dopisać grupowanie po department_id, skoro department_name wynika bezpośrednio z department_id
-- ale nie zawsze można pogrupować po samym id
-- SELECT department_name, count(employee_id) AS ilu, round(avg(salary),2) AS srednia
-- FROM employees FULL JOIN departments USING(department_id)
-- GROUP BY department_id;
-- grupowanie po id będzie pierwszym krokiem, a dla formalności musimy dopisać grupowanie po name
SELECT
department_name
,
count
(
employee_id
)
AS
ilu
,
round
(
avg
(
salary
),
2
)
AS
srednia
FROM
employees
FULL
JOIN
departments
USING
(
department_id
)
GROUP
BY
department_id
,
department_name
;
-- czy można pogrupować po samym ID, jeśli z niego wynika inna wartość (np. name)?
-- W Oraclu po prostu nie można
-- w Postgresie w zależności od struktury zapytania czasami można pogrupować po samym id:
SELECT
department_name
,
count
(
employee_id
)
AS
ilu
,
round
(
avg
(
salary
),
2
)
AS
srednia
FROM
departments
LEFT
JOIN
employees
USING
(
department_id
)
GROUP
BY
department_id
;
-- 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
PC22-BazyDanychMaven/sql/zajecia/wyklad_kompleksowy_2.sql
0 → 100644
View file @
f46626bc
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment