Rozdział 5
Praktyczne wprowadzenie do języka
SQL
Zadaniem tego rozdziału jest przynajmniej częściowe rozwianie aury
tajemniczości, otaczającej SQL. Mimo że SQL jest stosunkowo prostym językiem,
proces jego poznawania może być bardzo zniechęcający, szczególnie w przypadku
osób, które nigdy wcześniej nie miały z nim kontaktu. Powodem jest powszechnie
spotykany sposób prezentacji języka SQL w
książkach i
materiałach
szkoleniowych. W ramach jednej sesji omawiane są wszystkie elementy składni
każdego z poleceń, a całość okraszona jest sporą dawką teorii relacyjnych baz
danych. Czytelnik musi samodzielnie posortować wszystkie informacje i wybrać te
rzeczywiście istotne.
Niniejszy rozdział skonstruowany jest inaczej. Czytelnik prowadzony będzie krok
po kroku przez proces tworzenia obiektów bazy danych, dodawania wierszy tabel
i formułowania zapytań. Omawiany rozdział ma jak najszybciej i jak najprościej
dostarczyć podstawowych informacji o języku SQL. Zwięzłość nie oznacza jednak
braku ścisłości, a jedynie pominięcie mniej istotnych zagadnień.
Przewodnik po języku SQL podzielony został na dwie części. Z pierwszą częścią,
zatytułowaną „Szybki start”, można zapoznać się w trakcie jednej sesji. W drugiej
części znaleźć można rozwinięcie pewnych zagadnień, poruszonych w części
pierwszej. Jednak również druga część dotyczy wyłącznie zagadnień
podstawowych. Zaawansowane możliwości języka SQL omówiono w rozdziale 24.
UWAGA:
Od Czytelnika nie oczekuje się wcześniejszej znajomości języka SQL. Czytelnicy,
którzy znają już podstawy tego języka i dysponują pewnym doświadczeniem,
mogą pominąć niniejszy rozdział i od razu przejść do Rozdziału 6, „Projektowanie
baz danych w modelu klient-serwer”.
116
Część I
UWAGA:
Zakładamy, że Czytelnik dysponuje serwerem baz danych SQL i mechanizmem
przesyłania poleceń SQL do tego serwera. W
przykładach, zawartych
w niniejszym rozdziale, stosowany jest serwer Local InterBase, dostarczany
w pakiecie Delphi Client/Server. Zamiast niego można jednak użyć dowolnego
serwera, w miarę możliwości zgodnego ze specyfikacją SQL ANSI ’92.
Szybki start
Języka SQL najlepiej uczyć się na przykładach i przy pomocy praktycznych
ćwiczeń. Zapraszamy zatem do nauki.
UWAGA:
Wielokrotnie w tym rozdziale podnoszony jest problem różnic i niezgodności
między dwiema rodzinami dialektów SQL - rodziną Sybase i ANSI. Sybase SQL
Server i jego kuzyn, opracowany na podstawie licencji - Microsoft SQL Server -
korzystają z dialektu, którego składnia pod wieloma względami istotnie różni się
od standardu ANSI. Z kolei InterBase, Oracle i większość pozostałych systemów
zarządzania bazami danych zachowuje daleko posuniętą zgodność ze standardem
ANSI SQL, przy czym każdy z producentów uzupełnia dialekt własnymi
rozszerzeniami. Przykłady, przytaczane w tym rozdziale, zapisane są w dialekcie
InterBase, a wiele z nich nie będzie działać na serwerze SQL Server bez
odpowiednich modyfikacji. Należy także pamiętać, że ilekroć omawiany jest
dialekt Sybase, można przyjąć, że ta sama składnia obowiązywać będzie
w przypadku serwera Microsoft SQL Server. Analogicznie, ilekroć omawiana jest
składnia Interbase, można bezpiecznie założyć, że będzie ona również
obowiązywać w przypadku systemów innych producentów, zgodnych z ANSI,
w tym w systemie Oracle.
Wybór edytora SQL
Jedną z pierwszych decyzji adepta języka SQL musi być wybór odpowiedniego
edytora. Edytor SQL jest narzędziem, służącym do wprowadzania i uruchamiania
poleceń SQL. We wszystkich przykładach w niniejszym rozdziale stosowany jest
edytor Windows Interactive SQL (WISQL), dostarczany razem z serwerem Local
Interbase. Proponujemy korzystanie z tego narzędzia w początkowym okresie
zapoznawania się z językiem. Użytkownicy serwera Sybase SQL mogą skorzystać
z dołączonego do niego edytora ISQL. Z kolei użytkownicy Oracle mogą stosować
SQL*Plus. Dostępne są także edytory niezależnych producentów, takie jak
Desktop DBA firmy Datura albo DB Artisan firmy Embarcadero. W każdym
Rozdział 5 Praktyczne wprowadzenie do języka SQL
117
przypadku stosowane narzędzie musi wysyłać polecenia SQL do serwera oraz
wyświetlać wszelkie odpowiedzi z serwera i wyniki.
Terminatory SQL
W języku SQL występują dwa rodzaje terminatorów: terminatory poleceń
i terminatory wsadowe. W niektórych dialektach SQL wymagane jest kończenie
każdego polecenia terminatorem. Na przykład InterBase i Oracle wymagają, aby
każde polecenie kończyło się średnikiem (;). Terminatory poleceń są z reguły
związane z
konkretnym dialektem SQL, a
nie z
narzędziem przesyłającym
polecenia do serwera.
Z kolei terminatory wsadowe kończą ciąg poleceń SQL. Wystąpienie takiego
terminatora powoduje przesłanie ciągu poleceń do serwera, w
celu ich
przetworzenia. Formę terminatora wsadowego narzuca często narzędzie, a nie sam
dialekt SQL. Na przykład, edytor ISQL z pakietu Sybase wymaga, aby ciąg
poleceń SQL kończył się słowem kluczowym
GO
(istnieje możliwość zmiany
postaci terminatora), mimo że słowo
GO
nie należy do dialektu Transact-SQL
firmy Sybase, a po faktycznym przesłaniu go do serwera wystąpiłby błąd.
W edytorze WISQL, stosowanym we wszystkich przykładach w niniejszym
rozdziale, terminator w postaci średnika jest opcjonalny. Polecenia, wpisywane
w WISQL, będą wykonywane niezależnie od tego, czy kończą się średnikiem, czy
też nie.
UWAGA:
Definiując i kompilując procedury pamiętane i inne elementy programu na
platformie InterBase należy pamiętać, że komendy SQL, zapisane w ramach
polecenia definiującego, muszą kończyć się terminatorem - niezależnie od
stosowanego edytora. Niektóre polecenia SQL, w tym polecenie
CREATE
PROCEDURE
, same składają się z szeregu poleceń, które nie powinny być
wykonywane na etapie tworzenia procedury w edytorze SQL. Dlatego na czas
tworzenia procedury należy czasowo zmienić domyślny terminator edytora,
korzystając z
polecenia
SET TERMINATOR
. Zagadnienie to omówiono
szczegółowo w rozdziale 24.
Zakładanie bazy danych
Do praktycznego wypróbowania przykładów z dalszej części niniejszego rozdziału
niezbędna jest baza danych, w której będzie można utworzyć kilka tymczasowych
tabel. Założenie takiej bazy danych nie jest trudnym zadaniem. W języku SQL do
tego celu służy polecenie
CREATE DATABASE
. Szczegóły składni tego polecenia
118
Część I
różnią się w systemach różnych producentów. Oto przykładowy zapis w dialekcie
InterBase:
CREATE DATABASE "C:\DATA\IB\ORDENT.GDB"
USER "SYSDBA" PASSWORD "masterkey";
Aby utworzyć bazę danych, należy wpisać powyższe polecenie w edytorze
InterBase ISQL. Można również skorzystać z opcji
Create Database
z menu
File
narzędzia WISQL. Oto szczegółowa procedura postępowania:
1. Uruchomić program Windows Interactive SQL (WISQL) - powinien on
znajdować się w folderze programu Delphi.
2. Wybrać opcję
Create Database
z menu
File
programu WISQL.
3. Na ekranie pojawi się okno dialogowe
Create Database
. W polu
Database
należy wpisać
C:\DATA\IB\ORDENT
(w miejsce
C:\DATA\IB
należy
wpisać poprawną ścieżkę dostępu). Oczywiście wskazane jest wcześniejsze
utworzenie katalogu, w którym przechowywane będą obiekty bazy danych.
4. W odpowiednich polach okna dialogowego wpisać identyfikator użytkownika
i hasło. Jeśli hasło użytkownika SYSDBA nie zostało zmienione, to powinno
ono brzmieć
masterkey
. Na razie można pozostać przy tym haśle. Przy
wprowadzaniu hasła należy pamiętać, że InterBase rozróżnia w tym przypadku
duże i małe litery.
5. Kliknąć
OK
. InterBase utworzy bazę danych i podłączy do niej użytkownika.
W przyszłości podłączanie do istniejącej już bazy danych realizowane będzie
przy pomocy polecenia
Connect
to
Database
z menu
File
.
Tworzenie tabel
Po założeniu bazy danych przystąpić można do tworzenia poszczególnych
obiektów bazy. Rozpoczniemy od utworzenia trzech tabel. Wykorzystamy do tego
polecenie SQL CREATE TABLE. Aby utworzyć pierwszą tabelę należy
w edytorze SQL wpisać i spowodować wykonanie następującego polecenia:
CREATE TABLE CUSTOMER
{
CustomerNumber int
NOT
NULL,
LastName char(30)
NOT
NULL,
FirstName char(30)
NOT
NULL,
StreetAddress char(30) NOT
NULL,
City char(20)
NOT
NULL,
State char(2)
NOT
NULL,
ZIP char(10)
NOT
NULL
}
Rozdział 5 Praktyczne wprowadzenie do języka SQL
119
Powyższe polecenie tworzy tabelę CUSTOMER (KLIENT). Podobnego polecenia
można użyć do stworzenia tabeli ORDERS (ZAMÓWIENIA).
CREATE TABLE ORDERS
{
OrderNumber int
NOT
NULL,
OrderDate date
NOT
NULL,
CustomerNumber int
NOT
NULL,
ItemNumber int
NOT
NULL,
Amount numeric(9,2)
NOT
NULL
}
Teraz pozostała jeszcze do utworzenia tabela ITEMS (TOWARY). Oto
odpowiednie polecenie SQL:
CREATE TABLE ITEMS
{
ItemNumber int
NOT
NULL,
Description char(30)
NOT
NULL,
Price numeric(9,2)
NOT
NULL
}
UWAGA:
W przypadku większości serwerów nie ma obowiązku wpisywania klauzuli
NULL/NOT NULL
. Należy jednak zwrócić uwagę, że różne serwery przyjmują
różne wartości domyślne w przypadku jej pominięcia. Na przykład InterBase
zachowuje się zgodnie ze standardem ANSI SQL i dla kolumn, dla których autor
programu nie określił preferencji, przyjmuje domyślnie klauzulę
NULL
. Z kolei
SQL Server przyjmuje jako domyślną klauzulę
NOT NULL
. Wskazane jest zatem
jawne wpisywanie klauzuli
NULL/NOT NULL
. Zasady tej nie można jednak
zastosować do serwera InterBase, który nie zezwala na wpisanie klauzuli
NULL
,
gdyż jest ona przyjmowana jako domyślna. Polecenie tworzące tabelę
i zawierające klauzulę
NULL
dla którejkolwiek z kolumn, nie zostanie przyjęte
przez serwer InterBase.
Wpisywanie danych
Do wpisywania danych do tabel (po jednym pełnym wierszu) służy polecenie SQL
INSERT
. Poniżej zapisano polecenia, dodające wiersze danych kolejno do
wszystkich trzech tabel. Wprowadzenie i
wykonanie poniższych poleceń
spowoduje dopisanie trzech wierszy do tabeli CUSTOMER:
INSERT INTO CUSTOMER
120
Część I
VALUES(1,'Doe', 'John','123
➥
Sunnylane','Anywhere','OK','73115')
INSERT INTO CUSTOMER
VALUES(2,'Doe','Jane', '123
➥
Sunnylane','Anywhere','OK','73115')
INSERT INTO CUSTOMER
VALUES(3,'Citizen','John','57 Riverside','Reo','MO','65803')
Następnie należy dodać cztery wiersze do tabeli ORDERS:
INSERT INTO ORDERS
VALUES(101,'07/07/97',1,1001,123.45)
INSERT INTO ORDERS
VALUES(102,'07/08/97',2,1002,678.90)
INSERT INTO ORDERS
VALUES(103,'07/09/97',3,1003,86753.09)
INSERT INTO ORDERS
VALUES(104,'07/10/97',1,1002,678.90)
UWAGA:
Dopisując wiersze do tabel w systemie Oracle należy zwrócić uwagę na poprawny
format daty. Domyślnie przyjmowany przez Oracle format zapisu daty jest
uzależniony od ustawień międzynarodowych systemu i
wartości parametru
inicjalizacyjnego
NLS_DATE_FORMAT
. Na serwerze autora jako domyślny
przyjmowany jest format daty
'DD-MMM-YY'
, a zatem polecenia dodające
wiersze to tabeli ORDER przyjmą postać:
INSERT INTO ORDERS
VALUES(101,'07-JUL-97',1,1001,123.45)
INSERT INTO ORDERS
VALUES(102,'08-JUL-97',2,1002,678.90)
INSERT INTO ORDERS
VALUES(103,'09-JUL-97',3,1003,86753.09)
INSERT INTO ORDERS
VALUES(104, 10-JUL-97',1,1002,678.90)
Datę, zapisaną w dowolnym formacie, można również poddać konwersji przy
pomocy funkcji Oracle
TO_DATE
(), na przykład:
INSERT INTO ORDERS
VALUES(101,TO_DATE('07/07/97','MM/DD/YY'),1,1001,123.45)
INSERT INTO ORDERS
VALUES(102, TO_DATE('07/08/97','MM/DD/YY'),2,1002,678.90)
INSERT INTO ORDERS
VALUES(103, TO_DATE('07/09/97','MM/DD/YY'),3,1003,86753.09)
Rozdział 5 Praktyczne wprowadzenie do języka SQL
121
INSERT INTO ORDERS
VALUES(104, TO_DATE('07/10/97','MM/DD/YY'),1,1002,678.90)
Wreszcie, poniższe trzy polecenia dodają wiersze do tabeli ITEMS:
INSERT INTO ITEMS
VALUES(1001,'WIDGET A',123.45)
INSERT INTO ITEMS
VALUES(1002,'WIDGET B',678.90)
INSERT INTO ITEMS
VALUES(1003,'WIDGET C',86753.09)
Należy zwrócić uwagę, że żadne z powyższych poleceń
INSERT
nie zawiera listy
pól, a jedynie listę wartości. W swojej standardowej postaci polecenie
INSERT
wpisuje wartości do wszystkich kolumn w tabeli, w kolejności, w jakiej kolumny
te występują w definicji struktury tabeli. W każdym poleceniu
INSERT
można
jednak określić listę pól:
INSERT INTO ITEMS (ItemNumber,Price)
VALUES(1001,123.45)
Kolejność pól na liście nie musi odpowiadać kolejności, w jakiej występują one
w tabeli; należy jednak pamiętać, że kolejność wartości, wymienionych
w poleceniu
INSERT
, musi odpowiadać kolejności, w jakiej w tym samym
poleceniu wymieniono pola. Oto przykład poprawnego polecenia:
INSERT INTO ITEMS (Price, ItemNumber)
VALUES(123.45,1001)
Polecenie SELECT.
Do przejrzenia zawartości każdej z tabel wykorzystać można polecenie SQL
SELECT
, w postaci
SELECT
*
FROM
tablename
(w miejsce
tablename
należy wpisać nazwę tabeli, której zawartość ma być odczytywana, np.
CUSTOMER, ORDERS albo ITEMS). Jeśli wszystkie dotychczasowe przykłady
zostały dokładnie wprowadzone, to tabele CUSTOMER i ITEMS powinny
zawierać po trzy wiersze, a tabela ORDERS - cztery.
Na rysunku 5.1 przedstawiono zawartość tabeli CUSTOMER, wyświetloną po
wykonaniu polecenia
SELECT
*
FROM
CUSTOMER
w programie WISQL.
122
Część I
Na rysunku 5.2 przedstawiono zawartość tabeli ORDERS, wyświetloną po
wykonaniu polecenia
SELECT * FROM ORDERS
.
Na rysunku 5.3 przedstawiono zawartość tabeli ITEMS, wyświetloną po
wykonaniu polecenia
SELECT * FROM ITEMS
.
Rysunek 5.1.
Zawartość tabeli
CUSTOMER
wyświetlona
w programie
WISQL.
Rysunek 5.2.
Zawartość tabeli
ORDER
wyświetlona
w programie
WISQL.
Rozdział 5 Praktyczne wprowadzenie do języka SQL
123
UWAGA:
Implementacje klauzuli
FROM
na poszczególnych platformach systemowych różnią
się od siebie. Na niektórych platformach klauzula ta jest obowiązkowa, na innych
występuje opcjonalnie. Na przykład dialekty Oracle i InterBase wymagają użycia
klauzuli
FROM
również wówczas, gdy polecenie
SELECT
nie wybiera kolumn
tabeli. Z kolei Sybase SQL Server wymaga klauzuli
FROM
tylko przy wybieraniu
kolumn tabeli. Polecenie
SELECT
, które nie dotyczy kolumn tabeli (lecz np. tylko
wyrażeń albo funkcji systemowych), nie musi zawierać klauzuli
FROM
. Klauzulę tę
można pominąć, wybierając np. funkcję systemową, taką jak
getdate
(). Z kolei
wybierając funkcję
SYSDATE
w systemie Oracle trzeba koniecznie umieścić
w poleceniu
SELECT
klauzulę
FROM
.
Polecenie
SELECT
* zwraca wszystkie kolumny tabeli. W miejsce gwiazdki
można wpisać listę nazw pól, oddzielonych przecinkami, np.:
SELECT CustomerNumber, LastName, State FROM Customer
Taki zapis pozwala wybrać pola, których zawartość ma być zwrócona. rysunek 5.4
przedstawia wynik powyższego zapytania.
Rysunek 5.3.
Zawartość tabeli
ITEMS
wyświetlona
w programie
WISQL.
124
Część I
Wyrażenia
W poleceniu
SELECT
w miejsce kolumn wpisać można także wyrażenia,
zawierające wartości i funkcje. Niektóre dialekty SQL oferują funkcje, zwracające
użyteczne informacje, a jednocześnie wcale nie odwołujące się do kolumn.
Poniższe przykładowe polecenie, przeznaczone dla systemu Sybase albo Microsoft
SQL Server, zwraca aktualną datę i czas:
SELECT getdate()
Z kolei poniższe polecenie, zapisane w dialekcie InterBase, zwraca nazwisko
każdego klienta z tabeli CUSTOMER, zapisane dużymi literami (zob. rysunek
5.5):
SELECT UPPER(LastName), First Name
FROM CUSTOMER
Rysunek 5.4.
Polecenie SELECT
z listą pól.
Rysunek 5.5.
Przykład użycia
funkcji UPPER
w poleceniu
SELECT.
Rozdział 5 Praktyczne wprowadzenie do języka SQL
125
Funkcje sumujące
Funkcje sumujące (ang. aggregate columns) wykonują obliczenia na zbiorze
danych. Należą do nich np. funkcje
COUNT
,
SUM
,
AVG
,
MIN
,
STDDEV
,
VARIANCE
i
MAX
. Poniższe przykłady ilustrują użycie niektórych z nich.
SELECT COUNT(*) FROM CUSTOMER
Powyższe polecenie zwraca liczbę rekordów w tabeli CUSTOMER.
SELECT MAX(Amount) FROM ORDERS
Z kolei powyższe polecenie zwraca wartość największego (pod względem kwoty)
zamówienia w tabeli ORDERS, a poniższy przykład - sumę kwot wszystkich
zamówień w tabeli:
SELECT SUM(Amount) FROM ORDERS
Klauzula WHERE
Klauzula SQL
WHERE
określa, które wiersze ma zwrócić polecenie
SELECT
. Oto
kilka przykładów:
SELECT * FROM CUSTOMER
WHERE State='OK'
Powyższe polecenie zwróci informacje tylko o tych klientach, którzy mieszkają
w stanie Oklahoma (
OK
) - zob. rysunek 5.6.
Kolejny przykład, zilustrowany na rysunku 5.7, zwróci informacje o klientach,
w których adresie występuje słowo „Sunny”.
SELECT LastName, StreetAddress FROM CUSTOMER
WHERE StreetAddress LIKE '%Sunny%'
Rysunek 5.6.
Ilustracja
zastosowania
klauzuli WHERE
w poleceniu
SELECT,
126
Część I
Poniższy przykład zwraca listę zamówień, których wartość przekracza 500
dolarów - zob. rysunek 5.8.
SELECT OrderNumber, OrderDate, Amount FROM ORDERS
WHERE Amount > 500
Kolejny, ostatni przykład, zwraca listę zamówień złożonych pomiędzy 8 a 9 lipca
1997 (włącznie). Rezultat przedstawiono na rysunku 5.9.
SELECT OrderNumber, OrderDate, Amount FROM ORDERS
WHERE OrderDate BETWEEN '07/08/97' AND '07/09/97'
Rysunek 5.7.
Ilustracja
zastosowania
klauzuli WHERE
ze słowem LIKE
w poleceniu
SELECT,
Rysunek 5.8.
Zamówienia,
których wartość
przekracza 500
dolarów.
Rozdział 5 Praktyczne wprowadzenie do języka SQL
127
Złączenia
Klauzula
WHERE
bywa także używana do złączenia (ang. join) dwóch tabel
i wygenerowania złożonego wynikowego zbioru danych. Aby uzyskać złączenie
tabel, należy wprowadzić dwie modyfikacje do podstawowej formy polecenia
SELECT
: w klauzuli
FROM
należy wymienić dodatkowe tabele, a w klauzuli
WHERE
- połączyć skojarzone pola (zob. rysunek 5.10). Oto przykład:
SELECT CUSTOMER.CustomerNumber, ORDERS.Amount
FROM CUSTOMER, ORDERS
WHERE CUSTOMER.CustomerNumber=ORDERS.CustomerNumber
W powyższym przykładzie zwraca uwagę obecność tabeli ORDERS w klauzuli
FROM
, a także zastosowanie znaku równości, który łączy tabele CUSTOMER
i ORDERS, kojarząc ich pola CustomerNumber. Tabelę, wymienioną po lewej
stronie znaku równości, nazywamy tabelą zewnętrzną, zaś tabela po prawej stronie
Rysunek 59.
Ilustracja
zastosowania
klauzuli WHERE
ze słowem
BETWEEN.
Rysunek 5.10.
Tabela
CUSTOMER
złączona z tabelą
ORDERS.
128
Część I
to tabela wewnętrzna. Równie powszechnie stosowane są określenia: tabela lewa
i prawa, co identyfikuje pozycję danej tabeli w złączeniu lewostronnym ( left join),
które jest najpopularniejszym rodzajem złączenia w systemach zarządzania
relacyjnymi bazami danych.
Złączenia wewnętrzne i zewnętrzne
Omawiane dotychczas złączenie znane jest pod oficjalną nazwą złączenia
wewnętrznego (ang. inner join). Wynikiem złączenia wewnętrznego są tylko te
wiersze, dla których spełniony jest warunek złączenia. W przypadku złączenia
zewnętrznego (ang. outer join) wiersze zwracane są niezależnie od tego, czy
spełniony jest dla nich warunek złączenia. Jeśli dla danego wiersza warunek nie
jest spełniony, w miejsce wartości pól z tabeli wewnętrznej podstawiana jest
wartość
NULL
.
Formy poleceń, realizujących złączenie zewnętrzne, różnią się w zależności od
rodzaju stosowanego serwera i wariantu ANSI SQL, obsługiwanego przez serwer.
Występują dwie wersje podstawowej formy takiego polecenia. W dialekcie
Sybase/Microsoft SQL Server złączenie zewnętrzne realizowane jest przez np.
takie polecenie:
SELECT CUSTOMER.CustomerNumber, ORDERS.Amount
FROM CUSTOMER, ORDERS
WHERE CUSTOMER.CustomerNumber*=ORDERS.CustomerNumber
Należy zwrócić uwagę na gwiazdkę, umieszczoną z lewej strony znaku równości.
W dialekcie SQL Server tak umiejscowiona gwiazdka oznacza lewostronne
złączenie zewnętrzne. Istnieją również prawostronne złączenia zewnętrzne - są one
jednak znacznie rzadziej stosowane. W dialekcie SQL Server prawostronne
złączenie zewnętrzne zapisuje się podobnie, jak odpowiednie złączenie
lewostronne, z tym że gwiazdka musi znajdować się po prawej stronie znaku
równości:
SELECT CUSTOMER.CustomerNumber, ORDERS.Amount
FROM CUSTOMER, ORDERS
WHERE CUSTOMER.CustomerNumber=*ORDERS.CustomerNumber
Prawostronne złączenia zewnętrzne najlepiej nadają się do wyszukiwania
"osieroconych" kluczy, które występują w tabeli po prawej stronie, ale nie w tabeli
po lewej stronie.
Polecenie złączenia w dialekcie ANSI
Polecenie lewostronnego złączenia zewnętrznego w dialekcie ANSI przyjmuje
następującą postać (zob. rysunek 5.11):
SELECT CUSTOMER.CustomerNumber, ORDERS.Amount
FROM CUSTOMER LEFT OUTER JOIN ORDERS
Rozdział 5 Praktyczne wprowadzenie do języka SQL
129
ON CUSTOMER.CustomerNumber=ORDERS.CustomerNumber
W przypadku złączeń prawostronnych zewnętrznych i
lewostronnych
wewnętrznych należy zastąpić słowo
LEFT
(lewostronne) słowem
RIGHT
(prawostronne) albo
OUTER
(zewnętrzne) słowem
INNER
(wewnętrzne).
UWAGA:
Serwer InterBase nie dopuszcza stosowania konwencji Sybase w zapisie złączeń
zewnętrznych w klauzuli
WHERE
; dozwolona jest jedynie składnia ANSI. InterBase
dopuszcza natomiast realizowanie złączeń wewnętrznych przy użyciu klauzuli
WHERE
.
Podzapytania
Podzapytanie jest to polecenie
SELECT
, zawarte w klauzuli
WHERE
innego
zapytania, a zatem definiujące zbiór danych zwracany przez zapytanie (zob.
rysunek 5.12). W ogólnym przypadku podzapytania zwracają listę elementów,
która jest następnie używana w warunku zapytania nadrzędnego. Oto przykład:
SELECT * FROM CUSTOMER
WHERE CustomerNumber IN (SELECT CustomerNumber FROM ORDERS)
Rysunek 5.11.
Złączenie
zewnętrzne tabel
CUSTOMER
i ORDERS przy
użyciu polecenia
ANSI-SQL.
130
Część I
GROUP BY
SQL jest językiem zapytań zorientowanym na przetwarzanie zbiorów, a nie
pojedynczych rekordów, dlatego jego integralnym składnikiem są polecenia
grupujące dane. Właśnie te polecenia, wraz z funkcjami sumującymi, stosowane są
w
praktyce do uzyskiwania odpowiedzi na złożone zapytania, dotyczące
zawartości bazy danych. Programistom, korzystającym dotąd z języków rodziny
dBASE i przyzwyczajonym do przetwarzania danych po jednym rekordzie,
zapytania zorientowane na przetwarzanie zbiorów wydają się początkowo
nienaturalne. W programach do obsługi bazy danych, stworzonych specjalnie dla
komputerów klasy PC, wszelkie zestawienia zbiorcze uzyskuje się w wyniku
działania pętli, przetwarzającej kolejne rekordy. W środowisku SQL problemy
tego rodzaju rozwiązuje się zupełnie inną drogą. Pojedynczymi poleceniami SQL
można rozwiązywać zadania, które w języku Xbase wymagałyby wpisania 10 lub
nawet 50 wierszy programu. Taka zwięzłość jest wynikiem umiejętnego
stosowania polecenia
SELECT
z klauzulą
GROUP BY
oraz funkcji sumujących
SQL. Poniższy przykład ilustruje użycie klauzuli
GROUP BY
:
SELECT CUSTOMER.CustomerNumber, sum(ORDERS.Amount)
➥
TotalOrders
FROM CUSTOMER, ORDERS
WHERE CUSTOMER.CustomerNumber=ORDERS.CustomerNumber
GROUP BY CUSTOMER.CustomerNumber
Powyższe zapytanie zwraca listę klientów, wraz z sumami wartości ich wszystkich
zamówień (zob. rysunek 5.13).
Rysunek 5.12.
Podzapytanie,
zawarte w klauzuli
WHERE innego
zapytania.
Rozdział 5 Praktyczne wprowadzenie do języka SQL
131
Skąd wiadomo, które pola mają znaleźć się w klauzuli
GROUP BY
? Jako regułę
należy przyjąć umieszczanie w tej klauzuli wszystkich pól z listy kolumn polecenia
SELECT
, z wyjątkiem funkcji sumujących i wartości. Jako przykład rozważymy
następujące polecenie
SELECT
:
SELECT CUSTOMER.CustomerNumber, CUSTOMER.LastName,
[ccc] CUSTOMER.State, sum(ORDERS.Amount) TotalOrders
FROM CUSTOMER, ORDERS
WHERE CUSTOMER.CustomerNumber=ORDERS.CustomerNumber
Takie polecenie należałoby uzupełnić następującą klauzulą
GROUP BY
:
GROUP BY CUSTOMER.CustomerNumber, CUSTOMER.LastName,
➥
CUSTOMER.State
Niektóre serwery, w tym Oracle i InterBase, wymuszają stosowanie powyższej
reguły - nie spróbują nawet wykonać zapytań, które ją naruszają. Inne serwery,
w tym SQL Server, podejmą próbę wykonania zapytania, jednak uzyskane wyniki
najprawdopodobniej nie będą odpowiadały oczekiwaniom. Rozważmy następujące
zapytanie:
SELECT CUSTOMER.LastName, COUNT(*) NumberWithName
FROM CUSTOMER
Można by oczekiwać, że zapytanie to zwróci listę wszystkich nazwisk (LastName)
z tabeli CUSTOMER i dla każdego nazwiska poda liczbę jego wystąpień. Wynik
będzie jednak inny. Jak już wspomniano, InterBase w ogóle nie wykona
powyższego polecenia, gdyż brakuje w nim klauzuli
GROUP BY
. Z kolei SQL
Server wykona polecenie, ale w każdym zwróconym wierszu umieści liczbę
wszystkich rekordów w tabeli, a nie tylko tych, które zawierają określone
nazwisko. Tego rodzaju błędnie skonstruowane zapytania rozpoznać można po
tym, że wynik funkcji sumującej jest identyczny we wszystkich zwróconych
wierszach. W przypadku dużych zbiorów danych czas wykonania takich zapytań
będzie bardzo długi. Przykładowe zapytanie
NumberWithName
dla każdego
Rysunek 5.13
Zapytanie
wykorzystujące
klauzulę GROUP
BY
132
Część I
wiersza wynikowego zwróciłoby całkowitą liczbę wierszy w tabeli. Poprawione
zapytanie przyjmie postać:
SELECT CUSTOMER.LastName, COUNT(*) NumberWithName
FROM CUSTOMER
GROUP BY CUSTOMER.LastName
Powyższe zapytanie zwróci listę nazwisk z tabeli CUSTOMER, a dla każdego
nazwiska poda liczbę jego wystąpień. Ilustruje to rysunek 5.14.
Having
Klauzula
HAVING
służy do ograniczania zbioru wierszy, zwracanych w wyniku
wykonania klauzuli
GROUP BY
. Między
HAVING
a
GROUP
BY
występuje
zależność podobna do tej, która wiąże klauzulę
WHERE
z poleceniem
SELECT
.
W przeciwieństwie do klauzuli
WHERE
, która operuje na wierszach tabel
podawanych w zapytaniu,
HAVING
działa na zbiorze wierszy wynikowych (zob.
rysunek 5.15).
Rysunek 5.14.
Wyniki poprawnie
skonstruowanego
zapytania
Rozdział 5 Praktyczne wprowadzenie do języka SQL
133
Prawie zawsze można uzyskać analogiczne ograniczenie zbioru wynikowego, nie
stosując przy tym klauzuli
HAVING
. W ogólnym przypadku
HAVING
oferuje
mniejszą wydajność niż klauzula
WHERE
, gdyż ogranicza dopiero pogrupowany
zbiór wynikowy;
WHERE
realizuje ograniczenie jeszcze przed pogrupowaniem
zbioru wynikowego. Oto przykład użycia klauzuli
HAVING
:
SELECT CUSTOMER.LastName, COUNT(*) NumberWithName
FROM CUSTOMER
GROUP BY CUSTOMER.LastName
HAVING CUSTOMER.LastName<>'Citizen'
W poprawnie skonstruowanym zapytaniu kryterium wyboru powinno znaleźć się
w klauzuli
WHERE
:
SELECT CUSTOMER.LastName, COUNT(*) NumberWithName
FROM CUSTOMER
WHERE CUSTOMER.LastName<>'Citizen'
GROUP BY CUSTOMER.LastName
Niektóre serwery (np. Sybase SQL Server) wykrywają niewłaściwe użycie klauzuli
HAVING
i przed wykonaniem zapytania - w procesie jego optymalizacji -
automatycznie wprowadzają korektę. W przypadku innych serwerów należy zatem
zachować ostrożność, szczególnie przy tworzeniu zapytań operujących na dużych
tabelach. W systemach, które nie wykrywają błędnego użycia klauzuli
HAVING
,
umieszczenie jej w niewłaściwym miejscu z reguły blokuje możliwość użycia
indeksów przy generowaniu wyników zapytań.
ORDER BY
Klauzula
ORDER BY
określa kolejność wierszy w zbiorze wynikowym (zob.
rysunek 5.16). Jej użycie ilustruje poniższy przykład:
SELECT LastName, State FROM CUSTOMER
ORDER BY State
Rysunek 5.15.
Zastosowanie
klauzuli HAVING
do ograniczenia
zbioru wierszy,
zwracanych przez
klauzulę GROUP
BY.
134
Część I
Oto jeszcze jeden przykład:
SELECT FirstName, LastName
FROM CUSTOMER
ORDER BY LastName
Aliasy kolumn
W niektórych prezentowanych przykładach funkcje sumujące, takie jak
COUNT()
czy
SUM(),
opatrzone były logicznymi nazwami kolumn. Etykiety takie
nazywane są aliasami kolumn; aliasy zwiększają czytelność zapytań i ich zbiorów
wynikowych.W dialekcie ANSI SQL alias kolumny umieszcza się bezpośrednio za
odpowiednią kolumną na liście pól polecenia
SELECT
. W
przykładzie
ilustrującym zastosowanie klauzuli
GROUP BY
aliasem funkcji sumującej
COUNT()
jest etykieta NumberWithName. Aliasy kolumn można definiować dla
dowolnych elementów zbioru wynikowego, zatem nie tylko dla funkcji
sumujących. W
poniższym przykładzie alias LName zastępuje w
zbiorze
wynikowym kolumnę LastName:
SELECT CUSTOMER.LastName LName, Count(*) NumberWithName
FROM CUSTOMER
GROUP BY CUSTOMER.LastName
Należy jednak pamiętać, że nie dopuszcza się stosowania aliasów w innych
częściach zapytania, np. w
klauzulach
WHERE
lub
GROUP BY
. W
tych
fragmentach polecenia
SELECT
należy stosować właściwe nazwy kolumn lub
wartości.
SQL Server oferuje alternatywną konstrukcję składniową, w której alias występuje
po lewej stronie nazwy kolumny i jest od niej oddzielony znakiem równości, tak
jak w poniższym przykładzie:
SELECT CUSTOMER.LastName NumberWithName=Count(*)
Rysunek 5.16.
Zastosowanie
klauzuli ORDER
BY.
Rozdział 5 Praktyczne wprowadzenie do języka SQL
135
FROM CUSTOMER
GROUP BY CUSTOMER.LastName
Zapis taki nie jest dozwolony w dialektach Oracle i InterBase.
Aliasy tabel
Aby uniknąć wpisywania pełnej nazwy tabeli przy każdym odwołaniu do niej
w poleceniu
SELECT
, można zdefiniować krótszą nazwę zastępczą. W tym celu
należy w klauzuli
FROM
zdefiniować alias tabeli. Alias powinien znaleźć się
bezpośrednio za właściwą nazwą tabeli. Ilustruje to poniższy przykład:
SELECT C.LastName, Count(*) NumberWithName
FROM CUSTOMER C
GROUP BY C.LastName
Należy nadmienić, że aliasu można używać już na liście pól polecenia SELECT,
mimo że definiowany jest dopiero w klauzuli FROM. Odwołania do obiektów
bazy danych są bowiem analizowane jeszcze przed wykonaniem zapytania.
Krótkie podsumowanie
Na tym kończy się pierwsza część praktycznego wprowadzenia do języka SQL.
Omówiono w
niej tworzenie baz danych, tabel i
wpisywanie danych.
Przedstawiono ponadto podstawowe mechanizmy konstruowania zapytań przy
użyciu polecenia
SQL SELECT
.
Dodatkowe komentarze
Informacje zawarte w tej sekcji stanowią uzupełnienie omówionych dotychczas
zagadnień. Znajdują się tutaj również dodatkowe informacje dla osób
rozpoczynających programowanie w języku SQL, pominięte w poprzedniej sekcji -
"Szybki start". Bardziej szczegółowe omówienie zaawansowanych elementów
języka SQL znaleźć można w rozdziale 24.
Polecenie CONNECT
W większości dialektów SQL do zmiany bieżącego "kontekstu bazy danych" służy
polecenie
CONNECT
. Zmiana kontekstu polega na podłączeniu się do określonej
bazy danych, która będzie odtąd używana. Powyższe polecenie dostępne jest na
serwerach InterBase i Oracle. Poleceniem o działaniu odwrotnym do
CONNECT
jest
DISCONNECT
.
W dialekcie SQL Server do zmiany kontekstu bazy danych służy polecenie
USE
.
W przeciwieństwie do
CONNECT
,
USE
nie pozwala na jednoczesny dostęp do
136
Część I
więcej niż jednej bazy danych. Dialekt SQL Server nie zawiera odpowiednika
polecenia
DISCONNECT
.
Polecenie UPDATE
Do zmiany danych zapisanych w tabeli służy polecenie
SQL UPDATE
. Działa ono
podobnie jak polecenie
dBASE REPLACE ALL
. Oto przykład użycia
omawianego polecenia:
UPDATE CUSTOMER
SET Zip='90210' WHERE City='Beverly Hills'
W powyższym przykładzie zmiana może - w zależności od danych - dotyczyć tylko
kilku lub jednego wiersza, za sprawą klauzuli
WHERE
. Można również
zrezygnować z klauzuli
WHERE
; zmiana dotyczyć będzie wówczas wszystkich
wierszy tabeli:
UPDATE CUSTOMER
SET State='CA'
Możliwe jest także zdefiniowanie nowej zawartości kolumny przy pomocy
zawartości innych kolumn w
tej samej tabeli albo poprzedniej wartości
modyfikowanej kolumny:
UPDATE ORDERS
SET Amount=Amount+(Amount*.07)
Dialekt Transact-SQL, używany na serwerze SQL Server, zezwala na definiowanie
nowych wartości w tabeli przy pomocy wartości z innej tabeli. Oto przykład:
UPDATE ORDERS
SET Amount=Price
FROM ORDERS, ITEMS
WHERE ORDERS.ItemNumber=ITEMS.ItemNumber
Polecenie DELETE
Polecenie
DELETE
służy do usuwania wierszy z tabel. Aby usunąć wszystkie
wiersze z tabeli, należy użyć polecenia w postaci:
DELETE FROM CUSTOMER
Niektóre serwery oferują pojedyncze, szybciej działające polecenia - podobne do
polecenia ZAP, dostępnego w języku dBASE - służące do usuwania wszystkich
wierszy ze wskazanej tabeli. W przypadku serwera SQL Server odpowiednie
polecenie ma postać:
TRUNCATE TABLE CUSTOMER
Rozdział 5 Praktyczne wprowadzenie do języka SQL
137
Polecenie
DELETE
może zawierać klauzulę
WHERE
, określającą, które wiersze
mają być usunięte. Oto odpowiedni przykład:
DELETE FROM CUSTOMER
WHERE LastName<>'Doe'
COMMIT i ROLLBACK
Grupę zmian dokonanych w bazie danych określa się mianem transakcji. Polecenie
SQL COMMIT
utrwala rezultaty wykonanej transakcji. Działanie
COMMIT
można
przyrównać do zapisania (save) bazy danych. Z kolei polecenie
ROLLBACK
anuluje wszystkie zmiany, wprowadzone przez transakcję w bazie danych;
ROLLBACK
działa jak funkcja undo. Oba powyższe polecenia wpływają wyłącznie
na zmiany dokonane od poprzedniej operacji
COMMIT
; nie można anulować
właśnie utrwalonych zmian.
Niektóre platformy systemowe, w tym SQL Server, wymagają, aby transakcja
została jawnie rozpoczęta; w przeciwnym wypadku nie będzie możliwe jej
utrwalenie ani anulowanie. Program WISQL, dostarczany z serwerem InterBase,
po uruchomieniu automatycznie rozpoczyna transakcję (wysyłając odpowiednik
polecenia InterBase
SET TRANSACTION
). Gdy użytkownik zamyka WISQL,
program pyta, czy wykonane czynności mają być utrwalone. Ponadto w dowolnej
chwili można utrwalić albo anulować zmiany, korzystając z opcji
Commit Work
i
Rollback Work
, dostępnych w menu
File.