background image

Podstawowe polecenia języka SQL  

• 

SELECT 

Składnia polecenia 

SELECT

 jest mniej więcej następująca:  

SELECT [PARAMETRY] wyr [FROM tabela [OPCJE]]

  

a ściślej następująca:  

   

 

SELECT

 

   [ALL | DISTINCT | DISTINCTROW ]

 

   [HIGH_PRIORITY]

 

   [STRAIGHT_JOIN]

 

   [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]

 

   [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]

 

 wyr, ...

 

 [INTO OUTFILE 'plik' parametry_zapisu_do_pliku

 

   | INTO DUMPFILE 'plik']

 

 [FROM tabele

 

   [WHERE definicje_skąd]

 

   [GROUP BY {kolumna | wyr | pozycja}

 

   [ASC | DESC], ... [WITH ROLLUP]]

 

   [HAVING definicje_skąd]

 

   [ORDER BY {koluna | wyr | pozycja}

 

   [ASC | DESC] , ...]

 

   [LIMIT {[offset,] ile_wierszy | ile_wierszy OFFSET 
offset}]

 

   [PROCEDURE nazwa_procedury(lista_argumentów)]

 

   [FOR UPDATE | LOCK IN SHARE MODE]] 

Za pomocą polecenia 

SELECT

 możemy uzyskać interesujące nas dane z bazy 

danych.  

ƒ

 

każde wyrażenie 

wyr

 oznacza nazwę kolumny, z której chcemy otrzymać 

dane. Kolumn może być więcej i w takim wypadku rozdziela się je 
przecinkami.  

ƒ

 

tabela

 oznacza tabelę, z której chcemy uzyskać wiersze. Tabel może być 

więcej i w takim przypadku rozdziela się je przecinkami. Jeśli nazwy tablic nie 
są unikalne to należy stosować notację kropkową.  

ƒ

 

za pomocą opcji i parametrów możemy znacząco skomplikować polecenie 

SELECT

, i w ten sposób bardzo dokładnie określić, o które dane nam chodzi.  

ƒ

 

wyniki zapytania 

SELECT

 możemy wyświetlić zarówno na terminalu, jak 

również przekierować do pliku, dzięki czemu możemy wykonać backup 
wybranych danych, chociaż są do tego odrębne i lepsze narzędzia.  

ƒ

 

istnieją PARAMETRY, dzięki którym możemy wydobyć jedynie dane 
niepowtarzające się; dzięki odpowiednim opcjom możemy dane sortować i 
ograniczać ilość wyświetlanych danych,  

ƒ

 

Dzięki słowu 

WHERE

 można znacząco poprawić precyzję naszego zapytania 

do bazy danych. Po słowie 

WHERE

 możemy użyć dowolnych funkcji MySQL-a, 

poza funkcjami agregującymi. Na przykład:  

background image

   

 

mysql> SELECT col1 FROM tab WHERE col1 > 0; 

ƒ

 

Oprócz słowa 

WHERE

 możemy użyć podobnego w znaczeniu słowa 

HAVING

Słowo 

HAVING

 jest interpretowane w zapytaniu dopiero wówczas, gdy dane 

są wysyłane do klienta, który wysłal zapytanie do bazy, a więc już po 
wykonaniu wyszukania danych przez serwer. Inaczej mówiąc używanie słowa 

HAVING

 powinno być raczej ograniczone gdyż nie poddaje się optymalizacji 

zapytań do bazy danych. Słowo 

HAVING

 ma jednak tą zaletę, że można go 

używać w przypadku funkcji agregujących, czego nie można zrobić przy 
pomocy słowa 

WHERE

  

   

 

mysql> SELECT nazwisko, MAX(wynagrodzenie) FROM pracownicy 

GROUP BY nazwisko HAVING MAX(wynagrodzenie) > 10; 

ƒ

 

Wyrażeniu 

wyr

 można nadać skrót (ang. alias) przy pomocy partykuły 

AS

Taki skrót można dalej używać jako złożone wyrażenie po opcjach 

GROUP 

BY, ORDER BY, HAVING

.  

   

 

mysql> SELECT CONCAT(nazwisko,', ',imie) AS imie_nazw FROM 
tab ORDER BY imie_nazw; 

ƒ

 

Słowo 

AS

 jest opcjonalne. Powyższy przykład można równie dobrze zapisać 

jako:  

   

 

mysql> SELECT CONCAT(nazwisko,', ',imie) imie_nazw FROM tab 
ORDER BY imie_nazw; 

ƒ

 

jednak może to rodzić pewne problemy, jeśli za pomocą polecenia 

SELECT

 

wydobywa się dane z dwóch kolumn i zapomni się wstawić przecinka między 
nazwami kolumn. Wtedy nazwa kolumny, która powinna wystąpić po 
przecinku ale przecinka nie było, jest interpretowana jak skrót poprzedzającej 
ją nazwy kolumny. Na przykład:  

   

 

mysql> SELECT columna columnb FROM mytable; 

ƒ

 

W tym przykładzie 

columnb

 jest traktowane jak skrót 

columna

.  

ƒ

 

Nazwa tablicy też może mieć skrót, co czasem poprawia czytelność złożonych 
zapytań, i oczywiście skraca je. Skrót nazw tablic tworzy się używając składni 

tablica AS skrot

, na przykład  

   

 

mysql> SELECT t1.nazwa, t2.zarobki FROM pracownicy AS t1, 
info AS t2 WHERE t1.nazwa = t2.nazwa; 

mysql> SELECT t1.nazwa, t2.zarobki FROM pracownicy t1, info 

background image

t2 WHERE t1.nazwa = t2.nazwa; 

ƒ

 

Wydobyte wiersze danych możemy sortować względem nazw kolumn, 
skrótów nazw kolumn lub pozycji kolumn. Pozycje kolumn są liczbami 
całkowitymi i zaczynają się od wartości 1. Sortowanie zględem pozycji kolumn 
jest już przestarzałą właściwością i nie należy jej używać.  

   

 

mysql> SELECT szkola, miasto, wynik FROM zawody ORDER BY 

miasto, wynik; 

mysql> SELECT szkola, miasto AS m, wynik AS w FROM zawody 
ORDER BY m, w; 

mysql> SELECT szkola, miasto, wynik FROM zawody ORDER BY 2, 

3; 

ƒ

 

Aby sortować w odwrotnej kolejności należy dodać słowo 

DESC

 (ang. 

descending

) do nazwy kolumny po opcji 

ORDER BY

. Domyślnie sortuje się 

rosnąco, co zresztą można napisać explicte używając słowa 

ASC

.  

ƒ

 

Istnieją też inne sposoby modyfikacji sposobu sortowania i uzyskiwania przez 
to odpowiednich danych, ale wybiegają one poza ten wykład. Na przykład 
takim modyfikatorem jest 

WITH ROLLUP

 umieszczany po opcji 

GROUP BY 

kolumna

.  

ƒ

 

Aby ograniczyć ilość wypisywanych danych można posłużyć się słowem 

LIMIT

LIMIT

 przyjmuj jeden lub dwa argumenty całkowite. Jeśli podamy 

dwa rgumenty, to pierwszy oznacza od którego wiersza zaczniemy wyświetlać 
dane, natomiast drugi oznacza ile wierszy wyświetlimy, przy czym w tym 
wypadku wiersze nujmeruje ię od 0, a nie 1, zatem:  

   

 

mysql> SELECT * FROM tab LIMIT 5,10; # zwraca wiersze 6-15  

ƒ

 

Dla kompatybilności z baza PostgreSQL, MySQL ozumie też następującą 
składnię 

LIMIT ile_wierszy OFFSET od_ktorego

. Aby uzyskać 

wszystkie wiersze począwszy od jakiegoś numeru, można posłużyć się 
bardzo duża liczbą użytą jako drugi parametr słowa 

LIMIT

. Na przykłąd aby 

uzyskać wiersze od 96-ego do otatniego można napisać tak:  

   

 

mysql> SELECT * FROM tab LIMIT 95,18446744073709551615; 

ƒ

 

Kiedy podamy tylko jeden argument numeryczny dla słowa 

LIMIT

, wtedy jest 

on rozumiany jako liczba wierszy zaczynając liczenie od początku, innymi 
słowy 

LIMIT n

 jest równoważne 

LIMIT 0,n

. Na przykład:  

   

 

mysql> SELECT * FROM tab LIMIT 5; # zwraca 5 pierwszych 
wierszy  

background image

ƒ

 

Od wersji 4.1 MySQL-a można używać imitacji tablicy - 

DUAL

, która w 

rzeczywistości nie istnieje. Nie jest konieczne używanie nazwy 

