Bazy danych 7. SQL dalsze możliwości. Grupowanie. P. F. Góra http://th-www.if.uj.edu.pl/zfs/gora/ semestr letni 2005/06
MySQL i programowanie wsadowe C:\wyklady\bazy> mysql < nazwa pliku C:\wyklady\bazy> mysql -hhostname -uusername -p < nazwa pliku Plik musi być dostępny dla klienta. Pliki wsadowe można też wołać z wnętrza klienta: mysql> source nazwa pliku; Uwaga: Jeśli któreś polecenie w pliku wsadowym spowoduje bład, dalsze polecenia nie sa wykonywane. 7. SQL dalsze możliwości. Grupowanie. 2
Tworzenie tabeli wraz z kopiowaniem danych mysql> USE moistudenci; Database changed mysql> DESCRIBE Studenci; +------------+----------------------+------+-----+---------+----------------+ Field Type Null Key Default Extra +------------+----------------------+------+-----+---------+----------------+ NrStudenta smallint(5) unsigned NO PRI auto_increment Imie varchar(20) NO Nazwisko varchar(20) NO Uwagi varchar(30) YES Grupa char(2) YES +------------+----------------------+------+-----+---------+----------------+ 5 rows in set (0.21 sec) 7. SQL dalsze możliwości. Grupowanie. 3
mysql> CREATE TABLE Studenci2 -> (NrStudenta INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, -> Imie VARCHAR(20) NOT NULL, Nazwisko VARCHAR(20) NOT NULL, -> Uwagi VARCHAR(30), Grupa CHAR(2)) -> -- TU kończy się definicja tabeli! -> SELECT * FROM STUDENCI; Query OK, 9 rows affected (1.08 sec) Records: 9 Duplicates: 0 Warnings: 0 mysql> DESCRIBE Studenci2; +------------+------------------+------+-----+---------+----------------+ Field Type Null Key Default Extra +------------+------------------+------+-----+---------+----------------+ NrStudenta int(10) unsigned NO PRI auto_increment Imie varchar(20) NO Nazwisko varchar(20) NO Uwagi varchar(30) YES Grupa char(2) YES +------------+------------------+------+-----+---------+----------------+ 5 rows in set (0.01 sec) 7. SQL dalsze możliwości. Grupowanie. 4
mysql> SELECT * FROM Studenci2; +------------+----------+--------------+-------+-------+ NrStudenta Imie Nazwisko Uwagi Grupa +------------+----------+--------------+-------+-------+ 1 Mariusz Lewandowski xxxx xy 2 Jakub Stefanowski xy 3 Marek Giebułtowski xy 4 Barbara Jeziorek xy 6 Wojciech Wąchal xy 72 Jakub Janoszek xy 73 Maciej Gowin xy 74 Tadeusz Drozda xy 75 Marek Kolejny xy +------------+----------+--------------+-------+-------+ 9 rows in set (0.03 sec) 7. SQL dalsze możliwości. Grupowanie. 5
Zmienne tymczasowe SQL pozwala na definiowanie zmiennych tymczasowych nazwa zmiennej tymczasowej zawsze zaczyna się od @. Zmienna istnieje dopóty, dopóki nie zostanie zakończone połaczenie z serwerem. Procesy klienckie nie widza zmiennych zdefiniowanych przez inne procesy. mysql> SET @a=5; Query OK, 0 rows affected (0.01 sec) mysql> SELECT @a-2; +------+ @a-2 +------+ 3 +------+ 1 row in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 6
Zmiennej można przypisać wartość, która jest wynikiem zapytania: mysql> SET @b=(select NrStudenta FROM Studenci2 WHERE Imie= Tadeusz ); Query OK, 0 rows affected (0.89 sec) mysql> SELECT @b, @a+@b; +------+-------+ @b @a+@b +------+-------+ 74 79 +------+-------+ 1 row in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 7
Aliasy W zapytaniu SELECT możemy nadawać kolumnom inne nazwy niż te, które występuja w definicji tabeli. Nosza one nazwę aliasów. mysql> SELECT NrStudenta AS ToJestNumer, Imie FROM Studenci2; +-------------+----------+ ToJestNumer Imie +-------------+----------+ 1 Mariusz 2 Jakub 3 Marek 4 Barbara 6 Wojciech 72 Jakub 73 Maciej 74 Tadeusz 75 Marek 65536 X +-------------+----------+ 10 rows in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 8
Niekiedy wygodnie jest użyć aliasu w dalszej części zapytania, ale nie można go użyć w kaluzuli WHERE mysql> SELECT NrStudenta AS ToJestNumer, Imie FROM Studenci2 -> WHERE ToJestNumer > 10; ERROR 1054 (42S22): Unknown column ToJestNumer in where clause Można natomiast użyć aliasu w klauzuli HAVING: mysql> SELECT NrStudenta AS ToJestNumer, Imie as Pierwsze imię FROM Studenci2 -> HAVING ToJestNumer > 10; +-------------+---------------+ ToJestNumer Pierwsze imię +-------------+---------------+ 72 Jakub 73 Maciej 74 Tadeusz 75 Marek 65536 X +-------------+---------------+ 5 rows in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 9
Jest to szczególnie przydatne, gdy w warunku występuje wartość obliczana na podstawie więcej niż jednej kolumny: mysql> SELECT CONCAT(Imie,,Nazwisko) AS Imie_Nazwisko FROM Studenci2 -> HAVING Imie_Nazwisko LIKE "%r%z%"; +---------------------+ Imie_Nazwisko +---------------------+ Mariusz Lewandowski Barbara Jeziorek Tadeusz Drozda +---------------------+ 3 rows in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 10
Projekt Kancelaria (1) Kancelaria adwokacka gromadzi informacje o czynnościach wykonanych na rzecz klientów. Każda czynność ma swój unikalny numer, datę, numer klienta, kod wykonujacego ja pracownika, czas trwania czynności. Klient jest obciażany proporcjonalnie do czasu, jaki poświęcili jego sprawom pracownicy kancelarii. 7. SQL dalsze możliwości. Grupowanie. 11
Zawartość pliku wsadowego: -- To jest komentarz CREATE DATABASE kancelaria CHARACTER SET cp1250; USE kancelaria; -- Tworzymy tabelę CREATE TABLE czynnosci (Nr INT UNSIGNED NOT NULL PRIMARY KEY AUTO INCREMENT, Data DATE NOT NULL, KlientId SMALLINT UNSIGNED NOT NULL, Pracownik CHAR(3) NOT NULL, Czas SMALLINT UNSIGNED NOT NULL DEFAULT 15, Opis VARCHAR(24)); 7. SQL dalsze możliwości. Grupowanie. 12
-- Wprowadzamy dane przygotowane w pliku SET CHARACTER SET cp1250; LOAD DATA LOCAL INFILE c://wyklady//bazy//czyn.txt INTO TABLE czynnosci; Kwalifikator LOCAL oznacza, że plik będzie poszukiwany na komputerze, z którego zostal wywołany klient. Dodajmy jeszcze jedna krotkę: mysql> SET CHARACTER SET cp1250; mysql> INSERT INTO czynnosci -> (Date,KlientId,Pracownik,Opis) -> VALUES -> ( 2006-04-20,8, ZOF, ĄĆĘŁŃÓŚŹŻąćęłńóśźż ); 7. SQL dalsze możliwości. Grupowanie. 13
mysql> SELECT * FROM czynnosci -> ORDER BY Nr DESC LIMIT 12; +-----+------------+----------+-----------+------+--------------------------+ Nr Data KlientId Pracownik Czas Opis +-----+------------+----------+-----------+------+--------------------------+ 121 2006-04-20 8 ZOF 15 ĄĆĘŁŃÓŚŹŻąćęłńóśźż 120 2005-04-05 5 EWA 15 Kwerenda biblioteczna 119 2005-04-16 1 EWA 15 Konsultacje specjalisty 118 2005-02-21 3 PIO 30 Rozmowa z klientem 117 2005-03-21 4 KRZ 15 Kwerenda biblioteczna 116 2005-01-28 6 KRZ 30 Rozmowa z klientem 115 2005-04-23 7 JOZ 30 Czytanie akt 114 2005-02-16 2 KRZ 15 Rozmowa z klientem 113 2005-01-10 7 ZOF 30 Czytanie akt 112 2005-01-13 6 JOZ 15 Czytanie akt 111 2005-01-29 6 BAR 45 Czytanie akt 110 2005-03-14 3 KRZ 90 Udział w rozprawie +-----+------------+----------+-----------+------+--------------------------+ 12 rows in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 14
Zliczanie wierszy Ile wierszy wprowadziliśmy? mysql> SELECT COUNT(*) FROM czynnosci; +----------+ COUNT(*) +----------+ 121 +----------+ 1 row in set (0.05 sec) mysql> SELECT COUNT(*) AS IleWierszy FROM czynnosci; +------------+ IleWierszy +------------+ 121 +------------+ 1 row in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 15
Policzmy kolumnę Pracownik. mysql> SELECT COUNT(Pracownik) FROM Czynnosci; +------------------+ COUNT(Pracownik) +------------------+ 121 +------------------+ 1 row in set (0.00 sec) mysql> SELECT COUNT(DISTINCT Pracownik) AS IluPracownikow -> FROM czynnosci; +----------------+ IluPracownikow +----------------+ 8 +----------------+ 1 row in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 16
Funkcje agregujace COUNT(nazwa kolumny) SUM(nazwa kolumny numerycznej) MIN(nazwa kolumny numerycznej) MAX(nazwa kolumny numerycznej) AVG(nazwa kolumny numerycznej) VAR(nazwa kolumny numerycznej) STD(nazwa kolumny numerycznej) zlicza sumuje najmniejszy największy oblicza średnia oblicza wariancję oblicza odchylenie standardowe Bardzo często wykorzystywane przy grupowaniu. 7. SQL dalsze możliwości. Grupowanie. 17
Grupowanie Bardzo ważna umiejętność: Dane w tabeli grupujemy według pewnej kategorii, na przykład według pewnej kolumny. Przykład: Ile czasu poświęciliśmy poszczególnym klientom? mysql> SELECT KlientID, SUM(Czas) AS ChargedTime -> FROM czynnosci -> GROUP BY KlientId; +----------+-------------+ KlientID ChargedTime +----------+-------------+ 1 330 2 405 3 465 4 915 5 660 6 720 7 585 8 840 9 480 +----------+-------------+ 9 rows in set (0.01 sec) 7. SQL dalsze możliwości. Grupowanie. 18
Ile czasu poświęciliśmy poszczególnym klientom? Uwzględniamy tylko klientów, którym poświęciliśmy co najmniej 500 minut. mysql> SELECT KlientID, SUM(Czas) AS ChargedTime -> FROM czynnosci -> GROUP BY KlientId -> HAVING ChargedTime >= 500; +----------+-------------+ KlientID ChargedTime +----------+-------------+ 4 915 5 660 6 720 7 585 8 840 +----------+-------------+ 5 rows in set (0.06 sec) 7. SQL dalsze możliwości. Grupowanie. 19
Ile czasu poświęciliśmy poszczególnym klientom w poszczególnych miesia- cach? mysql> SELECT KlientID, MONTH(Data) AS Miesiac, SUM(Czas) AS ChargedTime -> FROM czynnosci -> GROUP BY KlientId, Miesiac; +----------+---------+-------------+ KlientID Miesiac ChargedTime +----------+---------+-------------+ 1 1 90 1 2 180 1 3 45 1 4 15... 9 1 135 9 2 15 9 3 180 9 4 150 +----------+---------+-------------+ 35 rows in set (0.95 sec) 7. SQL dalsze możliwości. Grupowanie. 20
To samo z podsumowaniem w (pod)grupach: mysql> SELECT KlientID, MONTH(Data) AS Miesiac, SUM(Czas) AS ChargedTime -> FROM czynnosci -> GROUP BY KlientId, Miesiac -> WITH ROLLUP; +----------+---------+-------------+ KlientID Miesiac ChargedTime +----------+---------+-------------+ 1 1 90 1 2 180 1 3 45 1 4 15 1 NULL 330... 9 1 135 9 2 15 9 3 180 9 4 150 9 NULL 480 NULL NULL 5400 +----------+---------+-------------+ 45 rows in set (0.06 sec) 7. SQL dalsze możliwości. Grupowanie. 21
Uwaga! Przy grupowaniu nie można mieszać wartości zagregowanych z nazwami kolumn, po których nie grupujemy: Zapytanie mysql> SELECT Month(Data) AS Miesiąc, SUM(Czas) FROM czynnosci -> GROUP BY Miesiąc; +---------+-----------+ Miesiąc SUM(Czas) +---------+-----------+ 1 1545 2 1530 3 1065 4 1245 +---------+-----------+ 4 rows in set (0.00 sec) jest w porzadku. Występuje wartość zagregowana i niezagregowane wartości z kolumny, po której grupujemy. 7. SQL dalsze możliwości. Grupowanie. 22
Jednak zapytanie mysql> SELECT Month(Data) AS Miesiąc, SUM(Czas) FROM czynnosci; ERROR 1140 (42000): Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause nie działa, gdyż występuje tak wartość zagregowana (SUM(Czas)), jak i nazwa kolumny, po której nie grupujemy (Month(Data) AS Miesiąc). 7. SQL dalsze możliwości. Grupowanie. 23
Klasyczny przykład na grupowanie miesięczna sprzedaż w sklepach mysql> DESCRIBE sales; +----------+--------------+------+-----+---------+-------+ Field Type Null Key Default Extra +----------+--------------+------+-----+---------+-------+ Nazwa varchar(8) NO PRI Data date NO PRI Sprzedaz mediumint(9) NO +----------+--------------+------+-----+---------+-------+ 3 rows in set (0.14 sec) 7. SQL dalsze możliwości. Grupowanie. 24
mysql> SELECT * FROM sales; +----------+------------+----------+ Nazwa Data Sprzedaz +----------+------------+----------+ Katowice 2007-01-31 1576 Katowice 2007-02-28 1912 Katowice 2007-03-31 2306 Katowice 2007-04-30 2106 Konin 2007-02-28 502 Konin 2007-03-31 1181 Konin 2007-04-30 1995 Warszawa 2007-01-31 1385 Warszawa 2007-02-28 1892 Warszawa 2007-03-31 2741 Warszawa 2007-04-30 2790 +----------+------------+----------+ 11 rows in set (0.01 sec) 7. SQL dalsze możliwości. Grupowanie. 25
Grupowanie po sklepach mysql> SELECT Nazwa, SUM(Sprzedaz) AS Total sales FROM sales GROUP BY Nazwa WITH ROLLUP; +----------+-------------+ Nazwa Total sales +----------+-------------+ Katowice 7900 Konin 3678 Warszawa 8808 20386 +----------+-------------+ 4 rows in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 26
Grupowanie po nazwach miesięcy mysql> SELECT MonthName(Data) AS Miesiac, SUM(Sprzedaz) AS Total sales FROM sales GROUP BY Miesiac WITH ROLLUP; +----------+-------------+ Miesiac Total sales +----------+-------------+ April 6891 February 4306 January 2961 March 6228 20386 +----------+-------------+ 5 rows in set (0.01 sec) 7. SQL dalsze możliwości. Grupowanie. 27
Projekt Średnia ruchoma W tabeli mamy zgromadzone dane numeryczne, indeksowane czasem (szereg czasowy) na przykład kolejne kursy akcji, kolejne odczyty temperatury, dane o sprzedaży z kolejnych miesięcy itp. Należy obliczyć średnia ruchoma y i = 1 p + 1 i+p/2 x j j=i p/2 Obliczenie tego po stronie aplikacji jest proste. Jak to zrobić w SQL? 7. SQL dalsze możliwości. Grupowanie. 28
mysql> CREATE DATABASE srednia; Query OK, 1 row affected (0.01 sec) mysql> USE srednia; Database changed mysql> CREATE TABLE W -> (Nr SMALLINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, -> Wartosc FLOAT NOT NULL); Query OK, 0 rows affected (0.12 sec) mysql> INSERT INTO W (Wartosc) VALUES -> (1),(5),(2.2),(0.8),(4),(3.1),(3.9),(2.7),(3.1),(0.9); Query OK, 10 rows affected (0.07 sec) Records: 10 Duplicates: 0 Warnings: 0 7. SQL dalsze możliwości. Grupowanie. 29
mysql> SELECT * FROM W; +----+---------+ Nr Wartosc +----+---------+ 1 1 2 5 3 2.2 4 0.8 5 4 6 3.1 7 3.9 8 2.7 9 3.1 10 0.9 +----+---------+ 10 rows in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 30
Problem rozwiażemy za pomoca samozłaczenia (self join). mysql> SET @p = 3; mysql> SELECT x.wartosc, AVG(y.wartosc) AS Srednia -> FROM W AS x, W AS y -> WHERE y.nr >= x.nr - @p/2 AND y.nr <= x.nr + @p/2 -> GROUP BY x.nr; Dwukrotnie odwolujemy się do tej samej tabeli, a więc musi ona występować pod różnymi aliasami. Wystapienie x służy do identyfikowania wierszy, więc występuje także w klauzuli GROUP BY. Z odpowiednich wystapień y obliczana jest średnia. 7. SQL dalsze możliwości. Grupowanie. 31
+---------+-----------------+ wartosc Srednia +---------+-----------------+ 1 3 5 2.7333333492279 2.2 2.6666666865349 0.8 2.3333333532015 4 2.6333333055178 3.1 3.6666666666667 3.9 3.2333333492279 2.7 3.2333333492279 3.1 2.2333333094915 0.9 1.9999999403954 +---------+-----------------+ 10 rows in set (0.94 sec) Skrajne wartości sa źle obsługiwane. 7. SQL dalsze możliwości. Grupowanie. 32
mysql> SET @p = 3; mysql> SET @gorny = (SELECT MAX(Nr) FROM W) + 1; mysql> SET @dolny = (SELECT MIN(Nr) FROM W) - 1; mysql> SELECT x.wartosc, AVG(y.wartosc) AS Srednia -> FROM W as x, W as y -> WHERE y.nr >= x.nr - @p/2 AND y.nr <= x.nr + @p/2 -> AND x.nr - @p/2 >= @dolny AND x.nr + @p/2 <= @gorny -> GROUP BY x.nr; +---------+-----------------+ wartosc Srednia +---------+-----------------+ 5 2.7333333492279 2.2 2.6666666865349 0.8 2.3333333532015 4 2.6333333055178 3.1 3.6666666666667 3.9 3.2333333492279 2.7 3.2333333492279 3.1 2.2333333094915 +---------+-----------------+ 8 rows in set (0.01 sec) 7. SQL dalsze możliwości. Grupowanie. 33
Projekt Suma narastajaca mysql> SELECT x.nr, x.wartosc, FORMAT(SUM(y.wartosc),1) AS RunningTotal -> FROM W AS x, W AS y -> WHERE y.nr <= x.nr -> GROUP BY x.nr; +----+---------+--------------+ Nr wartosc RunningTotal +----+---------+--------------+ 1 1 1.0 2 5 6.0 3 2.2 8.2 4 0.8 9.0 5 4 13.0 6 3.1 16.1 7 3.9 20.0 8 2.7 22.7 9 3.1 25.8 10 0.9 26.7 +----+---------+--------------+ 10 rows in set (0.00 sec) 7. SQL dalsze możliwości. Grupowanie. 34