Instructiunea SELECT
Sistemele de baze de date utilizeaza limbaje de interogare pentru a permite gasirea si extragerea datelor din tabelele bazelor de date. Operatia prin care se obtin aceste date poarta numele de interogare. Implementarea interogarilor se realizeaza prin intermediul instructiunii SELECT, pentru care poate fi utilizata urmatoarea sintaxa de baza.
SELECT [ALL | DISTINCT] expresie_selectie [, expresie_selectie ...] FROM referinte_tabele [WHERE conditie_selectie] [GROUP BY {nume_coloana | expresie | pozitie_coloana} [ASC | DESC], ...] [HAVING conditie_grupare] [ORDER BY {nume_coloana | expresie | pozitie_coloana} [ASC | DESC], ...] [LIMIT {[pozitie,] numar_inregistrari | numar_inregistrari OFFSET pozitie}]
Clauzele instructiunii SELECT trebuie introduse in ordinea specificata in sintaxa de mai sus. De exemplu, clauza HAVING trebuie plasata dupa GROUP BY si inaintea lui ORDER BY.
Expresiile de selectie implementeaza lista de selectie si pot contine referinte la coloane sau functii aplicate coloanelor unui tabel. Un caz special il reprezinta caracterul *, care semnifica includerea in rezultat a tuturor coloanelor dintr-un tabel. Un tabel din baza de date activa poate fi referit in mod direct prin numele lui, nume_tabel, sau prin nume_baza_date.nume_tabel, daca baza de date este specificata explicit.
mysql> USE sakila; mysql> SELECT * -> FROM actor -> LIMIT 5; +----------+------------+--------------+---------------------+ | actor_id | first_name | last_name | last_update | +----------+------------+--------------+---------------------+ | 1 | PENELOPE | GUINESS | 2006-02-15 04:34:33 | | 2 | NICK | WAHLBERG | 2006-02-15 04:34:33 | | 3 | ED | CHASE | 2006-02-15 04:34:33 | | 4 | JENNIFER | DAVIS | 2006-02-15 04:34:33 | | 5 | JOHNNY | LOLLOBRIGIDA | 2006-02-15 04:34:33 | +----------+------------+--------------+---------------------+
mysql> SELECT actor_id, lower(first_name) AS first_name, last_name, last_update -> FROM sakila.actor -> LIMIT 5; +----------+------------+--------------+---------------------+ | actor_id | first_name | last_name | last_update | +----------+------------+--------------+---------------------+ | 1 | penelope | GUINESS | 2006-02-15 04:34:33 | | 2 | nick | WAHLBERG | 2006-02-15 04:34:33 | | 3 | ed | CHASE | 2006-02-15 04:34:33 | | 4 | jennifer | DAVIS | 2006-02-15 04:34:33 | | 5 | johnny | LOLLOBRIGIDA | 2006-02-15 04:34:33 | +----------+------------+--------------+---------------------+
ALL vs DISTINCT
Clauza ALL permite colectarea tuturor inregistrarilor, chiar si a celor care se repeta (inregistrari duplicat). Daca nu se precizeaza nici una din clauzele ALL sau DISTINCT intr-o instructiune SELECT, atunci implicit se utilizeaza ALL.
mysql> SELECT ALL first_name -> FROM sakila.actor -> WHERE first_name RLIKE '^b'; +------------+ | first_name | +------------+ | BETTE | | BOB | | BURT | | BURT | | BEN | | BEN | | BURT | | BELA | +------------+
In exemplul precedent avem in vedere identificarea prenumelor actorilor care incep cu litera b. Se poate observa ca sunt mai multi actori ce detin aceiasi valoare pentru campul first_name (au acelasi prenume). Clauza DISTINCT permite eliminarea inregistrarilor duplicate din rezultat.
mysql> SELECT DISTINCT first_name -> FROM sakila.actor -> WHERE first_name RLIKE '^b'; +------------+ | first_name | +------------+ | BETTE | | BOB | | BURT | | BEN | | BELA | +------------+
Clauza WHERE
O prima modalitate de filtrare a datelor de la nivelul unei interogari o reprezinta clauza WHERE. Aceasta specifica o conditie pe care inregistrarile trebuie sa o indeplineasca pentru a fi pastrate in rezultat. Daca dorim sa colectam inregistrarile pentru care valorile de la nivelul coloanei last_name incep cu jo, putem utiliza in clauza de filtrare expresia last_name LIKE ‘jo%’.
mysql> SELECT * -> FROM sakila.actor -> WHERE last_name LIKE 'jo%'; +----------+------------+-----------+---------------------+ | actor_id | first_name | last_name | last_update | +----------+------------+-----------+---------------------+ | 8 | MATTHEW | JOHANSSON | 2006-02-15 04:34:33 | | 64 | RAY | JOHANSSON | 2006-02-15 04:34:33 | | 146 | ALBERT | JOHANSSON | 2006-02-15 04:34:33 | | 82 | WOODY | JOLIE | 2006-02-15 04:34:33 | | 43 | KIRK | JOVOVICH | 2006-02-15 04:34:33 | +----------+------------+-----------+---------------------+
Inregistrarile de la nivelul rezultatului precedent pot fi obtinute si prin implementarea unei expresii de filtrare care contine o expresie regulata (descrisa prin operatorul RLIKE).
mysql> SELECT * -> FROM sakila.actor -> WHERE last_name RLIKE '^(jo)'; +----------+------------+-----------+---------------------+ | actor_id | first_name | last_name | last_update | +----------+------------+-----------+---------------------+ | 8 | MATTHEW | JOHANSSON | 2006-02-15 04:34:33 | | 43 | KIRK | JOVOVICH | 2006-02-15 04:34:33 | | 64 | RAY | JOHANSSON | 2006-02-15 04:34:33 | | 82 | WOODY | JOLIE | 2006-02-15 04:34:33 | | 146 | ALBERT | JOHANSSON | 2006-02-15 04:34:33 | +----------+------------+-----------+---------------------+
Daca nu este precizata o conditie de selectie, sunt selectate toate inregistrarile. Conditia logica din clauza WHERE poate utiliza orice functie sau operator disponibil in MySQL, cu exceptia functiilor de sumarizare.
mysql> SELECT * -> FROM sakila.actor -> WHERE last_name RLIKE '^(jo)' AND SUBSTRING(first_name, 1, 1) IN ('m', 'k', 'r'); +----------+------------+-----------+---------------------+ | actor_id | first_name | last_name | last_update | +----------+------------+-----------+---------------------+ | 8 | MATTHEW | JOHANSSON | 2006-02-15 04:34:33 | | 43 | KIRK | JOVOVICH | 2006-02-15 04:34:33 | | 64 | RAY | JOHANSSON | 2006-02-15 04:34:33 | +----------+------------+-----------+---------------------+
In raspunsul precedent poate fi inserata o coloana id care sa contina numarul de ordine al inregistrarilor din rezultat. Gererarea valorilor de la nivelul acestei coloane necesita utilizarea unei variabile de utilizator, denumita @id. Acesata variabila este initializata in instructiunea SET, dupa care prima expresie din lista de selectie genereaza valorile prin inrementare cu unitatea.
mysql> SET @id = 0; mysql> SELECT (@id := @id +1) AS id, actor.* -> FROM sakila.actor -> WHERE last_name RLIKE '^(jo)' AND SUBSTRING(first_name, 1, 1) IN ('m', 'k', 'r'); +------+----------+------------+-----------+---------------------+ | id | actor_id | first_name | last_name | last_update | +------+----------+------------+-----------+---------------------+ | 1 | 8 | MATTHEW | JOHANSSON | 2006-02-15 04:34:33 | | 2 | 43 | KIRK | JOVOVICH | 2006-02-15 04:34:33 | | 3 | 64 | RAY | JOHANSSON | 2006-02-15 04:34:33 | +------+----------+------------+-----------+---------------------+
Gruparea datelor
Un grup include toate inregistrarile de la nivelul unui rezultat care prezinta aceeasi valoare pentru expresia de grupare. Clauza GROUP BY specifica numele unor coloane, expresii sau pozitii ale unor coloane din tabel care definesc grupuri de inregistrari. In general, expresia de grupare este reprezentata de o singura coloana.
mysql> SELECT first_name -> FROM sakila.actor -> WHERE first_name BETWEEN 'b' AND 'c' -> GROUP BY first_name; +------------+ | first_name | +------------+ | BELA | | BEN | | BETTE | | BOB | | BURT | +------------+
Prin utilizarea clauzei GROUP BY, inregistrarile din rezultat sunt ordonate dupa coloanele precizate in expresia de grupare, in mod similar aplicarii clauzei ORDER BY. Pentru a evita acest lucru sintaxa trebuie sa includa ORDER BY NULL.
mysql> SELECT first_name -> FROM sakila.actor -> WHERE first_name BETWEEN 'b' AND 'c' -> GROUP BY first_name -> ORDER BY Null; +------------+ | first_name | +------------+ | BETTE | | BOB | | BURT | | BEN | | BELA | +------------+
MySQL extinde clauza GROUP BY, astfel incat pot fi utilizati parametrii ASC si DESC dupa coloanele din expresia de grupare.
Daca clauza GROUP BY lipseste, iar in lista cu expresii de selectie apar functii de sumarizare, se considera ca toate inregistrarile fac parte din acelasi grup. Functia de sumarizare COUNT(*) returneaza numarul de inregistrari pentru fiecare grup.
mysql> SELECT COUNT(*) AS countActors -> FROM sakila.actor; +-------------+ | countActors | +-------------+ | 200 | +-------------+
Clauza HAVING
Clauza HAVING se utilizeaza impreuna cu GROUP BY pentru a selecta un anumit grup de inregistrari. Daca clauza WHERE se utilizeaza pentru a filtra inregistrarile rezultate in urma rularii unei interogari, clauza HAVING permite filtrarea inregistrarilor obtinute prin aplicarea functiilor de sumarizare. Diferenta dintre cele doua clauze amintite anterior este data de faptul ca HAVING filtreaza inregistrarile din rezultat, dupa ce acestea au fost grupate.
Putem avea in vedere determinarea grupurilor de actori care detin aceiasi initiala pentru numele de familie. In acest caz, clauza HAVING se utilizeaza dupa realizarea grupurilor de inregistrari, pentru a pastra doar acele grupuri care au mai mult de 10 inregistrari (actori).
mysql> SELECT SUBSTR(last_name, 1, 1) AS letter, COUNT(*) AS countByFirstChar -> FROM sakila.actor -> GROUP BY letter ASC -> HAVING countByFirstChar > 10; +--------+------------------+ | letter | countByFirstChar | +--------+------------------+ | B | 22 | | C | 15 | | D | 21 | | G | 12 | | H | 19 | | M | 15 | | P | 14 | | T | 13 | | W | 19 | +--------+------------------+
Clauza HAVING poate referi coloane specificate in expresiile de selectie, coloane introduse de GROUP BY si functii de sumarizare. Clauza WHERE nu poate referi functii de sumarizare.
Ordonarea inregistrarilor
Ordonarea inregistrarilor de la nivelul unui rezultat al unei interogari dupa anumite coloane/expresii se realizeaza prin intermediul clauzei ORDER. Daca sunt specificate valori intregi, acestea vor fi interpretate ca fiind numerele de ordine ale coloanelor din lista care cuprinde expresiile de selectie; prima coloana din lista de selectie are alocat indexul 1.
mysql> SELECT first_name, last_name -> FROM sakila.actor -> WHERE first_name BETWEEN 'b' AND 'c' -> ORDER BY first_name DESC, last_name ASC; +------------+-----------+ | first_name | last_name | +------------+-----------+ | BURT | DUKAKIS | | BURT | POSEY | | BURT | TEMPLE | | BOB | FAWCETT | | BETTE | NICHOLSON | | BEN | HARRIS | | BEN | WILLIS | | BELA | WALKEN | +------------+-----------+
Ordonarea se face dupa prima coloana specificata in clauza ORDER BY. Daca doua inregistrari prezinta aceeasi valoare in prima coloana, ordonarea se face luand in calcul restul de coloane din clauza ORDER BY (daca acestea sunt specificate).
Parametrii ASC si DESC indica ordonarea crescatoare, respectiv descrescatoare a inregistrarilor din rezultat. Daca nu este specificat nici unul din parametrii ASC, DESC, ordonarea se face in ordine crescatoare.
Clauza LIMIT
Clauza LIMIT poate fi utilizata pentru a controla numarul de inregistrari returnate prin intermediul instructiunii SELECT. In cadrul acestei clauze pot fi utilizate unul sau doua argumente intregi, pozitive. Daca utilizeaza ambele argumente, clauza LIMIT indica pozitia primei inregistrari returnate in rezultat (primul argument) si numarul maxim de inregistrari returnate (cel de-al doilea argument).
mysql> SELECT * -> FROM sakila.actor -> LIMIT 2, 5; +----------+------------+--------------+---------------------+ | actor_id | first_name | last_name | last_update | +----------+------------+--------------+---------------------+ | 3 | ED | CHASE | 2006-02-15 04:34:33 | | 4 | JENNIFER | DAVIS | 2006-02-15 04:34:33 | | 5 | JOHNNY | LOLLOBRIGIDA | 2006-02-15 04:34:33 | | 6 | BETTE | NICHOLSON | 2006-02-15 04:34:33 | | 7 | GRACE | MOSTEL | 2006-02-15 04:34:33 | +----------+------------+--------------+---------------------+
Pozitia primei inregistrari din lista este 0. Daca nu este specificat primul argument, acesta se considera 0. Sintaxa LIMIT numar_inregistrari este echivalenta cu LIMIT 0, numar_inregistrari.
mysql> SELECT film_id, title, release_year -> FROM sakila.film -> LIMIT 0, 5; +---------+------------------+--------------+ | film_id | title | release_year | +---------+------------------+--------------+ | 1 | ACADEMY DINOSAUR | 2006 | | 2 | ACE GOLDFINGER | 2006 | | 3 | ADAPTATION HOLES | 2006 | | 4 | AFFAIR PREJUDICE | 2006 | | 5 | AFRICAN EGG | 2006 | +---------+------------------+--------------+