DUAL

 kiedy 

nie używamy żadnej tablicy, ale zostało to wprowadzone dla kompatybilności 
z innymi bazami danych.  

   

 

mysql> SELECT 1 + 1 FROM DUAL;  

ƒ

 

Polecenie 

SELECT ... INTO OUTFILE 'nazwa_pliku'

 zapisuje 

zwracane przez 

SELECT

 wiersze do pliku o nazwie 

nazwa_pliku

. Plik jest 

tworzony na serwerze z bazą danych, więc dany użytkownik musi mieć 
odpowiednie prawa do zapisywania plików na serwerze. Plik o nazwie 

nazwa_pliku

 nie może wcześniej istnieć, co zabezpiecza chociażby takie 

pliki jak /etc/passwd przed zniszczeniem ich zawartości. Wyrażenie 

SELECT 

... INTO OUTFILE

 jest przeznaczone głównie do szybkiego zapisania 

zawartości tablicy w pliku na serwerze bazodanowym. Jeśli zamierzamy 
wyniki wykonania polecenia 

SELECT

 zapisać w pliku na serwerze klienta, to 

należy posłużyć się raczej komendą  

   

 

mysql -e "SELECT ..." > file_name  

ƒ

 

Możemy użyć odpowiednich opcji w poleceniu 

SELECT ... INTO 

OUTFILE

, mianowicie:  

FIELDS TERMINATED BY

  

ENCLOSED BY

  

ESCAPED BY

  

LINES TERMINATED BY

  

Na przykład:  

   

 

mysql> SELECT a,b,a+b INTO OUTFILE '/tmp/result.txt' FIELDS 

TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED 
BY '\n' FROM test_tab;  

Uwaga! Jakikolwiek plik utworzony w wyniku wydania polecenia 

INTO 

OUTFILE

 lub 

INTO DUMPFILE

 jest tworzony z prawami pisania w nim 

przez wszystkich użytkowników systemu na serwerze bazodanowym.  

ƒ

 

Jeśli zamiast 

SELECT ... INTO OUTFILE

 użyjemy polecenia 

SELECT 

... INTO DUMPFILE

 MySQL zapisze w pliku tylko jedną długą linię, a 

więc bez jakichkolwiek znaków końca linii czy kolumny. Jest to użyteczne w 
przypadku używania danych typu 

BLOB

.  

ƒ

 

Za pomocą opcji 

ALL, DISTINCT, DISTINCTROW

 można określić czy 

takie same wiersze, które zostaną odnalezione za pomocą polecenia 

SELECT

 

mają być wypisywane czy też nie. Jeśli chcemy mieć tylko dane unikalne 
możemy posłużyć się opcjami 

DISTINCT

 lub 

DISTINCTROW

, jeśli zaś 

chcemy uzyskać wszystkie dane, które zostały dopasowane za pomocą 
polecenia 

SELECT

 możemy po prostu opuścić te opcje, lub explicite podać 

opcję 

ALL

.  

background image

ƒ

 

HIGH_PRIORITY

. Najczęściej z bazą danych pracuje jednocześnie kilka 

(lub więcej) osób. Często zdaża się, że pracują na tych samych danych. Jeśli 
jedna z nich dokonuje zmiany zawartości tablicy a druga chce w tym samym 
czasie uzyskać dane z tej tablicy, to najpierw jest wykonywane polecenie 
zmieniające dane w tablicy, a dopiero później za pomocą polecenia 

SELECT

 

są wybierane (zaktualizaowane już w tym momencie) dane. Za pomocą 
parametru 

HIGH_PRIORITY

 można nadać poleceniu 

SELECT

 wysoki 

priorytet i w ten sposób uzyskać dane nawet wtedy kiedy tablica jest jakby 
"zamknięta" (ang. locked) do czytania z powodu wykonywania zmian danych 
w tej tablicy.  

ƒ

 

STRAIGHT_JOIN

 wymusza optymalizator zapytania do bazy (uruchamiany 

jest zawsze w momencie wywoływania polecenia) do łączenia tablic w 
kolejności takiej, w jakiej są wymieniane po słowie 

FROM

. Można też użyć 

tego parametru do przyspieszenia zapytania jeśli optymalizator łączy tabele w 
kolejności nieoptymalnej.  

ƒ

 

SQL_BIG_RESULT

 można użyć w przypadku, kiedy używamy też 

GROUP 

BY

 lub 

DISTINCT

, aby poinformować optymalizator, że wynik zapytania 

będzie się składał z dużej ilości danych. W takim wypadku MySQL użyje tzw. 
tablic tymczasowych jeśli to będzie konieczne, co powinno przyspieszyć 
uzyskanie danych i odciążyć serwer bazodanowy z konieczności użycia tzw. 
pamięci swap.  

ƒ

 

SQL_BUFFER_RESULT

 wymusza użycia tablic do umieszczania w nich 

wyników zapytania. Jest to szczególnie pomocne w przypadku, kiedy z dużą 
bazą danych pracuje wielu użytkowników i kiedy często następuje 
"zamykanie" dostępu do tablic z powodu zmiany danych w tabliach. W takich 
wypadkach tablice tymczasowe pomagają szybciej "otwierać" "zamknięte" 
tablice i dawać do nich dostęp.  

ƒ

 

SQL_SMALL_RESULT

 może być użyte razem z 

GROUP BY

 lub 

DISTINCT

 

w celu użycia szybszych tablic tymczasowych zamiast sortowania.  

ƒ

 

SQL_CALC_FOUND_ROWS

 (parametr dostępny od wersji 4.0.0 MySQL-a) 

wymusza policzenie ilości wszystkich wierszy zapytania (lekceważąc 
ewentualną opcję 

LIMIT

. Liczba znalezionych wierszy może być później 

użyta do w funkcji 

SELECT FOUND_ROWS()

 i wykorzystana do yzyskania 

tych wierszy.  

ƒ

 

SQL_CACHE

 pozwala zachować wyniki polecenia 

SELECT

 w specjalnym 

buforze. To z kolei pomaga szybciej używać tych danych bez potrzeby 
każdorazowego wykonywania wyszukiwania tych danych; jeśli jeszcze raz 
użyjemy identycznego zpaytania, to serwer wysyła dane z bufora a nie 
parsuje i wykonuje jeszcze raz polecenie wyszukania danych. Jest to 
szczególnie przydatne kiedy mamy tablice, które nie zmieniają się zbyt często. 
Jeśli w buforze znajdują się dane z ostatnio zbuforowanego zapytania i 
wykonamy zmiany w tablicach, z których uzyskaliśmy dane, to serwer 
wyczyści bufor i następne zapytanie, które tutaj korzystałoby z bufora, będzie 
już normalnie traktowane.  

ƒ

 

SQL_NO_CACHE

, odwrotnie niż 

SQL_CACHE

 powoduje nieumieszczanie 

danych w buforze.  

 

• 

DELETE 

Składnia polecenia 

DELETE

 jest mniej więcej następująca:  

DELETE [PARAMETRY] FROM tabela [OPCJE]]

  

background image

Składnia polecenia 

DELETE

 różni się nieznacznie w zależności od tego, czy chcemy 

usunąć dane z jednej tablicy czy też z kilku tabel jednocześnie.  

W przypadku usuwania danych z jednej tablicy:  

   

 

DELETE

 

  [LOW_PRIORITY] [QUICK] [IGNORE] FROM tabela

 

  [WHERE definicje_skąd]

 

  [ORDER BY ...]

 

  [LIMIT liczba_wierszy] 

W przypadku usuwania danych z kilku tablic:  

   

 

DELETE

 

  [LOW_PRIORITY] [QUICK] [IGNORE]

 

  nazwa_tablicy[.*] [, nazwa_tablicy[.*] ...]

 

  FROM nazwy_tablic

 

  [WHERE definicje_skąd] 

lub też  

   

 

DELETE

 

  [LOW_PRIORITY] [QUICK] [IGNORE]

 

  FROM nazwa_tablicy[.*] [, nazwa_tablicy[.*] ...]

 

  USING nazwy_tablic

 

  [WHERE definicje_skąd] 

Za pomocą polecenia 

DELETE

 możemy usunąć wiersze danych z tablicy, które 

spełniają odpowiednie warunki wymienione w klauzuli 

WHERE

. Polecenie 

DELETE

 

zwraca liczbę usuniętych wierszy danych.  

ƒ

 

Uwaga! Jeśli wykonamy polecenie 

DELETE

 bez klauzuli 

WHERE

, wtedy 

usunięte zostają wszystkie dane ze wskazanej tablicy. Znacznie szybszą 
metodą usunięcia wszystkich danych z tablicy jest użycie polecenia 

