background image

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”.  

background image

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 

background image

 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 

background image

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 

background image

 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 

background image

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) 

background image

 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. 

background image

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. 

background image

 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. 

background image

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. 

background image

 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, 

background image

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. 

background image

 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. 

background image

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 

background image

 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. 

background image

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ą 

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. 

background image

 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 

background image

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 

background image

 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. 

background image

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. 

background image

 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 

background image

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 

background image

 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.