plik


#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 org
Porcjowanie wynikĂłw zapytania ( porcjowanie wynikĂłw kurs mysql ) webmade org
ZEM 07 08 Komentarz wynikow
07 Charakteryzowanie budowy pojazdĂłw samochodowych
9 01 07 drzewa binarne
02 07
str 04 07 maruszewski
przetworniki II opracowane
07 GIMP od podstaw, cz 4 Przekształcenia
07 KomĂłrki abortowanych dzieci w Pepsi
Podstawy Cyfrowego Przetwarzania SygnalĂłw
07 Badanie „Polacy o ADHD”
CKE 07 Oryginalny arkusz maturalny PR Fizyka

więcej podobnych podstron