TRUNCATE TABLE nazwa_tablicy

, przy czym nie zostanie wtedy 

zwrócona liczba usuniętych rekordów (wierszy).  

ƒ

 

Jeśli w poleceniu 

DELETE

 podamy parametr 

LOW_PRIORITY

 wtedy 

wykonanie 

DELETE

 jest opóźniane dopóty, dopóki nie wykonają się wszystkie 

inne polecenia na danej tablicy.  

ƒ

 

Jeśli w poleceniu 

DELETE

 podamy parametr 

QUICK

 w przypadku usuwania 

danych z tablicy typu 

MyISAM

, wtedy istnieje szansa przyspieszenia w 

niektórych przypadkach usuwania danych.  

ƒ

 

Parametr 

IGNORE

 powoduje ignorowanie wszytkich błędów pojawiających się 

podczas usuwania rekordów. Błędy, które są ignorowane podczas 
wykonywania 

DELETE

 z parametrem 

IGNORE

 są zgłaszane jako 

ostrzerzenia.  

ƒ

 

W przypadku tablic typu 

MyISAM

, usuwane rekordy są zachowywane na 

specjalnej liście, i bezpośrednio później wykonane polecenie 

SELECT

 zwraca 

usunięte rekordy. Oznacza to, że usuwane dane zajmują cały czas zasoby 

background image

komputera. Można to zmienić używając polecenia 

OPTIMIZE TABLE

 lub 

myisamchk

 i zmieniając tablicę.  

ƒ

 

Opcja 

LIMIT liczba_wierszy

 ogranicza liczbę usuwanych rekordów do 

liczba_wierszy

.\ Przydatne jest to w przypadku, gdy chcemy się 

upewnić, że nasze polecenie 

DELETE

 nie zajmie zbyt dużo czasu pracy bazy. 

Można powtarzać to polecenie dopóty, dopóki nie zostanie przekroczona 
liczba wierszy do usunięcia.  

ƒ

 

Jeśli użyjemy polecenia 

DELETE

 razem z klauzulą 

ORDER BY

 wtedy 

możemy usuwać rekordy w kolejności takiej, jaka zostanie obliczona po 
wykonaniu sortowania. Jest to bardzo użyteczne w połączeniu z opcją 

LIMIT

. Na przykład w poniższym przykładzie znajdowane są rekordy 

pasujące do klauzuli 

WHERE

, następnie sortowane względem danych typu 

timestamp i usuwany jest tylko jeden z nich (najstarszy):  

   

 

DELETE FROM jakas_tablica

 

WHERE user = 'kowalski'

 

ORDER BY timestamp

 

LIMIT 1; 

ƒ

 

Można usuwać dane z kilku tabel jednocześnie. Pierwsza, podana wyżej, 
składnia umożliwia usuwanie danych tylko z tablic wymienionych przed 
klauzulą 

FROM

. Druga składnia umożliwia usuwanie danych tylko z tych tablic, 

które zostały wymienione przed klauzulą 

USING

. Efekt jest taki, że można 

usuwać dane z kilku tablic jednocześnie i również mieć dodatkowe tablice, 
które są używane podczas szukania, ale z których nie usuwamy danych  

   

 

mysql> DELETE t1,t2 FROM t1,t2,t3 WHERE t1.id=t2.id AND 
t2.id=t3.id; 

mysql> DELETE FROM t1,t2 USING t1,t2,t3 WHERE t1.id=t2.id AND 
t2.id=t3.id; 

ƒ

 

Powyższe polecenie używają trzech tablic do wyszukania odpowiednich 
danych ale powdują usunięcie danych tylko z tablic 

t1,t2

.  

ƒ

 

Jeśli chcemy usuwać dane z kilku tabel relacyjnych typu 

InnoDB

, w których 

zdefiniowane są klucze obce, wtedy usuwanie rekordów poleceniem 

DELETE

 

może odbywać się w innej kolejności niż powinno to wynikać z relacji 
pomiędzy tablicami. W takim wypadku wykonanie polecenia 

DELETE

 nie 

powiedzie się a ropoczęte usuwanie danych jest odwracane i dane sa 
bezpieczne. W takim przypadku, można usuwać dane tylko z jednej tablicy i 
polegać na możliwościach składni 

ON DELETE

 używanego przy konstrukcji 

tablicy, która sprawi, że odpowiednie tablice relacyjne zostaną zmodyfikowane 
jak należałoby się tego spodziewać (pod warunkiem że dobrze zdefiniujemy 
relacje pomiędzy danymi w różnych tablicach!).  

 

• 

INSERT 

Składnia polecenia 

INSERT

 jest mniej więcej następująca:  

background image

INSERT [PARAMETRY] tabela VALUES [OPCJE]]

  

a dokładniej  

   

 

INSERT

 

  [LOW_PRIORITY | DELAYED] [IGNORE]

 

  [INTO] tabela [(kol1,kol2,...)]

 

  VALUES ({wyr | DEFAULT},...),(...),...

 

  [ ON DUPLICATE KEY UPDATE kol=wyr, ... ] 

lub:  

   

 

INSERT

 

  [LOW_PRIORITY | DELAYED] [IGNORE]

 

  [INTO] tablica

 

  SET kol={wyr | DEFAULT}, ...

 

  [ ON DUPLICATE KEY UPDATE kol=wyr, ... ] 

lub też:  

   

 

INSERT

 

  [LOW_PRIORITY | DELAYED] [IGNORE]

 

  [INTO] tablica [(kol1,kol2,...)]

 

  SELECT ... 

Za pomocą polecenia 

INSERT

 możemy wstawić nowe rekordy danych do istniejącej 

tablicy.  

Składnia 

INSERT ... VALUES

 oraz 

INSERT ... SET

 wstawiają nowe rekordy 

jeśli dokładnie podamy wszystkie wartości (do wszystkich pól tablic). Składnia 

INSERT ... SELECT

 wstawia rekordy wybrane z innej tablicy lub z innych tablic.  

Nazwy kolumn, do których będziemy definiować dane można zadać dokładnie albo 
podając w poleceniu 

INSERT

 listę nazw kolumn 

(kol1,kol2,...)

 albo stosując 

drugą z wymienionych składnię , a więc używając słowa 

SET

. Jeśli nie chcemy 

podawać nazw kolumn (bo na przykład może być ich dużo) to nie musimy tego robić; 
wystarczy jeśli podamy poprawnie listę wartości, ale bezwględnie ważna jest tutaj 
kolejność wartości na liście wartości 

VALUES()

. Jeśli nie znamy struktury tablicy 

albo kolejności pól (kolumn) to możemy posłużyć się poleceniem 

DESCRIBE 

nazwa_tablicy

.  

Wartości kolumn możemy podawać na kilka różnych sposobów:  

ƒ

 

jeśli nie podamy wartości dla kolumny to przyjmowana jest wartość domyślna, 
zdefiniowana podczas definicji struktury tablicy. Możemy na przykład nie 
podać wartości wszystkich pól (kolumn) tablicy. W takim przypadku MySQL 
wypełni wartościami domyślnymi wszystkie pola wstawianego rekordu. 
MySQL ma zawsze zdefiniowane wartości dla wszystkich kolumn. 
Nienadawanie wartości polom rekordu nie jest jednak dobrym nawykiem, gdyż 

background image

z bazy danych korzystają aplikacje, które najczęściej oczekują konkretnych 
danych, a nie wartości domyślnych.  

Przykład:  

   

 

mysql> INSERT INTO nazwa_tab () VALUES();  

ƒ

 

Począwszy od wersji 4.0.3 MyQL-a można użyć słowa 

DEFAULT

 aby 

dosłownie nadać wartość domyślną polu rekordu.  

Przykład:  

   

 

mysql> INSERT INTO nazwa_tab VALUES 
(DEFAULT,DEFAULT,'Kowalski','18','1','0',DEFAULT); 

ƒ

 

Jeśli typ danych, które chcemy wstawić do tablicy nie odpowiada typowi 
kolumny zadeklarowanemu podczas tworzenia tablicy, wtedy może nastąpić 
domyślna konwersja do odpowiedniego typu danych. Na przykład próba 
wstawienia stringu 

'1999.0e-2'

 do kolumn typu 

INT, FLOAT, 

DECIMAL(10,6)

 lub 

YEAR

 spowoduje wstawienie odpowiednio 

1999, 

19.9921, 19.992100

, i 

1999

. Powodem, dla którego w kolumnach typu 

INT

 i 

YEAR

 znajdzie się 1999 jest to, że funkcja dokonująca konwersji typu 

