SQL/MySQL Rafał Kern 1
SQL (Structured Query Language) Deklaratywny - opisujemy warunki, jakie musi spełniać wynik Służy do zarządzania danymi w relacyjnych bazach danych
Składnia/zapytania SQL DML (Data Manipulation Language): insert/update/delete SQL DDL (Data Definition Language): create/drop/alter SQL DCL (Data Control Language): grant/revoke/deny SQL DQL (Data Query Language): select 3
Baza danych customers PK id_customer invoices PK id_invoice invoice_items PK id_item first_name id_customer id_invoice last_name date_create product email paid price id_country currency vat_rate SELECT customers.id_customer, email FROM customers WHERE id_country = 5; 4
JOIN SELECT customers.id_customer, email, date_create FROM customers JOIN invoices ON customers.id_customer = invoices.id_customer WHERE id_country = 5; JOIN == INNER JOIN == CROSS JOIN Dla zwykłego JOIN optymalizator wylicza kolejność łączenia tabel. LEFT/RIGHT JOIN wymusza kolejność łączenia tabel i może być nieefektywne. 5
GROUP BY SELECT customers.id_customer, email, COUNT(id_invoice) AS total FROM customers JOIN invoices ON customers.id_customer = invoices.id_customer WHERE id_country = 5 GROUP BY invoices.id_customer; id_customer email total 1 foo@example.com bar@example.com 5 3 example@example.com 3 6
GROUP BY WITH ROLLUP SELECT customers.id_customer, email, COUNT(id_invoice) AS total FROM customers JOIN invoices ON customers.id_customer = invoices.id_customer WHERE id_country = 5 GROUP BY invoices.id_customer WITH ROLLUP; id_customer email total 1 foo@example.com bar@example.com 5 3 example@example.com 3 NULL foo@example.com 10 7
HAVING SELECT customers.id_customer, email, COUNT(id_invoice) AS total FROM customers JOIN invoices ON customers.id_customer = invoices.id_customer WHERE id_country = 5 GROUP BY invoices.id_customer HAVING total > 10; dotyczy wyników zagregowanych, dlatego pojawia się po GROUP BY może zawierać kolumny nieagregowane, jeśli ONLY_FULL_GROUP_BY jest wyłączone 8
Explain EXPLAIN SELECT customers.id_customer, email, COUNT(id_invoice) AS total FROM customers JOIN invoices ON customers.id_customer = invoices.id_customer WHERE id_country = 5 GROUP BY invoices.id_customer; id select_t ype table type possible _keys key key_len ref rows Extra 1 SIMPLE invoices ALL NULL NULL NULL NULL 4598 Using temporary; Using filesort SIMPLE customers eq_ref PRIMARY PRIMARY 4 invoices. id_custom er 1 Using where 9
Indeksy http://pracownianatury.pl 10
Indeksy szybsze wyszukiwanie szybsze złączenia szybsze sortowanie MySQL używa tych indeksów, które determinują najmniejszą liczbę wierszy przebudowa indeksów - spowolniony zapis 11
Indeksy złożone INDEX name (last_name,first_name,email) Możliwości użycia indeksów: (last_name) (last_name,first_name) (last_name,first_name, email) Indeks nie zostanie użyty dla: SELECT email FROM customers WHERE first_name = 'Marek'; Indeks zostanie częściowo użyty dla: SELECT email FROM customers WHERE last_name= 'Nowak' OR first_name = 'Jacek'; 1
MySQL - fulltext char, varchar, text inverted index - wykorzystuje listę wyrazów oraz listę dokumentów, w których dany wyraz występuje zakładanie indeksu pełnotekstowego na dużych tabelach może być bardzo czasochłonne Sphinx, Xapian, Lucene 13
Cardinality & Selectivity Cardinality (liczność) - liczba wszystkich wierszy Selectivity (selektywność) - liczba pasujących wierszy w stosunku do wszystkich w tabeli (estymowana, nie wyliczana) Optymalizator MySQL-a z reguły nie stosuje indeksów przy selektywności powyżej 0% wczytywanie indeksów może być kosztowne 14
Explain raz jeszcze EXPLAIN SELECT customers.id_customer, email, COUNT(id_invoice) AS total FROM customers JOIN invoices ON customers.id_customer = invoices.id_customer WHERE id_country = 5 GROUP BY invoices.id_customer; id select_t ype table type possible _keys key key_len ref rows Extra 1 SIMPLE invoices ALL NULL NULL NULL NULL 4598 Using temporary; Using filesort SIMPLE customers eq_ref PRIMARY PRIMARY 4 invoices. id_custom er 1 Using where 15
Po dodaniu indeksów... ALTER TABLE `customers` ADD INDEX(`id_country`); ALTER TABLE `invoices` ADD INDEX(`id_customer`); id select_t ype table type possible_k key eys key_ len ref row s Extra 1 SIMPLE customers ref PRIMARY, id_country id_country 4 const 1156 Using where SIMPLE invoices ref id_customer id_customer 4 customers. id_customer Using index 16
Indeks nie zostanie użyty... różne zestawy znaków (character set) np. latin1, utf8 złożona konwersja (CAST, CONVERT) duża liczba wierszy (duża selektywność) 17
Transakcje http://www.barnhartlawplc.com/practice-areas/legal-practice-areas/financial-transactions 18
Transakcje (ACID) Atomicity - operacja jest wykonana w całości albo wcale Consistency - po wykonaniu operacji dane będą spełniały kryteria integralności Isolation- dwie współbieżne transakcje nie widzą zmian wprowadzonych przez siebie (z reguły tak jest, zależy od poziomu izolacji) Durability- po ponownym uruchomieniu wyniki transakcji są dostępne 19
Transakcje START TRANSACTION; UPDATE invoices SET paid = 1 WHERE id_invoice = 1543; UPDATE invoices SET paid = 0 WHERE id_invoice = 1541; COMMIT/ROLLBACK; 0
MyISAM vs InnoDB MyIsam bez transakcji bez kluczy obcych nie spełnia ACID lockowanie na poziomie tabeli InnoDB wspiera transakcje klucze obce spełnia ACID lockowanie na poziomie wiersza 1
Klucze obce http://archiwum.allegro.pl/oferta/mannesmann
Powiązania 3
Klucze obce pozwalają tworzyć powiązania między tabelami ten sam typ danych rozmiar i znak (signed/unsigned) stringi: długość, character set i collation unikać kluczy obcych na stringach! 4
Wywołania kaskadowe Gdy modyfikujesz lub usuwasz wiersz z tabeli głównej to: CASCADE - aktualizuj lub usuń powiązane z nią wiersze z tabeli potomnej, SET NULL - ustaw wartości powiązanych kolumn na null (kolumna nie może być zdefiniowana jako NOT NULL) NO ACTION/RESTRICT - brak akcji SET DEFAULT - MySQL rozpoznaje klauzulę ale NBD i InnoDB ignorują je i traktują jak RESTRICT 5
Wywołania kaskadowe DELETE FROM customers WHERE id_customer = ; customers invoices id_customer last_name id_invoice id_customer 1 Kowalski 1 Nowak 3 Zięba 3 3 4 Bąk 4 4 6
Normalizacja brak zduplikowanych danych w bazie kolumny w tabeli są zależne tylko od klucza głównego łatwa modyfikacja danych w tabeli mogą znajdować się klucze obce 7
Denormalizacja zduplikowanie wybranych danych trudniejsze zarządzanie danymi łatwiejsze wyszukiwanie mniej joinów ilość zapisów vs. ilość odczytów uzasadnione zwłaszcza, gdy istnieje obawa dezaktualizacji danych. 8
Denormalizacja 9
http://biernbaum.com/1-most-common-mistakes/ 30
Enum/Set Sortowane wg kolejności podanej w definicji ENUM( 3, 1, ), ENUM ( X, Z, Y ) Modyfikowane za pomocą ALTER - co z dużymi tabelami? Literówki podczas porównywania stringów Podczas wstawiania nieprawidłowej wartości zostanie ona podmieniona na. Jak pobrać listę opcji do selecta? 31
NOT IN SELECT c.id_contract, c.expiration_date FROM contracts AS c WHERE c.id_contract NOT IN(SELECT i.id_contract FROM invoices AS i); Query time: 36,189s lepiej tak: SELECT c.id_contract, c.expiration_date FROM contracts AS c WHERE NOT EXISTS (SELECT id_contract FROM invoices AS i WHERE i.id_contract = c.id_contract); Query time:,755s lub tak: SELECT c.id_contract, c.expiration_date FROM contracts AS c FROM contracts AS c LEFT JOIN invoices AS i ON i.id_contract = c.id_contract WHERE i.id_invoice IS NULL; Query time:,68s 3
Signed/unsigned SIGNED UNSIGNED ROZMIAR MIN MAX MIN MAX TINYINT -18 17 0 55 1 SMALLINT -3768 3767 0 65535 MEDIUMINT -8388608 8388607 0 1677715 3 INT -147483648 147483647 0 49496795 4 czy jest potrzebny int, może wystarczy smallint lub tinyint? 33
Join SELECT c.id_customer, i.id_invoice, ii.product FROM customers AS c LEFT JOIN invoices AS i LEFT JOIN invoice_items AS ii ON (i.id_invoice=ii.id_invoice) WHERE i.id_currency=1; Problem: całkowite złączenie tabel customers i invoices zanim zacznie się filtrowanie wierszy. Rozwiązanie: SELECT c.id_customer, i.id_invoice, ii.product FROM invoices AS i LEFT JOIN customers AS c LEFT JOIN invoice_items AS ii ON (i.id_invoice=ii.id_invoice) WHERE i.id_currency=1; 34
SELECT c.id_customer, i.id_invoice, ii.product FROM customers AS c LEFT JOIN invoices AS i LEFT JOIN invoice_items AS ii ON (i.id_invoice=ii.id_invoice) WHERE i.id_currency=1; invoices customers id_customer email 1 test@example.org 3 id_invoice id_customer id_currency 1 5 10 3 11 3 4 1 4 1 5 1 6 1 test@example.org test@example.org 4 test@example.org 5 test@example.org 35
SELECT c.id_customer, i.id_invoice, ii.product FROM invoices AS i LEFT JOIN customers AS c LEFT JOIN invoice_items AS ii ON (i.id_invoice=ii.id_invoice) WHERE i.id_currency=1; invoices customers id_invoice id_customer id_currency id_customer email 1 5 10 1 test@example.org 3 11 test@example.org 3 4 1 3 test@example.org 4 1 4 test@example.org 5 1 5 test@example.org 6 1 36
Wielokrotny JOIN Lepiej podzielić na mniejsze zapytania, wrzucić cache i łączyć na poziomie aplikacji 37
Float `price` float(10,5) unsigned NOT NULL, INSERT INTO `invoice_items` ('id_item`, `id_invoice`, `id_product`, `price`, `vat_rate`) VALUES (NULL, '606', '', '5000,4889', '3'); 38
Float - rozwiązanie `price` decimal(10,5) unsigned NOT NULL, INSERT INTO `invoice_items` ('id_item`, `id_invoice`, `id_product`, `price`, `vat_rate`) VALUES (NULL, '606', '', '5000,4889', '3'); 39
Typy kolumn 40
Typy kolumn zamiast typu string/varchar lepiej użyć id typu labelowanie dla poszczególnych id w osobnej tabeli słownikowej like jest relatywnie wolny użycie like %xxx% nie pozwala wykorzystać indeksów 41
Typy kolumn - rozwiązanie 4
Explain - podsumowanie minimalizować iloczyn liczby sprawdzanych wierszy unikać using temporary unikać ALL w joinach (pełny przegląd tabeli) łączyć przez eq_ref (jest najlepsze) 43
Dziękuję za uwagę Rafał Kern rafal.kern@rst.com.pl 44