#147
Rozdział 7.
Przetwarzanie wyników zapytań
Poznane dotychczas właściwości instrukcji SELECT pozwalają na wyszukiwanie w bazie potrzebnych danych. Przy jej pomocy znajdowaliśmy przychody z wybranego filmu lub wszystkich reżyserów zamieszkałych w podanym stanie.
Wyrażenie SELECT posiada także bogate możliwości tworzenia różnego rodzaju wyliczeń na danych, pozwalające spojrzeć na dane w sposób bardziej całościowy.
Można wyliczać wartości średnie, sumy dla wybranych kolumn lub dzielić dane na kategorie i wśród nich dokonywać obliczeń. Na przykład wykorzystując funkcje agregujące i grupowanie danych można wyliczyć średni budżet dla filmów wyprodukowanych przez konkretne studio filmowe lub przychody z filmów wyprodukowanych w wybranym roku.
Ten rozdział omawia funkcje agregujące, które umożliwiają wykonywanie obliczeń na wybranych kolumnach. Pokazane zostanie również wykorzystanie klauzuli GROUP BY, która umożliwia podział wyników na kategorie.
Wybór wartości unikalnych: operator DISTINCT
Tabela Movies zawiera kilka filmów z każdego studia. Gdybyśmy chcieli otrzymać po prostu listę wszystkich studiów filmowych, to zapytanie z listingu 7.1 nie wykona zadania poprawnie.
------------------------
Listing 7.1. Wybranie wszystkich studiów z bazy danych
SELECT studio_id
FROM Movies
#148
STUDIO_ID
-------------
1
2
4
3
1
2
3
3
4
1
10 rows selected
------------------------
Jak widać, wszystkie studia pojawiają się na liście, ale niektóre z nich pojawiają się wielokrotnie. Język SQL posiada operator DISTINCT, który umożliwia wyeliminowanie z wyniku wartości powtarzających się. Przykład zastosowania tego operatora znajduje się na listingu 7.2.
------------------------
Listing 7.2. Wybór -wartości niepowtarzalnych z tabeli Movies
SELECT DISTINCT studio_id
FROM Movies
STUDIO_ID
---------
1
2
3
4
------------------------
Jak widać, zastosowanie operatora DISTINCT spowodowało, że na liście wynikowej nie ma wartości powtarzających się. Podstawowa składnia wyrażeń wykorzystujących operator DISTINCT wygląda następująco:
SELECT [DISTINCT] select_list
FROM tablet , table, ...]
WHERE expression]
[ORDER BY expression]
Wybieranie unikalnych kombinacji wartości
W przypadku, gdy na liście pojawia się kilka nazw kolumn, wybierana jest każda niepowtarzalna kombinacja wartości. Listing 7.3 zawiera listę studiów filmowych, identyfikatory reżyserów i tytuły filmów.
------------------------
Listing 7.3. Tytuł (movie_title), studio (studio_id), identyfikator reżysera (directorjd)
z tabeli Filmy (Movies)
SELECT movie_title, studio_id, directo'r_id
FROM Movies
MOVIE_TITLE STUDIO_ID DIRECTOR_ID
------------------------
Yegetable House 1 1
Prince Kong 2 10
The Code Warrior 4 2
Bulli Durham 3 9
Codependence Day 1 1
#149
The Linux Files 2 2
SQL Strikes Back 3 9
The Programmer 3 1
Hard Code 4 10
The Rear Windows 1 1
10 rows selected
------------------------
Kolejny listing 7.4 zawiera listę studiów filmowych i reżyserów, z której, używając operatora DISTINCT, wyeliminowano wartości powtarzające się.
------------------------
Listing 7.4. Niepowtarzalne kombinacje kolumn studio Jd i directorj.d
SELECT DISTINCT studio_id, dlrector_id
FROM Movles
STUDIO_ID DIRECTOR_ID
------------------------
1 1
2 2
2 10
3 1
3 9
4 2
4 10
7 rows selected
------------------------
7 wierszy zostało wybranych na listingu 7.4, ponieważ dwa filmy były wykonane przez tego samego reżysera w tym samym studiu filmowym (wiemy o tym na podstawie danych z listingu 7.3).
Gdyby jednak w listingu 7.4 zostały wybrane 3 kolumny, to jako wynik zapytania pojawiłyby się unikalne kombinacje 3 wartości.
Zwróćmy uwagę na listing 7.5. ------------------------
Listing 7.5. Wyszukanie w tabeli wszystkich niepowtarzalnych wierszy
SELECT DISTINCT *
FROM Movies
------------------------
Zapytanie z listingu 7.5 zwraca wszystkie wiersze z tabeli Movies. W przypadku, gdyby zwróciło ich mniej, to oznaczałoby, że niektóre wiersze są identyczne. Jednak jak wiadomo, spełnienie wymogów relacyjnego modelu bazy danych nie dopuszcza na wprowadzenie do tabeli powtarzającego się wiersza. Klucz powinien być unikalny dla każdego wiersza, co zapewnia, że w tabeli nigdy nie ma dwóch identycznych wierszy.
DISTINCT i NULL
Jak wiadomo z wcześniejszej dyskusji o wartościach null, nie mogą one być uważane za wartości sobie równe, ponieważ ich wartość jest nieokreślona. Jednak operator DISTINCT łamie tę zasadę i w wyrażeniach z tym operatorem wszystkie wartości null są traktowane jak wartości identyczne.
#150
Wybór wartości z kolumny gross przy pomocy operatora DISTINCT pokazuje podstawowe właściwości tego operatora.
------------------------
Listing 7.6. Niepowtarzalne wartości z kolumny przychód (gross)
SELECT DISTINCT gross
FROM Movies
GROSS
--------
10
17.5
17.8
30
45.3
6 rows selected
------------------------
Jak wiadomo, dwa wiersze z tabeli Movies mają w kolumnie przychód (gross) wartość null. W zapytaniu pojawia się tylko jedna wartość null (co jest zgodne z zasadą działania operatora DISTINCT ).
Funkcje agregujące
W języku SQL dostępnych jest bardzo dużo funkcji agregujących. Są to funkcje, które nie działają na pojedynczej wartości pola, ale na grupie wartości zwracanych przez zapytanie. Na przykład możemy w tabeli policzyć liczbę wierszy spełniających określone kryteria lub możemy wyliczyć wartość średnią dla wszystkich wartości z wybranej kolumny.
Funkcje te działają na wszystkich wierszach w tabeli, na pewnej grupie wierszy wybranej klauzulą WHERE lub na grupach danych wybranych klauzulą GROUP BY.
Funkcji agregujących używamy posługując się składnią:
SELECT function(column)
FROM table [, table ...]
[WHERE condition]
Użycie tych funkcji zwykle związane jest z operacją na jednej kolumnie (na której wykonywane są obliczenia), a jako wynik zwracany jest tylko jeden wiersz.
Listing 7.7 zawiera zapytanie wykorzystujące funkcję agregującą COUNT ():
------------------------
Listing 7.7. Użycie funkcji agregującej
SELECT COUNT(*)
FROM Movies
COUNTI*)
---------
10
------------------------
#151
Jak widać, wynikiem zapytania jest jeden wiersz. Zawiera on liczbę wszystkich wierszy z tabeli Movies. Gdyby do listy pól w części select_list dodać wyrażenie nie będące funkcją agregującą, to pojawi się komunikat informujący o błędzie. Pokazuje to listing 7.8.
------------------------
Listing 7.8. Wybranie kolumny i funkcji agregującej w jednym zapytaniu
SELECT title, COUNT(*)
FROM Movies
SELECT title, COUNTI*)
FROM Movies
ERROR at line 1
ORA 00937: not a single group group function
------------------------
Z drugiej strony można bez problemu użyć wyrażenia matematycznego i łańcuchowego, jak pokazuje listing 7.9.
------------------------
Listing 7.9. Wyrażenia łańcuchowe z funkcją agregującą
SELECT 'There arę', COUNT(*1, 'movies in the database'
FROM Movies;
'THERE ARE COUNTI*) 'MOVIESINTHEDATABASE'
------------------------------------------------
There are 8 movies in the database
------------------------
Klauzula GROUP BY, która będzie szczegółowo omówiona dalej, umożliwia podział zapytania na grupy względem wartości z wybranej kolumny i wykonanie funkcji agregującej dla każdej tak zdefiniowanej kategorii.
================
Rada
Można użyć wielu funkcji agregujących w części SELECT zapytania. Na przykład aby wyliczyć sumę budżetu i przychodów w jednym zapytaniu należy napisać następującą instrukcję:
SELECT SUM(budget), SUM(gross)
FROM Movies
==================
Funkcje agregujące i klauzula WHERE
Klauzula WHERE może być wykorzystana w zapytaniach używających funkcji agregujących do określenia wierszy, na których będą wykonywane obliczenia. Na przykład zapytanie, które zliczy filmy wyprodukowane latach osiemdziesiątych wygląda, tak jak na listingu 7.10.
------------------------
Listing 7.10. Użycie funkcji agregującej z klauzulą WHERE
SELECT COUNTI*)
FROM Movies
WHERE release_date BETWEEN '01-JAN-19801 AND '31-DEC-1989'
COUNT(*)
--------
2
------------------------
#152
W przykładowej bazie danych znajdują się tylko dwa filmy z tego okresu. Każde wyrażenie WHERE, które może być wykorzystane w standardowej instrukcji SE-LECT, może być zastosowane wraz z funkcjami agregującymi.
Zmiana nazwy wyników funkcji agregującej
Słowo kluczowe AS może być wykorzystane do zmiany nazwy rezultatów funkcji agregujących, tak jak może być wykorzystane do zmiany nazwy każdego innego wyrażenia. Listing 7.11 zawiera przykład wykorzystania AS w zapytaniu agregującym.
-----------------------
Listing 7.11. Słowo kluczowe AS w zapytaniu agregującym
SELECT COUNTI*) AS total
FROM Movies
TOTAL
-------
8
------------------------
====================
Rada
Jeśli zamierzasz wstawiać kod zapytania do programu, musisz pamiętać o zmianie nazw kolumn będących wynikiem funkcji agregujących. Zmiany nazwy dokonujemy poprzez użycie słowa kluczowego AS. W większości języków programowania nie jest możliwe korzystanie z nazw nadawanych automatycznie.
===================
Funkcja COUNT()
Pierwszą funkcją agregującą, którą chcę dokładnie omówić, jest funkcja COUNT (). Użyłem jej już w kilku listingach w tym rozdziale. Funkcja zlicza ilość wierszy w zapytaniu.
Jeśli chcemy znać ilość wierszy zwróconych przez zapytanie, najprościej użyć funkcji COUNT(*), jak pokazałem we wcześniejszych częściach tego rozdziału. Są dwa powody, dla których warto używać funkcji COUNT (*) do tego celu. Po pierwsze, pozwalamy optymalizatorowi bazy danych wybrać kolumnę do wykonywania obliczeń, co czasem nieznacznie podnosi wydajność zapytania, po drugie, nie musimy się martwić o wartości null zawarte w kolumnie oraz o to, czy kolumna o podanej nazwie w ogóle istnieje. Zapytanie z listingu 7.12 zlicza osoby ze stanu Teksas.
------------------------
Listing 7.12. Zliczanie wierszy w tabeli Movies
SELECT COUNT(*)
FROM People
WHERE person stśte = 'TX'
COUNT(*)
----------
4
------------------------
#153
Funkcja COUNT() i wartości null
Funkcja w postaci couNT(column_name) nie uwzględnia pól z wartościami null. Użycie funkcji w postaci COUNT (*) zlicza wszystkie wiersze bez względu na zawartość. Fakt, że wiersze z wartością null nie są zliczane, może być przydatny, gdy wartość null oznacza coś szczególnego. Na przykład w tabeli Movies wartość null w kolumnie przychód wskazuje, że film jeszcze nie został skierowany do dystrybucji i nie zarobił pieniędzy. Zliczając ilość pozycji w polu przychód (gross) można określić ile filmów jest w dystrybucji. Pokazuje to listing 7.13.
------------------------
Listing 7.13. Zliczanie wierszy w kolumnie gdzie występują wartości null
SELECT COUNT(gross)
FROM Movies
COUNT(GROSS)
-------------
8
------------------------
Dwa wiersze z wartościami null w polu przychód (gross) nie są brane pod uwagę w wyniku zapytania.
Funkcja COUNT() i operator DISTINCT
Operator DISTINCT może być użyty równocześnie z funkcją COUNT (), aby zwrócić liczbę unikalnych rekordów. Używamy go według przedstawionej poniżej składni:
SELECT COUNT(DISTINCT column) FROM table
Zapytanie na listingu 7.14 zlicza studia w tabeli Movies, uwzględniając każde z nich tylko raz.
------------------------
Listing 7.14. Zliczanie tylko wartości niepowtarzalnych
SELECT COUNT(DISTINCT studio_id)
FROM Movies
COUNT(DISTINCTSTUDIO_ID)
------------------------
4
------------------------
Funkcje SUM() i AVG()
Funkcja SUM () dodaje wszystkie wartości i zwraca pojedynczy wynik, a funkcja AVG (} wylicza wartość średnią dla grupy wartości. W przeciwieństwie do funkcji COUNT (), która działa dla wszystkich typów danych, funkcje SUM () i AVG {) działają tylko dla argumentów liczbowych.
#154
Listing 7.15 pokazuje zapytanie, które wylicza budżet wszystkich filmów zgromadzonych w bazie danych. Zapytanie z listingu 7.16 wylicza średni budżet dla tych filmów.
------------------------
Listing 7.15. Użycie funkcji SUM() do zsumowania wartości w kolumnie
SELECT SUM(budget)
FROM Movies
SUM(BUDGET)
--------------
262.85
------------------------
------------------------
Listing 7.16. Utycie funkcji A VG() do obliczenia wartości średniej
SELECT AVG(budget)
FROM Movies
AVG(BUDGET)
----------
26.285
------------------------
===============
Rada
W przypadku funkcji SUM () i AVG (} wartości null są ignorowane (nie są uwzględniane do obliczeń).
==================
Obie funkcje mogą być użyte łącznie z operatorem DISTINCT, podobnie jak funkcja COUNT (). Jeśli użyjemy tego operatora, to obliczenia są przeprowadzane tylko dla wartości unikalnych. Porównajmy wyniki zapytań przedstawionych na listingach 7.16, 7.17 z tymi na listingach 7.18 i 7.19.
------------------------
Listing 7.17. Funkcja SUM() dodaje wartości w wybranej kolumnie
SELECT SUM(DISTINCT budget)
FROM Movies
SUM(DISTINCT BUDGET)
------------------------
65.1
------------------------
------------------------
Listing 7.18. Wyliczenie wartości średniej
SELECT AVG(DISTINCT budget)
FROM Movies
AVG(DISTINCT BUDGET)
------------------------
23.65
------------------------
Funkcje MIN() i MAX()
Funkcja MIN () służy do znajdowania wartości najmniejszej w zbiorze wartości, a funkcja MAX () największej. Obie funkcje mogą być użyte dla różnych typów danych.
#155
Funkcja MAX () znajduje największy łańcuch danych (zgodnie z regułami porównywania łańcuchów omówionymi w rozdziale 5.), najnowszą datę (lub najodleglejszą w przyszłości) oraz największą liczbę w zestawieniu. Funkcja MIN {) znajduje odpowiednio wartości najmniejsze.
=================
Rada
Ani funkcja MIN () ani MAX () nie zwraca wartości null, jeżeli w zestawieniu występują jeszcze inne wartości. Wartość null jest traktowana jako nieokreślona i nie można jej porównywać z innymi.
=================
Zarówno funkcja MAX () jak i funkcja MIN () mogą być stosowane łącznie z operatorem DISTINCT, ale nie ma to większego znaczenia, ponieważ obie funkcje i tak zwracają tylko jedną wartość z zestawienia.
Listing 7.19 przedstawia zapytanie wykorzystujące funkcję MIN{) do znalezienia najwcześniej wydanego filmu.
------------------------
Listing 7.19. Wyszukanie najstarszej daty w tabeli Movies
SELECT MIN(release_date)
FROM Movies
MIN(RELEA)
---------------
01-JAN-75
------------------------
Wykonywanie obliczeń z podziałem na kategorie
Załóżmy, że potrzebujemy dane do raportu zawierającego zestawienie budżetu każdego studia filmowego. Można tego dokonać wykonując wiele zapytań, z których każde wylicza budżet wybranego studia. Przykładowe zapytanie pokazuje listing 7.20.
------------------------
Listing 7.20. Zapytanie wyliczające budżet studia Delighted Artists
SELECT SUM(budget)
FROM Movies
WHERE studio_id = 3
SUM(BUDGET)
--------------
65.1
------------------------
Taki sposób rozwiązania tego zadania rodzi dwa problemy. Po pierwsze, musimy utworzyć oddzielne zapytanie dla każdego studia filmowego. Po drugie, musimy znać nazwy wszystkich studiów filmowych wprowadzonych do bazy. W związku z tym metoda ta okazuje się bardzo niepraktyczna przy tworzeniu podobnych raportów.
Zamiast tworzyć oddzielne zapytania, możemy użyć klauzuli GROUP BY, która automatycznie dzieli wyniki zapytania na wybrane kategorie.
#156
Klauzula GROUP BY
Klauzula GROUP BY umożliwia grupowanie wyników względem zawartości wybranej kolumny. W pewnym sensie jej działanie jest podobne do operatora DISTINCT, ponieważ zwraca pojedynczy wynik dla każdej kategorii. Jeżeli jednak użyjemy w zapytaniu jednocześnie funkcji agregującej dla innej kolumny, to funkcja ta dokona zliczeń dla kategorii określonych w klauzuli GROUP BY. Składnia zapytania wykorzystującego klauzule GRUP BY wygląda następująco:
SELECT select_list FROM table [ , table, ...]
[WHERE condition) [GROUP BY group_by_list]
[ORDER BY order by list]
Liczba wierszy stanowiących wynik zapytania jest taka sama, jak liczba wierszy zwrócona przez zapytanie z operatorem DISTINCT dla kolumny wybranej przez klauzulę GROUP BY.
Na listingu 7.21 użyłem funkcji SUM () do wyliczenia łącznego budżetu filmów z tabeli Filmy (Movies). Na listingu 7.22 dzięki wykorzystaniu w zapytaniu klauzuli GROUP BY wyliczenia przeprowadzone są dla każdej kategorii osobno.
------------------------
Listing 7.21. Łączny budżet z tabeli Filmy (Movies)
SELECT SUM(budget)
FROM Movies
SUM(BUDGET)
-------------
277.85
------------------------
------------------------
Listing 7.22. Budżet filmów z podziałem na każde studio filmowe
SELECT studio_id, SUM(budget)
FROM Movies
GROUP BY studio id
STUDIO_ID SUM(BUDGET)
1 85
2 24.25
3 65.1
4 87.3
------------------------
Jak widać, budżet został wyliczony dla każdego studia filmowego osobno.
------------------------
Listing 7.23. Użycie operatora DISTINCT do wybrania wartości niepowtarzalnych
SELECT DISTINCT studio_id
FROM Movies
STUDIO_ID
------------
1
2
3
4
#157
W tym wypadku użycie klauzuli GROUP BY bez funkcji agregujących daje taki sam
efekt, jak użycie operatora DISTINCT. Pokazuje to listing 7.24.
------------------------
Listing 7.24. Klauzula GROUP BY bez funkcji agregujących
SELECT studio_id
FROM Movies
GROUP BY studlo_id
STUDIO_ID
----------
1
2
3
4
------------------------
Kolejne podobieństwo GROUP BY do operatora DISTINCT polega na tym, że wartości null traktowane są w ten sam sposób. Wszystkie pozycje z wartością null w kolumnie grupującej są zaliczane do tej samej kategorii.
Jest bardzo ważne, aby kolumna, względem której dokonujemy podziału na kategorie, znajdowała się w części deklaracyjnej wyrażenia SELECT. Zapytanie podane poniżej nie działa, ponieważ nie podano kolumny studio_id na liście deklaracji (select_list).
SELECT SUM(budget)
FROM Movies
GROUP BY studio_id
Klauzula GROUP BY działa z wszystkimi funkcjami agregującymi COUNT (), SUM (), AVG (), MIN (), MAK () zgodnie z podanymi tutaj zasadami.
Grupy i podgrupy
Klauzula ORDER BY umożliwia porządkowanie wyników zapytania wewnątrz kategorii, a klauzula GROUP BY tworzy grupy i podgrupy w przypadku, gdy wybrana jest więcej niż jedna kolumna. Listing 7.25 pokazuje przykład, w którym zliczono ilość filmów, które reżyser wykonał dla wybranego studia. Klauzula ORDER BY musi występować po GROUP BY, kolejność ta nie jest opcjonalna.
------------------------
Listing 7.25. Tworzenie podgrup
SELECT studio_id, director_id, COUNT(*)
FROM Movies
GROUP BY studio id, director id.
STUDIO_ID DIRECTOR_ID COUNT(*)
-----------------------
1 1 3
2 2 1
2 10 1
3 1 1
3 9 2
4 2 1
4 10 1
-----------------------
#158
Jak widać, wyniki są najpierw dzielone na kategorie według studia, a następnie według reżysera. Gdyby w klauzuli GROUP BY zmienić kolejność kategorii, najpierw według reżysera, a następnie według studia.
Wynik pokazywałby ile filmów wybrany reżyser wykonał dla poszczególnych studiów filmowych. Ilustruje to listing 7.26.
-----------------------
Listing 7.26. Kolejny przykład tworzenia podgrup
SELECT studio_id, director_id, COUNT(*)
FROM Movies
GROUP BY director id, studio id
STUDIO_ID DIRECTOR_ID COUNT(*
-----------------------
1 1 3
3 1 1
2 2 1
4 2 1
3 9 2
2 10 1
4 10 1
7 rows selected
-----------------------
Klauzule GROUP BY i ORDER BY
Klauzula ORDER BY stosowana łącznie z klauzulą GROUP BY sortuje wyniki zapytania. Wymaga to dodania za klauzulą GROUP BY klauzuli ORDER BY. Przykład pokazuje listing 7.27.
-----------------------
Listing 7.27. GROUP BY i ORDER BY w jednym zapytaniu
SELECT Studi0_id, COUNT(*)
FROM Movies GROUP BY studio_id
ORDER BY studio_id
STUDIO_ID COUNT(*)
-----------------------
1 3
2 2
3 3
3 2
4 2
-----------------------
Klauzula GROUP BY nie może zawierać nazwy kolumny nie uwzględnionej na liście SELECT, z kolei ORDER BY nie może wykorzystywać nazwy nie zawartej w klauzuli GROUP BY.
Próba sortowania względem nazwy kolumny nie zawartej w klauzuli GROUP BY spowoduje komunikat o błędzie.
Można umieszczać wiele kolumn w części ORDER BY jeśli tylko są one wymienione w klauzuli GROUP BY. Wyjaśnia to przykład na listingu 7.28.
#159
Listing 7.28. Użycie wielu nazw kolumn w klauzulach ORDER BY i GROUP BY
SELECT studio_id, director_id, COUNT(*)
FROM Movies
GROUP BY studio id, director_id
ORDER BY studio id, director id,
STUDIO_ID DIRECTOR_ID COUNT(*)
-----------------------
1 1 3
2 21
2 10 1
3 1 1
3 9 2
4 2 1
4 10 1
7 rows selected
-----------------------
Ustawienie kolumn w części ORDER BY nie musi być takie samo, jak w części GROUP BY. Można spowodować sortowanie najpierw względem kolumny drugiej, a następnie według pierwszej. Wyniki przedstawiono na listingu 7.29, a na listingu 7.30 te same wyniki w bardziej czytelnej postaci.
-----------------------
Listing 7.29. Sortowanie na podstawie drugorzędnego kryterium grupowania
SELECT studio_id, director_id, COUNT (*)
FROM Movies
GROUP BY studio_id, director_id
ORDER BY director id, studio id
STUDIO_ID DIRECTOR_ID COUNT(*)
-----------------------
1 1 3
3 1 1
2 2 1
4 2 1
3 9 2
2 10 1
4 10 1
7 rows selected
-----------------------
-----------------------
Listing 7.30. Inna wersja zapytania z listingu 7.29
SELECT studio_id, director_id, COUNT(*)
FROM Movies
GROUP BY studio^id, director_id
ORDER BY director id, studio id
STUDIO_ID DIRECTOR_ID COUNT(*)
-----------------------
1 1 3
1 3 1
2 2 1
2 4 1
9 3 2
10 2 1
10 2 1
7 rows selected
-----------------------
#160
Użycie wielu nazw kolumn w klauzuli GROUP BY nie powoduje konieczności użycia tej samej liczby kolumn w części ORDER BY. Na listingu 7.31 pokazano zapytanie, które sortuje wyniki tylko na podstawie głównego kryterium, a na listingu 7.32 tylko na podstawie drugiego kryterium grupowania.
-----------------------
Listing 7.31. Zapytanie grupujące z sortowaniem według głównego kryterium
SELECT studio_id, director_id, COUNT(*)
FROM Movies
GROUP BY studio_id, director_id
ORDER BY studio id
STUDIO_ID DIRECTOR_ID COUNT(*)
-----------------------
1 1 3
3 1 1
2 2 1
4 2 1
3 9 2
2 10 1
4 10 1
7 rows selected
-----------------------
-----------------------
Listing 7.32. Zapytanie grupujące z sortowaniem według drugiego kryterium
SELECT studio_id, director_id, COUNT(*)
FROM Movies
GROUP BY studio_id, director_id
ORDER BY director id
STUDIO_ID DIRECTOR_ID COUNT(*)
-----------------------
1 1 3
3 1 1
2 2 1
4 2 1
3 9 2
2 10 1
4 10 1
7 rows selected
-----------------------
Klauzula GROUP BY stosowana łącznie z WHERE
Klauzulę WHERE możemy użyć łącznie z GROUP BY, aby ograniczyć ilość wierszy zanim będą dzielone na grupy i podgrupy. Na przykład możemy wybrać tylko filmy przeznaczone do dystrybucji w określonym czasie, albo filmy tylko z wybranego studia.
Używając instrukcji WHERE łącznie z GROUP BY najpierw realizowane jest ograniczenie wynikające z kryteriów w klauzuli WHERE. Następnie wybrane rekordy są grupowane i powstaje ostateczny wynik zapytania.
Przykład na listingu 7.33 dla każdego studia zlicza filmy, które weszły na ekrany w latach 80-tych.
#161
-----------------------
Listing 7.33. Przykład zapytania z użyciem klauzul GROUP BY i WHERE
SELECT studio_id, COUNT(*}
FROM Movies
WHERE release_date BETWEEN '01-JAN-801 AND '31-DEC-891
GROUP BY studio id
STUDIO_ID COUNT(*)
-----------------------
1 1
3 1
-----------------------
Filtrowanie wyników zapytań z użyciem HAVING
Język SQL dostarcza jeszcze jedną metodę filtrowania wyników zapytania w połączeniu z klauzulą GROUP BY. Klauzula WHERE filtruje wyniki zapytania zanim są one grupowane, natomiast klauzula HAVING filtruje wyniki po wykonaniu grupowania. Wyrażenia zawarte w tej klauzuli wykonywane są na całych grupach, a nie na pojedynczych rekordach.
Na przykład, chcąc znaleźć studia, których budżet przekroczył 60 min $, stosując zapytanie z klauzulą WHERE nie otrzymamy poprawnej odpowiedzi. Używając HAYING możemy policzyć budżety dla wszystkich studiów i wybrać tylko te grupy, dla których suma jest większa niż 60 min $. Pokazuje to listing 7.34.
-----------------------
Listing 7.34. Filtrowanie grup z użyciem HAVING
SELECT studio_id, SUM(budget)
FROM Movies
GROUP BY studio_id HAYING SUM(budget) > 60
STUDIO_ID SUM(BUDGET)
-----------------------
1 85
3 65.1
3 87.3
-----------------------
Jak widać funkcje agregujące są użyte w dwóch miejscach zapytania, w klauzuli SELECT oraz HAVING. Częsty błąd polega na użyciu w klauzuli HAVING tylko nazwy kolumny, co automatycznie powoduje błąd. W tej części zapytania musi się znajdować takie samo wyrażenie, jak na liście klauzuli SELECT.
Nazwy kolumn, które nie pojawiają się na liście klauzuli SELECT, nie mogą być w ogóle użyte w klauzuli GROUP BY. Klauzula HAVING pojawia się przed ORDER BY, ale za częścią GROUP BY.
Poprawną kolejność wszystkich klauzul pokazano na listingu 7.35.
#162
-----------------------
Listing 7.35. Instrukcja z użyciem wielu klauzul łącznie
SELECT studio_id, SUM(budget)
FROM Movies GROUP BY studio_id
HAVING SUM(budget) > 60 ORDER BY studio_id
STUDIO_ID SUM(BUDGET)
-----------------------
1 85
3 65.1
4 87.3
-----------------------
Złożone wyrażenia w klauzuli HAVING
Nie ma żadnych przeciwwskazań, żeby używać złożonych wyrażeń w obrębie klauzuli HAVING. Jedyne ograniczenie polega na tym, że wszystkie wyrażenia w części HAVING muszą mieć swój odpowiednik na liście klauzuli SELECT. Z tego powodu zapytanie przedstawione na listingu 7.36 może nie działać prawidłowo w niektórych bazach danych (chociaż działa prawidłowo w bazie Oracle8).
-----------------------
Listing 7.36. Zapytanie wykorzystujące w części HA VING nazwę kolumny, której nie ma w części select_list zapytania
SELECT studio_id, SUM(budget)
FROM MoviesGROUP BY studio_id
HAYING SUM(budget) > 60
AND MAX(release datę) > '90-DEC-31'
STUDIO_ID SUM(BUDGET)
-----------------------
1 85
3 65.1
4 87.3
-----------------------
HAVING i WHERE
HAVING i WHERE mogą być stosowane w jednym zapytaniu. W niektórych wypadkach ma sens filtrowanie wyników zapytania tymi dwoma metodami równocześnie. Załóżmy, że chcemy znaleźć filmy, których reżyserzy zarobili średnio ponad 10 min $. Aby uniknąć wypaczenia wyników musimy pominąć filmy, które jeszcze nie weszły do dystrybucji i w związku z tym nie przyniosły jeszcze żadnego dochodu. Na listingu 7.37 pokazano rozwiązanie tego zadania, wykorzystując kombinację HTWING i WHERE.
-----------------------
Listing 7.37. HA VING i WHERE w jednym zapytaniu
SELECT director_id, AVG(gross)
FROM Movies
WHERE gross IS NOT NULL
GROUP BY director_id
HAYING AVG(gross) > 10
ORDER BY AYG(gross)
DIRECTOR_ID AVG(GROSS)
-----------------------
2 17.65
10 30
1 30.7
-----------------------
#163
Jak widać z listingu 7.37 kolejność elementów jest zgodna z tym, co pokazano
wcześniej: WHERE.... GROUP BY.....HAVING.....ORDER BY.....Wszystkie elementy
zapytania w znaczący sposób wpływają na jego ostateczny wynik. Gdyby nie odfiltrowano na początku (czyli w klauzuli WHERE) wartości null, to w części HAYING mogłoby to spowodować usunięcie niektórych grup zamiast pojedynczych rekordów. Z tego samego powodu nie dokonujemy filtrowania na podstawie funkcji agregującej w klauzuli WHERE.
W praktyce
Transact SQL wspierany przez produkty Microsoft SQL Server oraz Sybase Adap-tive Server dostarcza dodatkowych środków, z którymi możemy używać funkcji agregujących. Używając klauzuli COMPUTE BY można wprowadzić funkcje agregujące do zwykłych zapytań wybierających. Tworząc raporty przy użyciu języka SQL (w przeciwieństwie do metody, gdzie SQL służy tylko do wybierania odpowiednich danych, a raport przygotowywany jest w innym języku programowania), klauzula COMPUTE BY pomaga zaoszczędzić wiele pracy programisty. Na przykład listing 7.38 pokazuje zapytanie, które zwraca tytuł filmu (title), przychód (gross) i budżet (budget) dla każdego filmu w bazie danych, a ponadto średni przychód i średni budżet wyliczany na podstawie wszystkich danych.
----------------------------
Listing 7.38. Użycie klauzuli COMPUTE BY do stworzenia raportu
SELECT raovie_title, budget, gross
FROM Movies
COMPUTE AVG(budget), AVG(gross)
Movie_title budget gross
Minerał House 20 30
Prince Kong 3 52
The Code Warrior 10 18
Bili Durhara 10 16
Codependence Day 15 30
The Linux Files 22 18
SQL Strikes Back 5 10
The Programmer 50 45
Hard Code 11 30
The Rear Windows; 50 18
avg avg
==== ====
-; 26.200000 26.700000
(11 row(s) affected)
----------------------------
Jak widać, listing zawiera nie tylko wartości dla poszczególnych wierszy, ale również wartości zbiorcze wyliczone dla wszystkich filmów łącznie. Gdyby nie zastosowanie klauzuli COMPUTE BY, podobny efekt można by osiągnąć tworząc dwa zapytania. Jedno zapytanie wybierałoby pojedyncze pozycje, a drugie wyliczałoby wartości średnie. Można również połączyć zastosowanie zapytań wybierających z elementami COMPUTE BY i GROUP BY. Użycie COMPUTE BY wymaga ponadto użycia klauzuli ORDER BY, aby pogrupować wiersze zwrócone przez zapytanie. Dopiero wtedy można zastosować funkcje agregujące dla każdej grupy. Pokazuje to listing 7.39.
#164
----------------------------
Listing 7.39. Użycie COMPUTE BY do zastosowania funkcji agregujących dla grup
SELECT movie_title, studio_id, budget, gross
FROM Movies ORDER BY studio_id
COMPUTE SUM(budget), SUM(gross) BY studio_id
movie_title studio_id budget gross
-------------------------------------------------------------
Minerał House 1 20 30
Codependence Day 1 15 30
The Rear Windows 1 50 18
============
- - sum sum
85 78
Movie_title studio_id budget gross
-------------------------------------------------------------
Prince Kong 2 52 52
The Code Warrior 2 18 18
The Linux Files 2 22 18
============
sum sum
- - 35 88
Bili Durhara 3 10 16
SQL Strikes Back 3 5 10
The Programmer 3 50 45
========
- - sum sum
65 71
Movie_title studio_id budget gross
-------------------------------------------------------------
Hard Code 4 77 30
sum sum
==========
- - 77 30
(14 row(s) affected)
------------------------------
Klauzula ORDER BY jest wykorzystana do pogrupowania wierszy względem wartości z kolumny studio_id, a funkcje agregujące są zastosowane do każdej grupy. Wyniki tego zapytania można by uzyskać wykonując dwa zapytania. Jednak aż 8 zapytań byłoby potrzebnych, by uzyskać podobny układ danych z pominięciem klauzuli COMPUTE BY.
Wyszukiwarka
Podobne podstrony:
07 Przetwarzanie jednorodnych struktur danych (tablice)Ograniczenie wyników zapytania ( limit kurs mysql ) webmade orgPorcjowanie wyników zapytania ( porcjowanie wyników kurs mysql ) webmade orgZEM 07 08 Komentarz wynikow07 Charakteryzowanie budowy pojazdów samochodowych9 01 07 drzewa binarne02 07str 04 07 maruszewskiprzetworniki II opracowane07 GIMP od podstaw, cz 4 Przekształcenia07 Komórki abortowanych dzieci w PepsiPodstawy Cyfrowego Przetwarzania Sygnalów07 Badanie „Polacy o ADHD”CKE 07 Oryginalny arkusz maturalny PR Fizykawięcej podobnych podstron