string-to-integer "patrzy" tylko na początek stringu 

'1999.0e-2'

 i "widzi", 

że pasuje do formatu liczby całkowutej albo roku. W przypadku kolumn typu 
zmiennoprzecnikowego lub stałoprzecinkowego funkcja dokonująca konwersji 
typu string-to-floating-point i string-to-fixed-point "patrzy" na cały string jak na 
potencjalną liczbę o właściwej konsrukcji.  

ƒ

 

Wyrażenie 

wyr

 może się odnosić do dowolnej kolumny, dla której wartość 

zdefiniowano wcześniej (w tej samej instrukcji 

INSERT

).  

Na przykład:  

   

 

mysql>INSERT INTO tab (col1,col2) VALUES(15,col1*2);  

Tutaj można się było odwołać do 

col1

, gdyż 

col1

 było zdeiniowane 

wcześniej niż 

col2

.  

Przykład 2:  

   

 

mysql> INSERT INTO tab (col1,col2) VALUES(col2*2,15);  

To wyrażenie jest niepoprawne, gdyż wartość 

col1

 odnosi się do 

col2

która nie została jeszcze zdefiniowana.  

 
Można stosować następujące parametry wywołania polecenia 

INSERT

:  

background image

ƒ

 

Parametr 

DELAYED

 powoduje wstawienie wiersza lub wierszy do buforu. 

Dane z fubora są wstawiane do tablicy w momencie, kiedy jest ona "wolna", 
tzn. nie ma do niej w danej chwili żadnych zapytań. Sprawdzanie jest 
dokonywane cyklicznie. Jeśli w wyniku któregoś sprawdzenia okaże się, że są 
zapytania do danej tablicy, wtedy polecenie 

INSERT

 jest wstrzymywane do 

momentu aż nie nastąpi "zwolnienie" tablicy.  

ƒ

 

Parametr 

LOW_PRIORITY

 powoduje opóźninie wykonania polecenia 

INSERT

 doputy, dopóki klienci czytają dane z danej tablicy. Może to 

spowodować długie czekanie na wykonanie 

INSERT LOW_PRIORITY

, w 

przypadku kiedy baza jest mocno obciążona zapytaniami do tablicy. W 
przeciwieństwie do opcji 

LOW_PRIORITY

 opcja 

DELAYED

 pozwala klientowi 

kontynuować natychmiast, kiedy tylko zwolni się dostęp do tablicy.  

ƒ

 

Parametr 

HIGH_PRIORITY

 powoduje zaniechanie konsekwencji jakie 

pociąga ze sobą uruchomienie bazy danych z opcją 

--low-priority-

updates

. Baza danych uruchomiona z taką opcją domyślnie pozwala 

wykonywać polecenia zmiany danych w tablicach (polecenia 

INSERT, 

UPDATE

 tak, jakby były one wykonywane z opcją 

LOW_PRIORITY

.  

ƒ

 

Parametr 

IGNORE

 powoduje ignorowanie wstawianych rekordów jeśli 

zawierają istniejący już w wierszu/ach tablicy indeks 

UNIQUE

 lub 

PRIMARY 

KEY

. Jeśli uruchomimy polecenie 

INSERT

 bez tego parametru i znajdzie się 

wiersz, który duplikuje istniejący klucz 

UNIQUE

 lub 

PRIMARY KEY

 wtedy 

całe polecenie wstawiające kilka wierszy jest przerywane. Jest to dość 
kłopotliwa sytuacja gdyż wtedy najczęściej trzeba zliczyć w jakiś sposób ile 
rekordów zostało do bazy wstawionych, i które rekordy zostały wstawione.  

Jeśli do polecenia 

INSERT

 dopiszemy 

ON DUPLICATE KEY UPDATE

 wtedy 

zamiast przerywania wykonania całego polecenia 

INSERT

 lub omijania takich 

rekordów, które duplikują wartość indeksu 

UNIQUE

 lub 

PRIMARY KEY

, następuje 

UPDATE

 rekordu.  

Na przykład jeśli kolumna 

a

 jest zadeklarowana jako 

UNIQUE

 i już zawiera wartość 

1, to poniższe dwa wyrażenia dają ten sam efekt:  

   

 

mysql> INSERT INTO tab (a,b,c) VALUES (1,2,3) ON DUPLICATE 
KEY UPDATE c=c+1; 

mysql> UPDATE tab SET c=c+1 WHERE a=1;  

Uwaga. Jeśli kolumna 

b

 również jest zadeklarowana jako 

UNIQUE

 wtedy polecenie 

INSERT

 może być równoważne następującemu poleceniu 

UPDATE

:  

   

 

mysql> UPDATE tab SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;  

Jeśli 

a=1 OR b=2

 pasuje do kilku (kilkunastu) wierszy wtedy trzeba pamiętać, że 

tylko jeden wiersz jest zmieniany instrukcją 

UPDATE

. Lepiej więc zabezpieczać się 

przed takimi wypadkami definiując odpowiednio tablice używając klauzuli 

ON 

DUPLICATE KEY

 niż wierzyć że jeśli się coś stanie to 

ON DUPLICATE KEY 

UPDATE

 uratuje nas przecież.  

background image

Od wersji 4.1.1 MySQLa można używać funkcji 

VALUES(col)

 w klauzuli 

ON 

DUPLICATE KEY UPDATE

 odnosząc się do kolumn z części 

INSERT ... 

UPDATE

. Innymi słowy 

VALUES(col)

 w klauzuli 

ON DUPLICATE KEY UPDATE

 

odnosi się do wartości kolumny 

col

, która mogłaby zostać wstawiona jeśli nie 

zdarzyłby się konflikt duplikacji klucza. Jest to szczególnie użyteczne, kiedy 
wstawiamy dane do wielu wierszy za pomoca jednego polecenia 

INSERT

.  

Na przykład:  

   

 

mysql> INSERT INTO tab (a,b,c) VALUES (1,2,3),(4,5,6) ON 

DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);  

Takie wyrażenie jest równoważne następującym dwom wyrażeniom:  

   

 

mysql> INSERT INTO tab (a,b,c) VALUES (1,2,3) ON DUPLICATE 

KEY UPDATE c=3;  

mysql> INSERT INTO tab (a,b,c) VALUES (4,5,6) ON DUPLICATE 
KEY UPDATE c=9;  

Jeśli używamy opcji 

ON DUPLICATE KEY UPDATE

 wtedy automatycznie 

parametr 

DELAYED

 jest ignorowany.  

W przypadku kolumn zadeklarowanych jako 

AUTO_INCREMENT

 można dowiedzieć 

się, jaka była ostatnia wartość takiej kolumny posługując się funkcją 

LAST_INSERT_ID()

.  

Jeśli posługujemy się wrażeniem 

INSERT ... VALUES

 z wieloma listami 

wartości, albo kiedy posługujemy się wyrażeniem postaci 

INSERT ... SELECT

 

wtedy baza zwróci nam informację mniej więcej następującej postaci:  

Records: 100 Duplicates: 0 Warnings: 0

  

Records

 wskazuje na liczbę analizowanych rekordół (to niekoniecznie musi być 

liczba wstawionych rekordów). 

Duplicates

 wskazuje na liczbę rekordów, które nie 

mogły być wstawione ze względu na duplikację wartości unikalnych. 

Warnings

 

wskazuje na liczbę prób wstawienia wartości do kolumn, które z jakichś powodów 
były problematyczne. 

Warnings

 mogą się zdarzyć w następujących przypadkach:  

Kiedy chcemy wstawić wartość 

NULL

 do kolumny, która była zadeklarowana 

jako 

NOT NULL

. W przypadku polecenia 

INSERT

 wielowierszowego lub 

polecenia 

INSERT ... SELECT

 wartość dla tej kolumny jest ustawiana na 

podstawie wartości domyślnej dla tej kolumny, a zatem 0 dla typów 
numerycznych, pusty string '' dla typów znakowych, oraz ``zero'' dla kolumn 
typu data i czas.  

Kiedy chcemy wstawić wartość, która przekracza dopusczalny zakres wartości 
dla danej kolumny. Następuje wtedy obcięcie do najbliższej z możliwych 
wartości. Dotyczy to zarówno wartości liczbowych, jak i znakowych.  

Kiedy chcemy wstawić wartość zupełnie innego typu, niż wynika to z definicji 
typu kolumny, np. kiedy chcemy wstawić 

'10.34 a'

 do kolumny 

numerycznej. Ogn tego strinu (nienumeryczny) jest odciety i dostaje się do 

background image

bazy tylko część numeryczna. Jeśli string nie miałby w sobie nic z cyfry, wtedy 
wstawiona by była wartość 0.  

Kiedy chcemy wstawić wartość do kolumny typu 

date

 lub 

time

, która jest 

nieodpowiednia dla tego typu kolumny. Wtenczas wartość kolumny jest 
ustawiana domyślnie jako odpowiednie ``zero''.  

 

• 

UPDATE 

Składnia polecenia 

UPDATE

 zależy od tego, w ilu tablicach dokonujemy zmiany 

danych. I tak:  

w przypadku jednej tablicy:  

   

 

UPDATE

 

  [LOW_PRIORITY] [IGNORE] nazwa_tab

 

  SET nazwa_kol1=wyr1 [, nazwa_kol2=wyr2 ...]

 

  [WHERE definicja_where]

 

  [ORDER BY ...]

 

  [LIMIT liczba_wierszy] 

a w przypadku wielu tablic:  

   

 

UPDATE

 

  [LOW_PRIORITY] [IGNORE] nazwa_tab1 [, nazwa_tab2 ...]

 

  SET nazwa_kol1=wyr1 [, nazwa_kol2=wyr2 ...]

 

  [WHERE definicja_where] 

Za pomocą polecenia 

UPDATE

 możemy zmienić zawartość kolumn w istniejących 

wierszach tablicy. Słowo 

SET

 wskazuje, które kolumny będziemy zmieniać oraz co 

będzie ich nową zawartością. Jeśli w poleceniu 

UPDATE

 wpiszemy słowo 

WHERE

 to 

możemy określić, w których wierszach dokonamy zmian, przy czym obowiązują tutaj 
zwyczajne zasady wyboru odpowiednich wierszy, tak jak w innych poleceniach SQL, 
a w szczególności w przypadku polecenia 

SELECT

. Jeśli nie podamy opcji 

WHERE

wtedy zostaną zmienione wszystkie wiersze (uwaga! niebezpieczne). Podobnie działa 
opcja 

LIMIT

; ogranicza liczbę wierszy, które ulegną zmianie podczas wykonywania 

polecenia 

UPDATE

.  

ƒ

 

W przypadku słowa 

UPDATE

 możemy używać następujących dwóch 

parametrów:  

1. parametr 

LOW_PRIORITY

 powoduje opóźnienie wykonania polecenia 

UPDATE

 do momentu zakończenia wykonywania przez bazę 

wszystkich innych poleceń pobierania danych z danej tablicy.  

2. parametr 

IGNORE

 powoduje nieprzerywanie polecenia 

UPDATE

 w 

przypadku, kiedy wystąpią błędy podczas zmiany wartości kolumn 
(tzw. błędy duplikatu klucza). W takim wypadku wiersze, dla których 
występują konflikty, nie są zmieniane.  

ƒ

 

Polecenie 

UPDATE

 zapamiętuje bierzącą (przed zmianą) wartość danej 

kolumny. Na przykład, jeśli chcemy zwiększyć wartość kolumny o 2 to 
możemy wykonać następujące polecenie:  

background image

   

 

mysql> UPDATE dane_personalne SET wiek=wiek+1;  

ƒ

 

Przypisania (po słowie 

SET

 są wykonywane w poleceniu 

UPDATE

 od lewej do 

prawej. W przykłaszie poniżej najpierw następuje pomnożenie wartości 
kolumny przez 2, a dopiero później zwiększenie wartości kolumny o 1:  

   

 

mysql> UPDATE dane_personalne SET wiek=wiek*2, wiek=wiek+1;  

ƒ

 

Jeśli próbujemy nadać kolumnie nową wartość, która jest taka sama jak 
poprzednia, to MySQL zasygnalizuje ten fakt.  

ƒ

 

Jeśli zmieniamy kolumny, które przy definicji tablicy były zadeklarowane jako 

NOT NULL

 wstawiając wartość 

NULL

, wtedy MySQL wstawia do takiej 

kolumny wartość domyślną i wypisuje ostrzeżenie. Wartościami domyślnymi 
są (chyba, że przy definicji struktury tablicy zaznaczono inaczej): 0 dla kolumn 
typu numerycznego, pusty string '' dla kolumn typu znakowego, oraz wartość 
``zero'' dla kolumn typu data i czas.  

ƒ

 

Polecenie 

UPDATE

 zwraca liczbę wmienionych rekordów (wierszy).  

ƒ

 

Zaczynając od wersji 3.23 MySQLa możemy używać opcji 

LIMIT 

liczba_wierszy

 w celu ograniczenia liczby wierszy, które zmienimy 

poleceniem 

UPDATE

. Opcja 

LIMIT liczba_wierszy

 działa 

następująco:  

0. Przed wersją 4.0.13 MySQLa polecenie 

UPDATE

 zatrzymuje się w 

momencie kiedy zostanie zmienionych 

liczba_wierszy

 wierszy, 

które spełniały warunki wymienione po opcji 

WHERE

.  

1.  Od wersji 4.0.13 MySQLa polecenie 

UPDATE

 zatrzymuje się w 

momencie kiedy zostanie znalezionych 

liczba_wierszy

 wierszy, 

które spełniały warunki wymienione po opcji 

WHERE

, bez względu na 

to, czy zostały one zmienione czy też nie zostały zmienione.  

ƒ

 

Jeśli użyjemy opcji 

ORDER BY

 wtedy wiersze będą zmieniane w kolejności 

takiej jak podano w to w tej opcji.  

ƒ

 

Od wersji 4.04 MySQLa za pomocą jednego polecenia 

UPDATE

 możemy 

zmieniać dane w kilku tablicach:  

   

 

mysql> UPDATE jednostki, miesiace SET 
jednostki.cena=miesiace.cena WHERE jednostki.id=miesiace.id; 

ƒ

 

Uwaga. Przy poleceniu 

UPDATE

 działającym na kilku tablicach, nie możemy 

używać opcji 

ORDER BY

 i 

LIMIT

.  

ƒ

 

Aby wykonać zmiany w tablicy musimy mieć odpowiednie przywileje do 
dokonywania takich zmian.  

ƒ

 

Kiedy chcemy zmienić dane w kilku tablicach typu 

InnoDB

, dla których 

zdefiniowane są klucze klucze obce, wtedy najprawdopodobniej MySQL 
wykona 

UPDATE

 w innej kolejności niż wynikałoby to z relacji rodzic/dziecko. 

W takim wypadku najprawdopodobniej polecenie 

UPDATE

 się nie powiedzie i 

dane nie zostaną zmienione. W takich wypadkach lepiej jest zmienić dane w 
jednej tablicy i polegać na możliwościach tablic typu 

InnoDB

, dzięki którym 

tablice z kluczami obcymi zostaną zmienione w sposób odpowiedni.  

 

background image

• 

REPLACE 

Składnia polecenia 

REPLACE

 jest następująca  

   

 

REPLACE

 

  [LOW_PRIORITY | DELAYED]

 

  [INTO] nazwa_tab [(nazwa_kol1,nazwa_kol2...)]

 

  VALUES ({wyr | DEFAULT},...),(...),... 

lub  

   

 

REPLACE

 

  [LOW_PRIORITY | DELAYED]

 

  [INTO] nazwa_tab

 

  SET nazwa_kol={wyr | DEFAULT}, ... 

lub  

   

 

REPLACE

 

  [LOW_PRIORITY | DELAYED]

 

  [INTO] nazwa_tab [(nazwa_kol,...)]

 

  SELECT ... 

ƒ

 

Polecenie 

REPLACE

 działa dokładnie tak samo jak 

INSERT

, z tym, że jeśli w 

tablicy 

nazwa_tab

 istnieje już rekord o takiej samej wartości 

PRIMARY 

KEY

 lub indeksu 

UNIQUE

, to poprzedni rekord jest najpierw usuwany a 

dopiero wtedy wstawiany nowy. Jest to więc polecenie, które powoduje 
bardziej bezpośrednie skutki niż polecenie 

UPDATE

. Z tego powodu jest więc 

i bardziej niebezpieczne i wygodniejsze, bowiem jeśli nie ma danego rekordu 
w tablicy a chcielibyśmy wykonać 

UPDATE

 tego rekordu, to polecenie 

UPDATE

 się nie wykona. natomiast polecenie 

REPLACE

 wykona się. Czasem 

jest to pożądane działanie a czasem nie jest. Należy umieć rozróżnić te dwa 
przypadki i wybrać odpowiednie rozwiązanie (albo 

UPDATE

 albo 

REPLACE

).  

ƒ

 

Należy zauważyć, że polecenie 

REPLACE

 nie ma sensu, jeśli tablica nie 

zawiera klucza podstawowego (

PRIMARY KEY

) lub unikalnego (

UNIQUE

). 

Wtedy po prostu polecenie 

REPLACE

 jest równoważne poleceniu 

INSERT

gdyż nie ma indeksu, na podstawie którego można określić czy nowy rekord 
zastąpi poprzedni.  

ƒ

 

Wartości wszystkich kolumn sa tak jak w przypadku polecenia 

INSERT

 

podawane w wyrażeniu. Jeśli pominiemy jakieś kolumny, to ich wartości 
zostaną nadane na podstawie wartości domyślnych zdefiniowanych podczas 
definiowania struktury tablicy.  

ƒ

 

Aby można było używać polecenia 

REPLACE

 musimy mieć prawo do 

wykonywania poleceń 

INSERT

 oraz 

DELETE

 dla danej tabeli.  

ƒ

 

Polecenie 

REPLACE

 zwraca liczbę zmienionych rekordów (usuniętych starych 

i wstawionych nowych). Jeśli ta liczba wynosi 1 (dla pojedynczego polecenia 

REPLACE

 działającego na jednym rekordzie), to oznacza to, że został 

wstyawiony jeden nowy rekord do tablicy, ale nie został usunięty żaden stary 
rekord. Jeśli natomiast ta liczba jest większa niż 1, to oznacza to, ze zostało 

background image

usuniętych 1 lub więcej nowych rekordów zostało usuniętych zanim zostały 
wstawione nowe rekordy. Inaczej mówiąc, jeśli 

REPLACE

 zwraca 1 to znaczy, 

że rekord został dodany, natomiast jeśli 

REPLACE

 zwróci więcej niż 1, to 

zostyał zamieniony.  

ƒ

 

Należy zauważyć, że może się zdażyć sytuacja, w której za pomocą jednego 
polecenia 

REPLACE

, przeznaczonego teoretycznie dla jednego rekordu, 

zostanie zastąpionych więcej niż jeden starych rekordów. Stanie się tak 
wówczas, gdy tablica zawiera wielokrotne, unikalne indeksy i nowy rekord 
duplikuje wartości dla różnych, starych rekordów z różnymi unikalnymi 
indeksami.  

ƒ

 

Algorytm stosowany przez MySQL podczas wykonywania polecenia 

REPLACE

 jest następujący:  

0. Spróbuj wstawić nowy rekord do tablicy.  
1. Jeśli próba wstawienia nowego rekordu nie powiodła się z powodu 

duplikacji klucza podstawowego lub unikalnego, to:  

1. usuń z tablicy rekord, który powoduje konflikt z powodu takiego 

samego klucza podstawowego lub unikalnego;  

2.  spróbuj ponownie wstawić nowy rekord do tablicy.  

 

• 

DO

  

Składnia polecenia 

DO

 jest następująca  

   

 

DO wyr [, wyr] ...  

Polecenie 

DO

 wykonuje po prostu wyrażenie 

wyr

, jednak nie zwraca żadnych 

wartości. Jest to właściwie swoisty skrót od 

SELECT wyr ... 

jednak ma tą 

zaletę, że jest wyraźnie szybsze. Stosowane jest wówczas gdy chcemy znacznie 
przyspieszyć wykonywanie polecenia 

SELECT

 i nie zależy nam na tym, żeby 

zobaczyć wynik. Jest to więc dość rzadki przypadek.  

Polecenie 

DO

 wykonuje się zatem wtedy, gdy posługujemy się jakimiś funkcjami, 

które posiadają tzw. efekty uboczne, tzn. wykonują pewne zadanie, i zwracają wynik, 
ale sam wynik nas nie interesuje. Na przykład funkcja 

RELEASE_LOCK()

, która 

zwalnia dostęp do bazy, kiedy wcześniej został on zamknięty funkcją 

GET_LOCK()

.  

Przykład:  

   

 

DO RELEASE_LOCK('string');  

 

• 

TRUNCATE

  

Składnia polecenia 

TRUNCATE

 jest następująca  

   

 

TRUNCATE TABLE nazwa_tablicy;  

background image

Polecenie 

TRUNCATE TABLE

 usuwa zupełnie wszystkie dane z tablicy o nazwie 

nazwa_tablicy

. Logicznie, jest to ekwiwalent polecenia 

DELETE

 usuwającego 

wszystkie rekordy, jednak są praktyczne różnice. W przypadku tablic typu 

InnoDB

 

polecenie 

TRUNCATE TABLE

 jest zamieniane na odpowiednie polecenie 

DELETE

więc w tym przypadku nie ma zupełnie różnic, jednak w innych przypadkach są 
różnice:  

ƒ

 

Operacja 

TRUNCATE

 usuwa zupełnie i tworzy od nowa tablicę co jest 

znacznie szybsze niż usuwanie rekordu po rekordzie.  

ƒ

 

Operacje 

TRUNCATE

 nie są bezpieczne w przypadku transakcji; baza na 

pewno zgłosi błąd jeśli mamy aktywną transakcję lub zamknięty dostęp do 
tablicy.  

ƒ

 

Nie jest zwracana liczba usuniętych rekordów.  

ƒ

 

Tak długo jak plik z definicją tablicy (plik '

nazwa_tablicy.frm

') jest 

nieuszkodzony, tak długo można wykonać polecenie 

TRUNCATE TABLE

 i w 

ten sposób odtworzyć pustą tabelę nawet w przypadku kiedy dane z tabeli lub 
indeksy są uszkodzone.  

ƒ

 

Uchwyt talicy, którym posługuje się wewnętrznie MyQL nie pamięta ostatnio 
użytej wartości 

AUTO_INCREMENT

 ale zaczynać odliczać od początku.  

Polecenie 

TRUNCATE TABLE

 pochodzi od rozszerzenia języka SQL, które 

wprowadziła firma Oracle.  

 

Podzapytania  

Podzapytanie jest wyrażeniem 

SELECT

 wewnątrz jakiegoś innego polecenia.  

Przykład:  

   

 

mysql> SELECT * FROM tab1 WHERE col1 = (SELECT col1 FROM 
tab2);  

W tym przykładzie 

SELECT * FROM tab1

 jest zapytaniem zewnętrznym, natomiast 

SELECT col1 FROM tab2

 jest podzapytaniem. Mówi się, że podzaptanie jest 

zagnieżdżone w zapytaniu zewnętrznym, przym możliwe jest zagnieżdżanie wielokrotne 
(głębokie). Podzapytanie musi być zawsze otoczone nawiasami okrągłymi.  

Główne zalety stosowania podzapytań:  

• 

Pozwalają na izolację części zapytań, co czyni je klarowniejszymi, i umożliwia 
oddzielenie uzyskiwania jednych informacji od drugich w jednym zapytaniu.  

• 

Dostarczają mechanizmów, dzieki którym można stosować często czytelniejsze 
metody uzyskiwania danych, które inaczej musiałyby być uzyskiwane przy pomocy 
znacznie bardziej skomplikowanych i mało czytelnych zapytań.  

• 

W opini wielu użytkowników podzapytań, podzapytania są bardziej czytelne niż 
odpowiednie polecenia zbudowane bez używania podzapytań. W rzeczy samej, to z 
powodu podzapytań nazwano język służący do operacji na bazach danych językiem 
SQL - STRUKTURALNYM JĘZYKIEM ZAPYTAŃ (ang. Structured Query Language).  

Oto przykład ilustrujący sposób używania podzapytań:  

background image

   

 

mysql> DELETE FROM t1

 

WHERE s11 > ANY

 

(SELECT COUNT(*) /* komentarz */ FROM t2

 

WHERE NOT EXISTS

 

(SELECT * FROM t3

 

WHERE ROW(5*t2.s1,77)=

 

(SELECT 50,11*s1 FROM t4 UNION SELECT 50,77 FROM

 

(SELECT * FROM t5) AS t5))); 

• 

Podzapytanie jako argument skalarny 

W najprostszym przypadku podzaptanie jest prostym argumentem. Możemy więc go 
używać wszędzie tam, gdzie tylko można użyć wartości z kolumny lub po prostu 
jakiejś wartości dosłownej. Podczas używania więc podzapytań możemy się 
spodziewać, że podzapytanie będzie miało wszystkie te właściwości, które mają 
zwykłe argumenty: typ danych, długośc, wskazanie, czy może być wartością 

NULL

 

itd.  

Na przykład:  

   

 

mysql> CREATE TABLE t1 (s1 INT, s2 CHAR(5) NOT NULL);

 

SELECT (SELECT s2 FROM t1); 

Podzapytanie w tym poleceniu 

SELECT

 jest typu 

CHAR

, ma długość 5 znaków, i 

może mieć wartość 

NULL

. W zasadzie prawie wszystkie podzapytania mogą mieć 

wartość 

NULL

, gdyż jeśli tablica jest pusta, tak jak w przykładzie powyżej, wtedy 

wartość podzapytania będzie równa 

NULL

.  

Jest kilka ograniczeń związanych z używaniem podzapytań:  

Zapytanie zewnętrzne (wyrażenie zewnętrzne) może być jednym z: 

SELECT, 

INSERT, UPDATE, DELETE, SET, DO

  

Podzapytanie może zawierac jakiekolwiek ze słów kluczowych, które może 
zwierac zwykłe polecenie 

SELECT

DISTINCT, GROUP BY, ORDER 

 i inne formy wyrażeń języka SQL.  

BY, LIMIT

• 

Porównywanie przy pomocy podzapytań 

Podzapytania używa się najczęściej w celu porównywania, a więc:  

   

 

jakis_argument   operator   (podzapytanie)  

gdzie operator może być jedenm z: 

= > < >= <= <>

  

Na przykład:  

   

 

mysql> ... 'a' = (SELECT column1 FROM t1);  

background image

Następny przykład:  

   

 

mysql> SELECT col1 FROM tab1 WHERE col1 = (SELECT MAX(col2) 

FROM t2);  

Następny przykład:  

   

 

mysql> SELECT * FROM tab1 AS t WHERE 2 = (SELECT COUNT(*) 

FROM tab1 WHERE t1.id = t.id);  

Takich zapytań (jak z dwóch ostatnich przykładów) nie da sie zapisać bezpośrednio 
inaczej niż przy pomocy podzapytań. Innym rozwiązaniem jest użycie dwóch 
osobnych zapytan.  

• 

Podzapytania z ANY, IN, oraz SOME 

Składnia:  

   

 

argument   operator_porównania   ANY   (podzapytanie)

 

argument   IN   (podzapytanie)

 

argument   operator_porównania   SOME   (podzapytanie) 

Słowo kluczowe 

ANY

, które musi wystąpić po operatorze porównującym, oznacza 

zwróć 

TRUE

 jeśli w wyniku porównania otrzymamy 

TRUE

 dla któregokolwiek z 

wierszy zwróconych przez podzapytanie,  

Na przykład:  

   

 

mysql> SELECT s1 FROM tab1 WHERE s1 > ANY (SELECT s1 FROM 

tab2);  

Przypuśćmy, że jest wiersz w tabeli 

t2

, kóry zawiera 

(10)

. Wyrażenie powyższe 

jest wtedy równe 

TRUE

, gdy tablica 

t2

 zawiera 

(21,14,7)

, gdyż w 

t2

 jest 

wartość 

7

, która jest mniejsza od 

10

. Powyższe wyrażenie jest równe 

FALSE

, gdy 

tablica 

t2

 zawiera 

(20,10)

, lub gdy tablica 

t2

 jest pusta. Wyrażenie ma wartość 

UNKNOWN

 jeśli tablica 

t2

 zawiera 

(NULL,NULL,NULL)

.  

Słowo 

IN

 jest skrótem od 

= ANY

. Zatem dwa poniższe wyrażenia są równoważne:  

   

 

mysql> SELECT s1 FROM tab1 WHERE s1 = ANY (SELECT s1 FROM 

tab2);

 

mysql> SELECT s1 FROM tab1 WHERE s1 IN (SELECT s1 FROM tab2);

Słowo 

SOME

 jest skrótem od słowa 

ANY

. Zatem dwa poniższe wyrażenia są 

równoważne:  

background image

   

 

mysql> SELECT s1 FROM tab1 WHERE s1 <> ANY (SELECT s1 FROM 

tab2);

 

mysql> SELECT s1 FROM tab1 WHERE s1 <> SOME (SELECT s1 FROM 

tab2); 

• 

Podzapytania z ALL 

Składnia:  

   

 

argument   operator_porównania   ALL   (podzapytanie)  

Słowo 

ALL

 które musi wystąpić po operatorze porównania, znaczy: zwróć prawdę 

(TRUE) jeśli porównanie jest prawdziwe dla wszystkich wierszy, które zwraca 
podzapytanie
.  

Na przykład:  

   

 

mysql> SELECT col1 FROM tab1 WHERE col1 > ALL (SELECT col1 
FROM tab2);  

Przypuśćmy, że w tablicy 

tab1

 jest wiersz zawierający wartość 

(10)

 w kolumnie 

col1

. Powyższe wyrażenie jest prawdziwe (

TRUE

) jeśli tablica 

tab2

 zawiera 

(-

5,0,+5)

, gdyż oczywiście 10 jest większe od każdej z tych trzech wartości tablicy 

tab2

. Wyrażenie to będzie nieprawdziwe (

FALSE

 jeśli tablica 

tab2

 zawiera 

(12,6,NULL,-100)

, gdyż wartość 12 z tablicy 

tab2

 jest większe od 10. 

Powyższe wyrażenie ma wartość 

UNKNOWN

 jeśli tablica 

tab2

 zawiera 

(0,NULL,1)

  

Ponadto, jeśli tablica 

tab2

 jest pusta, to wynikiem powyższego zapytania będzie 

TRUE

. Można by się spodziewać, że wynik powinien być 

UNKNOWN

, jednak w tym 

wypadku wynik jest zawsze 

TRUE

. Tak więc w poniższym przykładzie wynik jest 

równy 

TRUE

  

   

 

mysql> SELECT * FROM tab1 WHERE 1 > (SELECT col1 FROM tab2); 

Natomiast poniższe wyrażenie ma wartość 

UNKNOWN

 jeśli 

tab2

 jest pusta:  

   

 

mysql> SELECT * FROM tab1 WHERE 1 > (SELECT col1 FROM tab2); 

Ponadto, następujące wyrażenie ma wartość 

UNKNOWN

 jeśli 

tab2

 jest pusta:  

   

 

mysql> SELECT * FROM tab1 WHERE 1 > ALL (SELECT MAX(col1) 
FROM tab2);  

background image

• 

Podzapytania skorelowane 

Podzapytanie skorelowane jest podzapytaniem, które odwołuje się do tablicy, która 
występuje też w zapytaniu zewnętrznym.  

Na przykład:  

   

 

mysql> SELECT * FROM tab1 WHERE col1 = ANY 

 

(SELECT col1 FROM tab2 WHERE tab2.col2 = tab1.col2);  

Należy zauważyć, że zapytanie zawiera odwołanie do kolumn tablicy 

tab1

, pomimo 

tego, że podzapytanie po słowie 

FROM

 nie wspomina ani słowem o tablicy 

tab1

MySQL patrzy więc na zapytanie zewnętrzne i znajduje tam 

tab1

.  

Przypuśćmy, że tablica 

tab1

 zawiera wiersz gdzie 

col1

 = 5, a 

col2

 = 6, podczas 

gdy tablica 

tab2

 zawiera wiersz, w którym 

col1

 = 5 a 

col2

 = 7. Proste wyrażenie 

... WHERE col1 = ANY (SELECT col1 FROM tab2)

 mogłoby zwrócić 

TRUE

, jednak w tym przykładzie wartość wyrażenia po słowie 

WHERE

 w 

podzapytaniu jest równe 

FALSE

 (gdyż 

(5,6)

 nie jest równe 

(5,7)

), tak więc w 

powyższym przykładzie cyrażenie jako całość jest równe 

FALSE

.  

Ważna uwaga:  

MySQL analizuje wyrażenie zaczynając od wewnątrz (podzapytanie) i kończy analizę 
na zewnątrz (zapytanie zewnętrzne).  

Na przykład:  

   

 

mysql>SELECT col1 FROM tab1 AS x WHERE x.col1 = (SELECT col1 
FROM tab2 AS x WHERE x.col1 = (SELECT col1 FROM tab3 WHERE 
x.col2 = tab3.col1));  

W tym przykładzie wyrażenie 

x.col2

 musi być kolumną w tablicy 

tab2

 ponieważ 

SELECT col1 FROM tab2 AS x ...

 "zmienia nazwę" tablicy 

tab2

. Nie jest 

jednak kolumną w tablicy 

tab1

 gdyż 

SELECT col1 FROM tab1 ...

 jest 

zapytaniem zewnętrznym, które "znajduje się znacznie dalej".  

W przypadku zapytań ze słowami 

HAVING

 lub 

ORDER BY

, MySQL szuka też nazw 

kolumn w liście kolumn zapytania zewnętrznego.  

• 

EXISTS   i   NOT EXISTS 

Jeśli podzapytanie zwraca jakąkolwiek wartość, wtedy wyrażenie 

EXISTS 

podzapytanie

 zwraca wartość 

TRUE

, natomiast 

NOT EXISTS 

podzapytanie

 zwraca 

FALSE

.  

Na przykład:  

   

background image

 

mysql> SELECT col1 FROM tab1 WHERE EXISTS (SELECT * FROM 
tab2);  

W przykładzie tym, jeśli 

tab2

 zawiera jakiekolwiek wiersze, nawet wiersze, kóre nie 

zwierają nic innego poza wartością 

NULL

 wtedy warunek 

EXISTS

 jest zawsze równy 

TRUE

.  

Następne przykłady:  

ƒ

 

Jaki rodzaj sklepu występuje w jednym lub więcej miejscowościach?  

   

 

mysql> SELECT DISTINCT rodzaj_sklepu FROM sklepy

 

     WHERE EXISTS (SELECT * FROM miasta_sklepy

 

       WHERE miasta_sklepy.rodzaj_sklepu = 
sklepy.rodzaj_sklepu);  

ƒ

 

Jakiego rodzaju sklepu nie ma w żadnym mieście?  

   

 

mysql> SELECT DISTINCT rodzaj_sklepu FROM sklepy

 

     WHERE NOT EXISTS (SELECT * FROM miasta_sklepy

 

       WHERE miasta_sklepy.rodzaj_sklepu = 
sklepy.rodzaj_sklepu);  

ƒ

 

Jaki rodzaj sklepu występuje we wszystkich miastach?  

   

 

mysql> SELECT DISTINCT rodzaj_sklepu FROM Stores S1

 

     WHERE NOT EXISTS (

 

       SELECT * FROM miasta WHERE NOT EXISTS (

 

        SELECT * FROM miasta_sklepy

 

         WHERE miasta_sklepy.miasto = miasta.miasto

 

          AND miasta_sklepy.rodzaj_sklepu = 
sklepy.rodzaj_sklepu)); 

W ostatnim przykładzie mamy podwójnie zagnieżdżone zapytanie z 

NOT EXISTS

Formalnie, to zapytanie to brzmi: "czy istnieje miasto ze sklepem, którego nie ma 
wśród sklepów"? Ale można też prościej zapytać po prostu: "czy x jest prawdziwe dla 
wszystkich y"?  

• 

Podzapytania wierszowe 

Do tej pory rozważaliśmy podzapytania odnoszące się do kolumn (podzapytania 
skalarne), tzn. podzapytania, które zwracają pojedynczą wartość kolumny. 
Podzapytanie wierszowe jest takim podzapytaniem, które zwraca jeden wiersz, i tym 
samym może zwrócić więcej niż jedną wartość kolumny.  

Na przykład:  

   

background image

 

mysql> SELECT * FROM tab1 WHERE (1,2) = (SELECT col1, col2 
FROM tab2);  

albo:  

   

 

mysql> SELECT * FROM tab1 WHERE ROW(1,2) = (SELECT col1, col2 

FROM tab2);  

W obydwu przypadkach wartośc zapytania jest równa 

TRUE

 jeśli tylko tablica 

tab2

 

zawiera wiersz, w którym 

col1

 = 1, i 

col2

 = 2.  

Wyrażenie 

(1,2)

 i 

ROW(1,2)

 są czasem nazywane konstruktorami wierszowymi. 

Są też poprawne w troche innym kontekście. Na przykład, następne dwa zapytania 
są składniowo równoważne:  

   

 

mysql> SELECT * FROM tab1 WHERE (col1,col2) = (1,1);

 

mysql> SELECT * FROM tab1 WHERE col1 = 1 AND col2 = 1; 

Normalnie, konstruktorów wieszowych używa się w celu porównania z 
podzapytaniami, które zwracają wartości dówch lub więcej kolumn. Na przykład, 
wykonanie nastepującego zapytania:  

   

 

mysql> SELECT col1,col2,col3 

 

     FROM tab1

 

      WHERE (col1,col2,col3) IN

 

       (SELECT col1,col2,col3 FROM tab2); 

da nam wynik: "wszystkie wiersze, w tablicy 

tab1

 które istnieją też w tablicy 

tab2

".  

• 

Podzapytania po słowie FROM 

Składnia:  

   

 

mysql> SELECT ... FROM (podzapytanie) AS nazwa ...  

Klauzula 

AS

 jest obowiązkowa, gdyż każda tablica występująca zwykle po słowie 

FROM

 musi mieć nazwę. Wszystkie kolumny z listy kolumn podzapytania muszą mieć 

nazwy unikalne.  

Na przykład, załóżmy, że mamy następującą tablicę:  

   

 

mysql> CREATE TABLE tab1 (s1 INT, s2 CHAR(5), s3 FLOAT);

 

mysql> INSERT INTO tab1 VALUES (1,'1',1.0);

 

mysql> INSERT INTO tab1 VALUES (2,'2',2.0);

 

     WHERE sb1 > 1; 

background image

wtedy możemy skonstruować następujące zapytanie z podzapytaniem po słowie 

FROM

:  

   

 

mysql> SELECT sb1,sb2,sb3

 

     FROM (SELECT s1 AS sb1, s2 AS sb2, s3*2 AS sb3 FROM 
tab1) AS sb

 

     WHERE sb1 > 1; 

Wynik jest następujący:  

Result: 2, '2', 4.0.

  

Następny przykład:  

Załóżmy, że chcemy znać średnią zestawu sum z kolumny zgrupowanej tablicy. 
Następujące zapytanie nie zadziała:  

   

 

mysql> SELECT AVG(SUM(col)) FROM tab1 GROUP BY col1; 

Natomiast można uzyskac odpowiedni wynik, posługując się podzapytaniem:  

   

 

mysql> SELECT AVG(sum_col1)

 

     FROM (SELECT SUM(col) AS sum_col1

 

     FROM tab1 GROUP BY col1) AS tab1; 

Zauważmy, że nazwa kolumny użyta w podzapytaniu (

sum_column1

) jest użyta w 

zapytaniu zewnętrznym.  

• 

Optymalizacja podzapytań 

Trochę praktycznych porad w celu pisania szybszych zapytań z podzapytaniami.  

ƒ

 

Zamiast połączeń (ang. join) lepiej używać podzapytań:  

   

 

mysql> SELECT DISTINCT col1 FROM tab1 WHERE tab1.col1 IN (

 

     SELECT col1 FROM tab2);  

ƒ

 

zamiast:  

   

 

mysql> SELECT DISTINCT tab1.col1 FROM tab1, tab2

 

     WHERE tab1.col1 = tab2.col1;  

ƒ

 

Należy w miarę możliwości używać klauzul, specyficznych dla podzapytań, w 
zapytaniach zewnętrznych niż wewnętrznych. Na przykład należy użyć:  

   

background image

 

mysql> SELECT * FROM tab1 WHERE s1 IN

 

     (SELECT s1 FROM tab1 UNION ALL SELECT s1 FROM tab2);  

ƒ

 

zamiast:  

   

 

mysql> SELECT * FROM tab1 WHERE s1 IN

 

     (SELECT s1 FROM tab1) OR s1 IN (SELECT s1 FROM tab2);  

ƒ

 

Albo, należy napisać raczej:  

   

 

mysql> SELECT (SELECT col1 + 5 FROM tab1) FROM tab2;  

ƒ

 

niż  

   

 

mysql> SELECT (SELECT col1 FROM tab1) + 5 FROM tab2;  

ƒ

 

Należy raczej używać podzapytań wierszowych niż skorelowanych. Na 
przykład należy raczej użyć:  

   

 

mysql> SELECT * FROM tab1

 

     WHERE (col1,col2) IN (SELECT col1,col2 FROM tab2); 

ƒ

 

niż:  

   

 

mysql> SELECT * FROM tab1

 

     WHERE EXISTS (SELECT * FROM tab2 WHERE 

tab2.col1=tab1.col1

 

     AND tab2.col2=tab1.col2); 

ƒ

 

Należy używać raczej 

NOT (a = ANY (...))

 niż 

<> ALL (...)

.  

ƒ

 

Należy używać raczej 

x = ANY (tablica zawierająca (1,2))

 niż 

x=1 OR x=2

.  

ƒ

 

Należy używać raczej 

ANY

 niż 

EXISTS

.  

Ponadto, MySQL posiada swoje wewnętrzne mechanizmy, które powodują znaczną 
optymalizację podzaptań, na przykład wykonuje tylko jeden raz podzapytania 
nieskorelowane, lub zastępuje złożone operacje porównawcze odpowiednimi 
funkcjami.