16 rozdzial 15 EJCDLTJY3F3I2FKL Nieznany (2)

background image

Rozdział 15

Delphi na sewerze SQL Microsoft

W tym rozdziale zapoznamy się z relacyjnym systemem baz danych (ang.
RDBMS) Microsoft SQL Server. Omówiono w nim problematykę współpracy
Delphi z bazą SQL Server oraz specyficzną odmianę języka SQL, z której
korzysta.

Uruchamianie serwera

Uruchomienie i zatrzymanie systemu Microsoft SQL Server umożliwia program
SQL Server Manager, znajdujący się grupie programów lub folderze SQL Server.

Te dwa podstawowe elementy zarządzania serwerem są także realizowane poprzez
program Services z

Windows NT Control Panel. Ponieważ SQL Server

funkcjonuje jako usługa Windows NT, możemy też wykorzystać polecenie

Services

z programu Windows NT Server Manager.

Zatrzymanie serwera umożliwia także instrukcja

SHUTDOWN

języka Transact -

SQL. Wykorzystanie opcji

NOWAIT

spowoduje natychmiastowe przerwanie jego

pracy (normalnie serwer jest zatrzymywany dopiero po zakończeniu
uruchomionych procesów).

Łączenie się z serwerem

Warto odnotować, że wiele z poruszanych tutaj zagadnień odnosi się także do
Sybase SQL Server. Nie powinno to być dla nikogo zaskoczeniem, ponieważ
dopiero od wersji Microsoft SQL Server 6.0 produkty te istotnie się różnią.

Niska cena systemu Microsoft SQL Server i możliwość pracy w środowisku
Windows NT mogą stanowić zachętę do uruchamiania aplikacji
jednostanowiskowo (klient i serwer funkcjonują na tym samym komputerze).
Wiele małych przedsiębiorstw przyjmuje właśnie takie rozwiązanie. Aplikacja
wykorzystywana w taki sposób powinna być możliwie bezawaryjna. Jeśli bowiem
spowodujemy załamanie systemu, stracimy nie tylko proces klienta ale i serwera,
co może doprowadzić do uszkodzenia przetwarzanych danych. Uwaga ta odnosi
się także do każdego systemu operacyjnego i serwera SQL.

Natomiast niekwestionowaną zaletą instalacji jednostanowiskowej jest łatwość
konfigurowania i nawiązywania połączenia. Wybieramy wtedy protokół TCP/IP,

background image

450

Część III

podając - zamiast rzeczywistego adresu IP komputera - adres pętli zwrotnej (ang.
loop-back address) 127.0.0.1.

Konfigurowanie połączeń klienta

Do skonfigurowania oprogramowania klienta posłużymy się narzędziem SQL
Client Configuration Utility (powinno się ono znajdować w folderze programu
Microsoft SQL Server). Wykonanie opisanych poniżej czynności pozwoli nam
utworzyć nowe połączenie klienta:

1. Uruchamiamy oprogramowanie SQL Server Client Configuration Utility.

2. Wybieramy fiszkę

Advanced

.

3. Wpisujemy nazwę serwera do pola tekstowego

Server

. W zasadzie nazwa może

być dowolna, jednak podanie takiej, jak zdefiniowana na komputerze serwera,
ułatwi konfigurację.

4. W części Net Library Configuration wybieramy domyślny sieciowy protokół

komunikacyjny. Prawdopodobnie będzie to albo komunikacja przez nazwane
potoki (ang. Named Pipes) albo TCP/IP.

5. Jeśli wybraliśmy komunikację przez potoki, wpisujemy - do pola tekstowego

Server

- nazwę komputera, na którym pracuje serwer, i - aby dodać nowy alias

połączeniowy - klikamy

Add/Modify

, a następnie

Done

.

6. W przypadku innego protokołu komunikacyjnego, konieczne będą inne dane.

Ich rodzaj zależy od dokonanego wyboru. Np. TCP/IP wymaga adresu IP
komputera serwera oraz numeru nasłuchiwanego portu (powinny być one
oddzielone przecinkiem). Po ich wpisaniu klikamy

Add/Modify

a następnie

Done

.

Teraz potrzebujemy jeszcze aliasu klienta, który umożliwi aplikacji w Delphi
dostęp do naszego serwera SQL.

OSTRZEŻENIE:

Powinniśmy upewnić się, czy posiadamy właściwą wersję oprogramowania SQL
Client Configuration. Klient uruchomiony w 16 bitowym środowisku wymaga 16-
bitowej wersji biblioteki procedur połączeniowych, a klient 32-bitowy - 32-bitowej
wersji biblioteki. Niektóre narzędzia innych producentów do administrowania (np.
DB Artisan) są jeszcze aplikacjami 16-bitowymi. Korzystają one z 16-bitowych
wersji klienta, nawet jeśli pracujemy w

środowisku 32-bitowego systemu

operacyjnego.

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

451

Rozwiązywanie problemów połączeniowych z serwerem Microsoft SQL

Poniżej zebraliśmy kilka praktycznych porad:

1. Jeśli nasza, napisana w Delphi, aplikacja nie łączy się z serwerem, powinniśmy

spróbować nawiązać komunikację poprzez ISQL/w. Jeśli próba się powiedzie,
to prawdopodobnie źle skonfigurowaliśmy alias BDE. W takiej sytuacji
powinniśmy powrócić do programu BDE Configuration i upewnić się, czy
podane ustawienia są poprawne ( w szczególności nazwa serwera).

2. Niemożność połączenia się poprzez ISQL/w pozwala przypuszczać, że

przyczyną problemów może być protokół sieciowy. Jeśli korzystamy z TCP/IP,
wtedy połączenie z serwerem możemy sprawdzić za pomocą programu PING,
dostarczanego wraz z Windows 95 i Windows NT. Powinniśmy spróbować
łączenia poprzez nazwę komputera (zapisaną w pliku HOSTS) oraz poprzez
jego adres IP. Jeśli komunikujemy się z serwerem przez potoki, możemy
skorzystać z instrukcji

net view \\servername,

gdzie

servername

jest nazwą serwera NT, na którym realizowany jest serwer SQL. Po uzyskaniu
połączenia poprzez

c

wykonujemy instrukcję

netuse \\

servername.\IPC$.

Gdy obie zostaną wykonane poprawnie, skorzystamy

z procedur makepipe/readpipe. Uruchamiamy więc makepipe na serwerze oraz
readpipe

/Sservername

/

Dteststring

na komputerze klienta ( CTRL+C

lub CTRL+BREAK kończy realizację makepipe). Jeśli któraś z prób nie
wypadnie pomyślnie należy zwrócić się do Administratora NT. Źródłem
zakłóceń może być wadliwa obsługa protokołu potoków na serwerze.

3. Jeśli łączymy się poprzez adres IP, a nie udaje się to poprzez nazwę serwera

powinniśmy sprawdzić plik HOSTS. Tymczasowo można zmienić odwołanie
sieciowe (programem konfiguracyjnym SQL Net), zastępując w nim nazwę
komputera jego adresem IP. Z uwagi na fakt, że odwołanie do nazwy jest
wygodniejsze, powinniśmy możliwie najszybciej usunąć przyczynę
uniemożliwiającą połączenie.

4. W przypadku protokołu IPX, warto rozpocząć od sprawdzenia, czy nasz

komputer ma dostęp do serwera. Można to zrobić uruchamiając isql /L z linii
poleceń systemu operacyjnego. Isql /L wywołuje funkcję

dbserverenum

,

która wyświetla listę dostępnych serwerów. Jeśli naszego serwera nie ma na
liście, powinniśmy zwrócić się do administratora sieci (prawdopodobnie sieć
nie funkcjonuje poprawnie).

5. Jeśli korzystamy z TCP/IP i w żaden sposób nie udało się nam połączyć

z serwerem, to prawdopodobną przyczyną może być uszkodzenie sieci.
Oczywiście powinniśmy jeszcze raz sprawdzić adres IP serwera oraz uruchomić
PING, podając adres

127.0.0.1

- aby przekonać się, czy stos protokołu

TCP/IP funkcjonuje poprawnie. Jeśli test się nie powiedzie, to prawdopodobnie

background image

452

Część III

źle skonfigurowano sam protokół. W takim przypadku ponownie proponujemy
zwrócić się do administratora sieci.

6. Jeśli nie uzyskaliśmy połączenia za pomocą ISQL /w , mimo iż PING

funkcjonuje poprawnie, powinniśmy sprawdzić - czy numer portu w odwołaniu
jest zgodny z ustawionym na serwerze bazy danych. Informacji o portach
przypisanych do serwera powinien nam udzielić administrator bazy danych.

7. Jeśli wszystko wydaje się być skonfigurowane poprawnie, możemy dla próby

wykorzystać inny protokół ( jeśli oczywiście mamy taką możliwość). Serwer
pozwala na jednoczesną obsługę kilku protokołów, więc może tymczasowo
wykorzystywać tylko ten, z którym funkcjonuje poprawnie, aż do momentu
przywrócenia do właściwej pracy drugiego protokołu.

WSKAZÓWKA:

Podstawowym narzędziem diagnostycznym w środowisku Windows NT jest
Windows NT Diagnostics (z folderu Administrative Tools), umożliwiający
przeglądanie zasobów komputera. Jest to adaptowana do NT wersja popularnego
programu MSD (Microsoft Diagnostic), znanego z systemów DOS i Windows 3.x.
Plik z programem MSD nazywa się WINMSD.EXE, i znajduje się kartotece
\WINNT\SYSTEM32.

Tworzenie aliasu BDE

Pokażemy teraz jak stworzyć alias BDE, dzięki któremu nasze aplikacje w Delphi
uzyskają dostęp do serwera bazy danych. Ten temat był już omawiany wcześniej,
dlatego tutaj ograniczymy się tylko do pokazania, jak utworzyć alias DBE
w zależności od ustawień oprogramowania sterującego obsługą bazy danych.

Aliasy BDE można tworzyć w oparciu o program BDE Administrator lub Delphi
Database Explorer. Poniższy opis odnosi się do programu BDE Administrator.

1. Uruchamiamy DBE Administrator, klikamy (prawym przyciskiem myszy)

fiszkę

Databases

i wybieramy

New

. W oknie dialogowym

New

Database

Alias

wybieramy

MSSQL

z listy typów aliasów.

2. Po dodaniu nowego aliasu wpisujemy jego nazwę.

3. Konfigurujemy go, ustawiając parametry na stronie

Definition

.

4. Klikamy parametr

SERVER NAME

i ustawiamy w nim nazwę naszego serwera

SQL- tę, której użyliśmy poprzednio w programie SQL Client Configuration
Utility.

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

453

5. Dodatkowo możemy ustawić parametr

USER NAME,

podając nazwę

użytkownika, który będzie domyślnie logowany na serwerze. Będzie się ona
także ukazywała we wbudowanym w Delphi oknie dialogowym logowania.

Aby zapisać zmiany klikamy przycisk

Apply

i zamykamy sesję programu BDE

Administrator.

Specjalne ustawienia aliasu BDE

Omówione poniżej ustawienia wartości parametrów aliasu BDE mogą okazać się
istotne przy nawiązywaniu połączenia z Microsoft SQL Server. Chociaż możemy
ograniczyć się do ustawień domyślnych, to jednak w przypadku napotkania
problemów znajomość funkcji poszczególnych parametrów będzie z pewnością
bardzo przydatna. Warto zauważyć, że niektóre z

nich określają sposób

funkcjonowania programów obsługi bazy i mogą być ustawiane na stronie

Configuration

w programie BDE Administrator.

DRIVER FLAGS Setting and BLOB Handling

Czytanie lub zapis BLOB (Binary Large OBject) staje się czasami przyczyną
zawieszenia serwera Microsoft SQL. Jeśli występują tego rodzaju problemy,
proponujemy zmienić w aliasie ustawienie parametru

DRIVER FLAG

- z wartości

0 na 1. Powinno to być wystarczające dla usunięcia problemu, ale jednocześnie
może powodować naruszanie limitu czasu podczas zapisywania BLOB.

APPLICATION NAME (Nazwa aplikacji)

Wartością parametru

APPLICATION NAME

jest nazwa, pod którą nasz proces

będzie reprezentowany w tablicy procesów serwera SQL.

CONNECT TIMEOUT (Czas do zerwania połączenia)

Decyduje o tym, jak długo klient będzie ponawiał próby nawiązania połączenia
z serwerem SQL. Domyślnie przyjmowane jest 60 s. Zwiększenie tego parametru
może poprawić funkcjonowanie stacji klienckich (zwłaszcza, gdy łączymy się
przez sieć rozległą WAN).

HOST NAME (Nazwa hosta)

Parametr służy do nadania nazwy stacji roboczej , poprzez którą będzie ona
identyfikowana w tablicy procesów serwera SQL. Dzięki temu będzie można
odróżnić to połączenie od innych na różnych listach - takich jak np. tworzona
przez procedurę

sp_who

.

background image

454

Część III

NATIONAL LANG NAME ( Wybrany język)

Parametr ten definiuje język, w jakim ukazują się komunikaty o błędach. Jeśli go
nie ustalimy przyjęte zostanie ustawienie domyślne.

TDS PACKET SIZE (Rozmiar pakietów TDS)

Stosowany do ustalenia rozmiaru pakietów Tabular Data Stream (TDS) jest
protokołem pakietowym wysokiego poziomu, wykorzystywanym przez SQL
Server do wymiany danych z klientami. Chociaż w programie BDE Administrator
można określić rozmiar od 0 do 65535, to jednak należy ograniczyć się do zakresu
obsługiwanego przez SQL Server: od 512 do 32767. Może się okazać, że
zwiększenie rozmiaru pakietu spowoduje wzrost przepustowości systemu
z powodu zmniejszenia liczby pakietów wymaganych do transmisji dużych ilości
danych (np. pól BLOB). Domyślny rozmiar wynosi 4096 i najlepszą wydajność
osiąga się dla wielkości pakietów w zakresie od 4096 do 8192.

Aby ustalić maksymalny rozmiar pakietu obsługiwanego przez serwer można użyć
pamiętanej procedury serwera SQL o nazwie

sp_configure

. Powinniśmy więc

- aby otrzymać bieżący rozmiar pakietu - wykonać instrukcję

sp_configue

'network packet size'

. Należy odpowiednio ustawić ten parametr przed

zmianą odpowiadających mu ustawień w

programie DBE Administrator.

Niezgodność

TDS PACKET SIZE

w DBE Administrator i

network packet

size

w SQL Server może wywołać jeden z komunikatów błędów:

„

Error: unknown user name or password (nieznana nazwa

użytkownika lub hasło)

„

Server error-4002 Login failed (nieudane rejestracja
w systemie)

„

Server error -

20014 Login incorrect (niewłaściwa

rejestracja)

UWAGA:

Zmiana domyślnego rozmiaru pakietu jest możliwa dopiero od wersji 5 TDS

DATABASE NAME

Pozwala określić nazwę bazy danych SQL, z którą chcemy się połączyć. Zwykle
tworzy się osobny alias dla każdej wykorzystywanej bazy. Wtedy parametr

DATABASE NAME

wskazuje tę, do której odnosi się dany alias. Brak wartości

parametru

DATABASE NAME

jest równoznaczny ze wskazaniem na domyślną

bazę użytkownika. Nie polecamy jednak takiej strategii. Jednym z powodów jest

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

455

fakt, że do ustalenia bazy trzeba wtedy tworzyć zapytanie do jednej z tabel
systemowych (ang. syslogins table) na serwerze.

BLOB EDIT LOGGING (Rejestracja zmian w BLOB)

Można zablokować rejestrację zmian w polach

BLOB

, ustawiając wartość tego

parametru na

FALSE

. Minimalizujemy wówczas wymagania pamięciowe,

poprawiając wydajność. Ustawienie

FALSE

powoduje, że dane

BLOB

transmitowane są za pomocą programu narzędziowego SQL Server bulk copy .
Dlatego, jeśli zamierzamy użyć tego mechanizmu, powinniśmy ustawić opcję

set

select into/bulk copy on

w docelowej bazie - za pomocą procedury

pamiętanej

sp_dboption

.

MAX QUERY TIME

Ten parametr określa czas wyczekiwania przez SQL - do momentu zakończenia
asynchronicznego zapytania ( po upływie tego czasu jest ono usuwane). Microsoft
i Sybase SQL Links domyślnie wykonują zapytania synchronicznie. Wartość
parametru

TIMEOUT

na stronie sterownika MSSQL określa czas zakończenia

zapytania synchronicznego. Tryb asynchroniczny ustawimy zwiększając parametr

MSSQL DRIVER FLAG do 2048.

Domyślnie SQL Links ogranicza czas

zakończenia zapytania asynchronicznego do 300 s.

Wstęp do SQL

SQL Server jest nazwą produktu firmy Microsoft, i rzeczywiście komunikujemy
się z nim poprzez SQL. Powinniśmy mieć jednak na uwadze, że SQL Server jest
serwerem bazy danych, a nie serwerem SQL. W tym paragrafie zapoznamy się
językiem Transact-SQL - odmianą SQL, z której korzystają serwery SQL Server.
Zwrócimy w nim szczególną uwagę na kilka cech, które różnią tę implementację
SQL od produktów dostarczanych przez innych sprzedawców. Aby móc poznawać
w praktyce prezentowane elementy języka, wystarczy zapewnić sobie dostęp do
serwera i umieć się z nim komunikować poprzez ISQL.

Tworzenie bazy danych

Zaczniemy od utworzenia bazy danych i umieszczenia w niej tymczasowych tabel,
które posłużą do analizy podanych dalej przykładów. Skorzystamy z instrukcji

CREATE DATABASE

. Dokładna postać jej składni zależy od konkretnej

implementacji. Tutaj zaprezentujemy jej podstawową postać, rozpoznawaną przez
SQL Server:

background image

456

Część III

CREATE DATABASE dbname ON datadevice=size LOG ON
logdevice=size

Planując umieszczenie tworzonej bazy danych korzystamy z urządzeń logicznych.
Koncepcja urządzeń logicznych pozwala uniknąć odwołań do fizycznego
położenia danych na dysku. Stanowią one warstwę pośrednią pomiędzy
sterownikami dysku i bazami danych.

Definiujemy je korzystając z instrukcji

DISK INIT

. Oto prosty przykład jej

użycia:

DISK INIT
name='saltrn00'
physname='c:\mssqlsql\data\saltrn00.dat',
vdevno=5,
size = 2048

Parametr

name

określa uchwyt logiczny (ang. logical handler), który występuje

w odwołaniach do danego urządzenia.

Parametr

physname

określa szczegółowo fizyczne położenie i nazwę pliku. Jeśli

tworzymy urządzenie logiczne na surowej partycji Windows NT, podajemy tylko
literę określającą napęd.

vdevno

jest logicznym numerem urządzenia wirtualnego. Musi on być unikalny

wśród numerów urządzeń zdefiniowanych na serwerze. Może przyjmować
wartości z zakresu od 1 do 255. 0 jest zarezerwowane dla urządzenia głównego
(ang. master device).

Size

określa liczbę stron o wielkości 2K przeznaczonych dla danego urządzenia.

Np. wielkość 4M (4096K) uzyskamy podając 2048.

Analogicznie tworzymy urządzenia do rejestracji zdarzeń (ang. log devices)

DISK INIT
name='sallog00'
physname='c:\mssql\data\sallog00.dat',
vdevno=6,
size = 512

WSKAZÓWKA:

Utworzenie urządzenia poprzez DISK INIT w systemie plikowym NTFS będzie
trwało krócej niż w FAT. W tym ostatnim bowiem nowe urządzenie plikowe musi
być inicjalizowane natychmiast po utworzeniu. Dla dużych urządzeń operacja ta
jest bardzo czasochłonna. Natomiast w NTFS inicjalizacja nie jest konieczna.

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

457

Po zdefiniowaniu urządzeń możemy przystąpić do utworzenia bazy. Instrukcja

CREATE DATABASE

w

poniższym przykładzie wykorzystuje zdefiniowane

wcześniej urządzenia:

CREATE DATABASE sales ON saldat00=4 LOG ON sallog00=1

Instrukcja USE

Dzięki instrukcji

USE

możemy ustalić bieżącą (aktywną) bazę. Jej składnia jest

bardzo prosta:

USE dbname

Aktywną bazę można też wybrać z

listy rozwijalnej DB w

programie

narzędziowym ISQL. Po utworzeniu nowej bazy w ISQL klikamy listę rozwijalną
i odświeżamy ją poleceniem

Refresh

. Jeśli klikniemy listę drugi raz będzie już na

niej umieszczona nowo utworzona baza.

Tworzenie tabel

Po utworzeniu bazy czynimy ją aktywną i możemy już rozpocząć definiowanie
obiektów. Prawie każde pojęcie odnoszące się do relacyjnych baz danych może
być zaprezentowane za pomocą co najwyżej trzech tabel. Utwórzmy więc na
początek trzy tabele, dzięki którym będziemy mogli zrozumieć istotę omawianych
zagadnień. Posłużymy się poleceniem:

CREATE TABLE

. Wprowadźmy

odpowiednią instrukcję SQL, aby utworzyć tabelę CUSTOMER:

CREATE TABLE CUSTOMER
(
CustomerNumber int

NOT

NULL,

LastName Char(30)

NULL,

FirstName char(30)

NULL,

StreetAddress char(30) NULL,
City char(20)

NULL,

State char(2)

NULL,

Zip char(10)

NULL

)

Dalej utwórzmy tabelę SALE :

CREATE TABLE SALE
(
SaleNumber int

NOT

NULL,

SaleDate datetime

NULL,

CustomerNumber int

NOT

NULL,

ItemNumber int

NOT

NULL,

Amount money
)

background image

458

Część III

Po zbudowaniu tabeli SALE, pozostała do utworzenia tylko tabela ITEM:

CREATE TABLE ITEM
(
ItemNumber int

NOT

NULL,

Description char(30)

NULL,

Price money

NULL

)

Dodawanie kolumn

Instrukcja

ALTER TABLE

umożliwia dodanie kolumn do utworzonej tabeli.

Microsoft SQL Server, jako jeden z niewielu, nie daje możliwości modyfikacji ani
usuwania kolumn. Kolumnę możemy dodać w następujący sposób:

ALTER TABLE CONTACT
ADD PhoneNumber char(10) NULL

Oczywiście nie można rozszerzyć niepustej tabeli o nową kolumnę z zastrzeżeniem

NOT NULL.

Wtedy bowiem nie można byłoby odpowiednio uzupełnić

umieszczonych w niej wierszy

.

Więzy (constrains)

Więzy to mechanizmy, dzięki którym możemy ograniczyć rodzaj danych
umieszczanych w kolumnie lub powiązać je ze sobą. Pozwalają także określić
wartości domyślne dla kolumn. Definiuje się je dołączając odpowiednie instrukcje
do

CREATE TABLE

albo

ALTER TABLE

. Jednym z przykładów nakładania

więzów jest tworzenie klucza głównego:

ALTER TABLE CUSTOMER
ADD PRIMARY KEY (CustomerNumber)

W tym przykładzie zdefiniowaliśmy klucz główny dla tabeli CUSTOMER jako
pole

CustomerNumbe

r. Dzięki temu - na bazie pola

CustomerNumber

zostanie utworzony unikalny indeks. Oczywiście do zdefiniowania klucza
głównego tabeli nie można użyć kolumny, która zezwala na użycie wartości

NULL

.

Klucz obcy definiuje kolumnę w jednej tabeli, której wartości muszą znajdować
się w innej tabeli. Nie określa jednoznacznie wierszy (tak jak jest to w przypadku
klucza głównego). Musi być jednak kluczem głównym lub unikalnym w tabeli, do
której się odnosi. Dodanie klucza obcego powoduje, że SQL Server buduje wtórny
indeks bazujący na polu kluczowym. Oto odpowiedni przykład:

ALTER TABLE SALE
ADD CONSTRAINT INVALID_CUSTOMER_NUMBER FOREIGN KEY
(CustomerNumber)REFERENCES CUSTOMER

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

459

W przykładzie zdefiniowano pole

CustomerNumber

w tabeli SALE jako klucz

obcy, odnoszący się do tej samej kolumny w tabeli CUSTOMER. Nałożone więzy
powodują, że aby numer klienta ( ang customer number) mógł być wprowadzony
do tabeli SALE, musi najpierw istnieć w tabeli CUSTOMER.Także numery
używane w tabeli SALE nie mogą zostać usunięte z tabeli CUSTOMER.
Możliwość wymuszenia zależności pomiędzy dwiema tabelami przez
zadeklarowanie ich relacji w SQL nazywana jest deklaratywną spójnością
referencyjną (ang declarative referential integrity). Ten termin znaczy po prostu,
że spójność danych w tabelach związanych relacją jest zapewniana przez
zdefiniowanie (lub zadeklarowanie) tej relacji w bazie, a nie przez kod programu.

Trzeci typ więzów pozwala ustalić zakres dozwolonych wartości dla
wprowadzanych danych np.

ALTER TABLE CUSTOMER
ADD CONSTRAINT INVALID_STATE CHECK (State in('OK','AR','MO'))

Zwracamy uwagę na celowość użycia elementów negacji w

konwencji

nazewniczej dotyczącej więzów. Oprogramowanie czołowe (ang.front-end), które
zapobiega naruszeniu więzów, ma dostarczać użytkownikowi łatwą do
poprawnego zinterpretowania informację . Gdyby informacja o błędzie była
sygnowana

VALID_STATE

użytkownik mógłby się nie zorientować, co jest

przyczyną problemu. Dobrze dobrana nazwa ograniczenia, która służy jako
informacja zwrotna, będzie dla użytkownika wystarczającą wskazówką, w jaki
sposób próbowano naruszyć więzy . Dzięki temu nie musimy zastępować (swoimi
własnymi) komunikatów z wyjątków generowanych przez Delphi.

Testowanie funkcjonowania więzów

Każde więzy nałożone na bazę powinny zostać sprawdzone. Najlepszym testem
będzie próba ich naruszenia. Np. aby sprawdzić zdefiniowane powyżej więzy

INVALID_STATE,

wprowadzamy w programie ISQL następującą instrukcję:

INSERT INTO CUSTOMER (CustomerNumber,State)
VALUES(123,'CA')

Z uwagi na fakt, iż pozwalają one tylko na wprowadzenie wartości:

OK, AR

i MO,

próba umieszczenia tego wiersza w bazie powinna wywołać komunikat

błędu.

Jeśli wprowadzone więzy nie funkcjonują, należy przede wszystkim sprawdzić,
czy zostały one poprawnie zapisane w bazie oraz przeanalizować ich definicje.

background image

460

Część III

Tworzenie indeksów

Do budowania indeksów w

SQL służy instrukcja

CREATE INDEX.

Jej

podstawowa składnia wygląda następująco:

CREATE INDEX SALE02 ON SALE (SaleDate)

SALE02

jest nazwą nowego indeksu,

SALE

- tabelą, dla której budujemy indeks

a

SaleDate -

kluczem indeksu. Należy zaznaczyć, że nazwa indeksu w SQL

Server musi być unikatowa w bazie, w której jest on umieszczony.

Instrukcja

CREATE UNIQE INDEX

tworzy indeks zapewniający unikatowość

klucza, np:

CREATE UNIQE INDEX SALE01 ON SALE (SaleNumber)

Wprowadzanie danych

Instrukcja

INSERT

służy do wprowadzania danych do tabeli SQL Server'a. Każde

wystąpienie klauzuli

VALUES

w instrukcji

INSERT

umożliwia dodanie jednego

wiersza danych. Można też wprowadzić od razu kilka wierszy wybierając je
z innej tabeli. W poniższym przykładzie dodano dane do wszystkich naszych tabel.
Najpierw trzy wiersze do tabeli CUSTOMER (posłużymy się ISQL):

INSERT INTO CUSTOMER (CustomerNumber, LastName, FirstName,

StreetAddress, City, State, Zip)

VALUES(1,'Doe','John','123 Sunnylane','Anywhere',

'MO','73115')

INSERT INTO CUSTOMER (CustomerNumber, LastName, FirstName,

StreetAddress, City, State, Zip)

VALUES(2,'Doe','Jane','123 Sunnylane','Anywhere',

'MO','73115')

INSERT INTO CUSTOMER (CustomerNumber, LastName, FirstName,

StreetAddress, City, State, Zip)

VALUES(3,'Philgates','Buck','57 Riverside','Reo',

'AR','65803')

Teraz dodamy trzy wiersze do tabeli ITEM:

INSERT INTO ITEM(ItemNumber, Description, Price)
VALUES(1001,'Zoso LP',13.45)
INSERT INTO ITEM(ItemNumber, Description, Price)
VALUES(1002,'White LP',67.90)
INSERT INTO ITEM(ItemNumber, Description, Price)
VALUES(1003,'Bad Co. LP',11.45)

W końcu dodamy cztery wiersze do tabeli SALE:

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

461

INSERT INTO SALE (SaleNumber, SaleDate, CustomerNumber,

ItemNumber, Amount)

VALUES(101,'10/18/90',1,1001,13.45)
INSERT INTO SALE (SaleNumber, SaleDate, CustomerNumber,

ItemNumber, Amount)

VALUES(102,'02/27/92',2,100,67.90)
INSERT INTO SALE (SaleNumber, SaleDate, CustomerNumber,
ItemNumber, Amount)
VALUES(103,'05/20/95',3,1003,11.45)
INSERT INTO SALE (SaleNumber, SaleDate, CustomerNumber,

ItemNumber, Amount)

VALUES(104,'11/27/97',4,1004,67.90)

Warto zauważyć, że nie trzeba uwzględniać wszystkich kolumn ani ich porządku
ustalonego przy tworzeniu tabeli ale lista podanych wartości musi być zgodna pod
względem liczby elementów i ich uporządkowania z listą kolumn w instrukcji

INSERT

, np.:

INSERT INTO ITEM (Price, ItemNumber)
VALUES(13.45, 1001)

Instrukcja UPDATE

Instrukcja SQL:

UPDATE

służy do modyfikacji danych w tabeli. Jej klauzula

WHERE

umożliwia wybór modyfikowanych wierszy. Oto odpowiedni przykład:

UPDATE CUSTOMER
SET Zip='65803'
WHERE City='SpringField'

Klauzula

WHERE

w

instrukcji

UPDATE

pozwala ograniczyć liczbę

modyfikowanych wierszy nawet do jednego (zależnie od danych i warunku),
natomiast pomijając ją domyślnie zmodyfikujemy wszystkie wiersze:

UPDATE CUSTOMER
SET State='MO'

Poprawiając dane w kolumnie można korzystać z wartości innych kolumn danej
tabeli (także z tej kolumny). Załóżmy, że chcemy zwiększyć cenę każdego
produktu z tabeli ITEM o siedem procent. Modyfikację tę można wykonać
następująco:

UPDATE ITEM
SET Price=Price+(Price*.07)

background image

462

Część III

Instrukcja DELETE

Dzięki instrukcji SQL:

DELETE

mamy możliwość usuwania wierszy z tabeli. Np.

aby opróżnić tabelę CUSTOMER wystarczy napisać:

DELETE FROM CUSTOMER

Instrukcja

DELETE

może też zawierać klauzulę

WHERE,

ograniczającą zakres

usuwanych wierszy. Oto odpowiedni przykład:

DELETE FROM CUSTOMER
WHERE LastName<>'Doe'

Kontrola Transakcji

Grupa powiązanych zmian w bazie nazywa się formalnie transakcją. Własne
transakcje inicjujemy instrukcją

BEGIN TRANSACTION

. Instrukcja

COMMIT

służy do zachowania zmian dokonywanych w czasie transakcji, a

ROLLBACK

pozwala na ich wycofanie. Obie odnoszą się tylko do zmian dokonanych od chwili
wywołania ostatniej instrukcji

COMMIT

.

ROLLBACK

nie spowoduje wycofania

zmian przyjętych poprzedzającymi ją instrukcjami

COMMIT

.

UWAGA:

Oprócz zwykłych instrukcji języka DML (Data Manipulation Language) (np.

INSERT, UPDATE, DELETE)

kontrola transakcji uwzględnia także instrukcje

DDL (Data Definition Language - Język definiowania danych). Jeśli wycofa się
transakcję, w której został utworzony obiekt, to zostanie on usunięty z bazy.
Transakcji można więc użyć do warunkowego utworzenia tabeli roboczej.
Pozostanie ona w bazie pod warunkiem, że wszystkie dane zostaną do niej
poprawnie wprowadzone. Jeśli bowiem instrukcje

CREATE TABLE i INSERT

są umieszczone w tej samej transakcji i

INSERT

nie wykona się poprawnie, to

stworzona tabela zostanie automatycznie usunięta.

Instrukcja SELECT

Instrukcja SQL:

SELECT

, pobiera dane z określonych kolumn tabeli. Pozwoli

nam więc sprawdzić zawartość trzech naszych tabel. W tym celu wykonajmy
trzykrotnie instrukcję o składni

SELECT * FROM tablename

, zamieniając za

każdym razem

tablename

na nazwę odpowiedniej tabeli (CUSTOMER, SALE

oraz ITEM). Jeśli wcześniej dodaliśmy proponowane dane, to każda z powstałych
w ten sposób tabel roboczych powinna mieć co najmniej trzy wiersze.

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

463

SELECT

* zwraca całą tabelę. Jeśli gwiazdkę zastąpimy listą nazw pól

oddzielonych przecinkami to otrzymamy dane tylko z wybranych pól np.:

SELECT CustomerNumber, LastName, State FROM CUSTOMER

Wyrażenia kolumnowe

Instrukcja

SELECT

w SQL Server umożliwia podanie na liście kolumn nie tylko

samych nazw, ale także wyrażeń arytmetycznych zbudowanych z wartości kolumn,
stałych i funkcji. Podajemy taki przykład zastosowania instrukcji

SELECT

,

zwracającej rekordy z tabeli SALE z wartością sprzedaży powiększoną o $15
(opłatę za dostarczenie towaru):

SELECT SaleNumber, SaleDate, Amount+15 AmountPlusShipping
FROM SALE

Funkcje sumaryczne

Funkcje sumaryczne - takie jak np.

COUNT, SUM, AVG, MIN

oraz

MAX

-

wykonują pewne obliczenia na zbiorach danych. Podamy teraz kilka przykładów
ich użycia:

SELECT COUNT(*) FROM CUSTOMER

To zapytanie daje w wyniku liczbę klientów w pliku.

SELECT MAX(Amount) FROM SALE

To z kolei podaje największą wartość sprzedaży (w dolarach).

SELECT SUM(Amount) FROM SALE

Natomiast wynikiem tego zapytania jest całkowita wartość sprzedaży (w dolarach).

Klauzula WHERE

Klauzula SQL

WHERE

umożliwia wybranie wierszy zwracanych przez instrukcję

SELECT

. Oto przykład:

SELECT * FROM CUSTOMER
WHERE State='MO'

W wyniku otrzymujemy tylko tych klientów, którzy mieszkają w Missouri (symbol

'MO'

).

SELECT * FROM CUSTOMER
WHERE StreetAddress LIKE '%Sunny%'

Rezultatem tego zapytania będą dane klientów, u których w

polu

StreetAddress

występuje słowo

Sunny

. Należy pamiętać, że porównanie

znaków uwzględnia różnice pomiędzy wielkimi i małymi literami. Zawsze jednak

background image

464

Część III

można zastosować funkcję

UPPER

do zamiany wszystkich liter (na duże)

w kolumnie i poszukiwanym wzorcu.

Oto dalsze przykłady:

SELECT * FROM SALE
WHERE Amount>500

W wyniku otrzymujemy listę wszystkich transakcji , których kwota przekroczyła
500$.

SELECT

* FROM SALE

WHERE SaleDate BETWEEN '10/18/90' AND '05/20/95'

To zapytanie zwraca dane o wszystkich transakcjach dokonanych pomiędzy 18
października 1990 r. a 20. maja 1995 r. włącznie.

Złączenia (Joins)

Klauzula

WHERE

jest także wykorzystywana do łączenia tabel. Przy złączeniu

składnia klauzuli

WHERE

jest inna niż w podstawowej instrukcji

SELECT

. Określa

się dodatkowe tabele w klauzuli

SELECT FROM

i łączy się pola będące ze sobą

w relacji, formułując odpowiednie warunki w klauzuli

WHERE.

Dobrze ilustruje

to następujący przykład:

SELECT CUSTOMER.CustomerNumber, SALE.Amount
FROM CUSTOMER, SALE
WHERE CUSTOMER.CustomerNumber=SALE.CustomerNumber

Zauważmy, że do klauzuli

FROM

włączono tabelę SALE oraz użyto znaku

równości do złączenia tabel CUSTOMER i

SALE poprzez pole

CustomerNumber

. Tabela wymieniona po lewej stronie znaku równości

nazywana jest tabelą zewnętrzną, a po prawej - tabelą wewnętrzną. Ze względu na
ich pozycję w stosunku do znaku równości często używa się określenia prawa
i lewa. O złączeniu mówi się lewa do prawej lub lewostronne. Jest to najczęściej
używane złączenie w zapytaniach SQL.

Złączenia wewnętrzne (inner) oraz zewnętrzne (outer)

Złączenie lewostronne (wspomniane powyżej) formalnie nazywa się złączeniem
wewnętrznym. Złączenie wewnętrzne daje w wyniku tylko wiersze, dla których jest
spełniony warunek złączenia. Natomiast złączenia zewnętrzne uwzględnia też
wiersze, dla których warunek nie został spełniony. W złączeniu zewnętrznym, jeśli
nie znajdziemy pasujących wierszy w tabeli wewnętrznej, to kolumny z tabeli
wewnętrznej są dołączane z wartością

NULL

. W zależności od rodzaju złączenia

zewnętrznego - tzn. left (lewostronne) lub right (prawostronne) - wartościami

NULL

są odpowiednio uzupełniane wszystkie wiersze lewej albo prawej tabeli, dla

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

465

których nie był spełniony warunek złączenia. Oto przykład złączenia
zewnętrznego, opisany w języku Transact-SQL:

SELECT CUSTOMER.CustomerNumber, SALE.Amount
FROM CUSTOMER, SALE
WHERE CUSTOMER.CustomerNumber*=SALE.CustomerNumber

Składnia złączenia w ANSI

Oprócz własnej składni SQL Server akceptuje, przy konstruowaniu złączeń, także
składnię ANSI. ANSI definiuje specjalne operatory złączeń, umieszczane
w klauzuli

FROM

. Np. lewostronne wewnętrzne złączenie może wyglądać

następująco:

SELECT CUSTOMER.CustomerNumber, SALE.Amount
FROM CUSTOMER LEFT JOIN SALE
ON CUSTOMER.CustomerNumber=SALE.CustomerNumber

Składnię dla lewostronnego złączenia zewnętrznego przedstawiamy w poniższym
przykładzie:

SELECT CUSTOMER.CustomerNumber, SALE.Amount
FROM CUSTOMER LEFT OUTER JOIN SALE
ON CUSTOMER.CustomerNumber=SALE.CustomerNumber

Dla określenia prawostronnego złączenia zewnętrznego wystarczy po prostu
zamienić

LEFT

na

RIGHT

. Zauważmy, że prawostronne i lewostronne złączenia

wewnętrzne dają ten sam wynik i dlatego nie różnią się składniowo.

Złączenia wielopoziomowe (Multi-Tier Joins)

Złączenia wielopoziomowe korzystają z więcej niż dwóch tabel. Tabela A jest
złączana z tabelą B a ta z kolei z tabelą C. Rozważmy następujące zapytanie:

SELECT C.LastName, C.FirstName, I.Description, S.Amount
FROM CUSTOMER C,
SALE

S,

ITEM

I

WHERE C.CustomerNumber=S.CustomerNumber
and S.ItemNumber=I.ItemNumber

W tym zapytaniu

CUSTOMER

i

SALE

są złączone przez wspólny klucz- pole

CustomerNumber

a

SALE

i

ITEM

przez

ItemNumber

. W efekcie wszystkie

trzy tabele utworzą jeden zbiór wynikowy.

Samozłączenia (Self-Joins)

Oprócz złączenia z innymi tabelami, tabela może być złączana sama ze sobą. Ten
rodzaj złączenia nazywa się samozłączeniem. Rozważmy następujące zapytanie:

background image

466

Część III

SELECT S.CustomerNumber, S.Amount,
(S.Amount/SUM(S2.Amount))*100 Precentage
FROM SALE S,
SALE

S2

WHERE S.CustomerNumber=S2.CustomerNumber
GROUP BY S.CustomerNumber, S.Amount

To zapytanie podaje kwoty wszystkich zakupów dokonanych przez klientów, wraz
z rozbiciem procentowym w stosunku do całej kwoty zapłaconej przez danego
klienta. Budując takie zapytanie w ramach jednej instrukcji

SELECT

musimy

posłużyć się samozłączeniem. Najpierw tworzona jest i grupowana indywidualna
statystyka, a potem tabela SALE jest złączana ze sobą w celu uzyskania łącznej
kwoty zakupów każdego klienta. To pozwala już obliczyć odpowiednią wartość
procentową, która wystąpi w wyniku zapytania.

Złączenia z użyciem innych operatorów (Theta Joins)

W warunku złączenia oprócz najczęściej stosowanego operatora porównania " jest
równe" (=) mogą wystąpić inne - zwykle jest to operator " jest różne" (<>). Podany
przykład prezentuje takie złączenie, pokazując jednocześnie wykorzystanie
samozłączenia:

SELECT C.CustomerNumber, S.Amount, (S2.Amount) OTHERS
FROM CUSTOMER C,
SALE

S,

SALE

S2

WHERE C.CustomerNumber=S.CustomerNumber
AND C.CustomerNumber<>S2.CustomerNumber
GROUP BY C.CustomerNumber, S.Amount

Faktycznie to zapytanie zawiera dwa złączenia. Pierwsze - pomiędzy

CUSTOMER

i

SALE

- aby uzyskać kwotę zakupu dla każdego klienta . Następne zaś (theta join)

- do obliczenia sumy kwot wszystkich zakupów, które nie zostały zrobione przez
klienta. Ponieważ stosuje się dwa różne typy złączenia do tej samej tabeli, więc
zapytanie korzysta z dwóch różnych aliasów dla

SALE

.

Pełne złączenia (Full Joins)

Pełne złączenie pozwala łączyć wiersze z dwóch tabel bez klauzuli

WHERE

. Jest

ono przydatne w przypadku łączenia tabel, powiązanych relacjami. Jeśli żadna
z tabel nie zawiera wszystkich interesujących nas wierszy, to łącząc je poprzez
zewnętrzne pełne złączenie otrzymamy wszystkie wiersze. Jest to po prostu
kombinacja złączenia prawo i lewostronnego. Oto przykład:

SELECT ITEM.Description, ITEM.Price SALE.Amount
FROM SALE FULL OUTER JOIN ITEM
ON ITEM.ItemNumber=SALE.ItemNumber

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

467

Iloczyn kartezjański

Iloczyn kartezjański jest wynikiem połączenia wszystkich wierszy z jednej tabeli
ze wszystkimi wierszami innej. Zwykle taki zbiór otrzymujemy przypadkowo, gdy
opuścimy lub niewłaściwie sformułujemy warunek złączenia. Oto odpowiedni
przykład:

SELECT SALE.SaleNumber, ITEM.ItemNumber
FROM SALE, ITEM
ORDER BY SaleNumber, ItemNumber

Dla tabel z dużą ilością danych obliczanie iloczynu kartezjańskiego może
zablokować serwer na tak długo, że konieczne będzie zamknięcie połączenia albo
zatrzymanie wykonania zapytania. W niektórych systemach zatrzymanie procesu
jest możliwe tylko przez zrestartowanie komputera serwera. Należy więc unikać
tworzenia iloczynu kartezjańskiego szczególnie w przypadku, gdy pracujemy
z dużymi tabelami.

Podzapytania (Subqueries)

Podzapytanie to instrukcja

SELECT,

umieszczona w klauzuli

WHERE

innej

instrukcji

SELECT

. Ogólnie podzapytania to zapytania zwracające dane, które nie

są końcowym wynikiem, lecz będą wykorzystywane w dalszej części instrukcji
Oto przykład:

SELECT * FROM CUSTOMER
WHERE CustomerNumber IN (SELECT CustomerNumber FROM SALE)

Pewne operatory funkcjonują tylko w podzapytaniach. S¹ to

ANY, ALL, SOME,

EXISTS i SINGULAR

. Chociaż słowo kluczowe

ALL

jest także używane

w instrukcji

SELECT,

to jako operator występuje tylko w podzapytaniach.

GROUP BY

Ponieważ SQL jest językiem zapytań zorientowanym na przetwarzanie zbiorów
(set - oriented), instrukcje grupujące dane są jego integralną częścią. Często osoby
tworzące bazy danych i pracujące z innymi systemami DBMS uważają takie
podejście za nietypowe, bowiem są przyzwyczajone do procedur przetwarzających
dane wiersz po wierszu. W wielu programach działających na komputerach PC,
aby zebrać dane sumaryczne iteruje się pętlę przez całą tabelę. Strategia SQL jest
zupełnie inna. Niekiedy pojedyncza instrukcja SQL zastępuje 10 lub nawet 50 linii
kodu programu napisanego w dBase. Jest to możliwe dzięki instrukcji

SELECT

,

klauzuli

GROUP BY

oraz funkcjom sumarycznym SQL. Klauzula

GROUP BY

pozwala definiować grupy wyjściowych wierszy, do których odnoszą się funkcje
sumaryczne użyte w klauzuli

SELECT

. Następny przykład pokazuje zastosowanie

klauzuli

GROUP BY

:

background image

468

Część III

SELECT CUSTOMER.CustomerNumber, SUM(SALE.Amount) TotalSale
FROM CUSTOMER, SALE
WHERE CUSTOMER.CustomerNumber=SALE.CustomerNumber
GROUP BY CUSTOMER.CustomerNumber

Rezultatem tego zapytania jest lista wszystkich klientów, wraz z sumaryczną
wartością transakcji każdego klienta.

Oczywiście nasuwa się pytanie, które pola połączyć klauzulą

GROUP BY

. SQL

Server wymaga, aby klauzula

GROUP BY

zawierała wszystkie kolumny

wymienione na liście kolumn instrukcji

SELECT

, które nie są funkcjami

sumarycznymi. W przypadku korzystania z klauzuli

GROUP BY

na liście kolumn

instrukcji

SELECT

powinna znaleźć się choć jedna funkcja sumaryczna. Jeśli ten

warunek nie jest spełniony, SQL Server uniemożliwia zastosowanie klauzuli

GROUP BY.

HAVING

Klauzula

HAVING

w instrukcji

SELECT

służy do selekcji wierszy zwracanych

przez klauzulę

GROUP BY

. Zależność między klauzulami

GROUP BY

oraz

HAVING

przypomina relację pomiędzy instrukcją

SELECT

a klauzulą

WHERE

.

Klauzula

HAVING

działa podobnie jak klauzula

WHERE,

ale na zbiorze

wynikowym, a nie na wierszach w tabelach zapytania.

Ogólnie

HAVING

jest mniej efektywne niż

WHERE

, ponieważ selekcjonuje zbiór

wynikowy, po tym jak zostanie on zgrupowany , zaś

WHERE

czyni to najpierw. Są

jednak sytuacje, w których korzystamy z klauzuli

HAVING

. Przeanalizujmy

poniższy przykład:

SELECT CUSTOMER.LastName, COUNT(*) NumberWithName
FROM CUSTOMER
GROUP BY CUSTOMER.LastName
HAVING COUNT(*)>1

HAVING

wykorzystujemy przy selekcji wierszy otrzymywanych przez zapytanie

bazujące na funkcji sumarycznej. Zastosowanie

WHERE

jest niemożliwe, bowiem

potrzebna informacja nie istnieje aż do momentu wykonania zapytania i obliczenia
odpowiednich wartości.

ORDER BY

Klauzula ta służy do odpowiedniego posortowania zbioru wynikowego. Oto
przykład:

SELECT * FROM CUSTOMER
ORDER BY State

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

469

Bez

ORDER BY

nie ma żadnej gwarancji, że wiersze zostaną odpowiednio

posortowane. Wtedy nawet ta sama instrukcja

SELECT,

wywołana dwukrotnie,

może stworzyć zbiory wynikowe, za każdym razem uporządkowane inaczej.

Aliasy kolumn

W kilku prezentowanych wcześniej przykładach używaliśmy logicznych nazw
kolumn, w których umieszczaliśmy wyniki obliczeń funkcji sumarycznych - jak

COUNT()

czy

SUM()

. Etykiety tego typu nazywamy aliasami kolumn. Dzięki nim

zapytanie i jego wynik są czytelniejsze. W SQL Server alias kolumny umieszcza
się bezpośrednio po prawej stronie odpowiadającej mu kolumny na liście pól
instrukcji

SELECT

. Na przykład w podanym poniżej zapytaniu aliasem wartości

funkcji sumarycznej

COUNT()

jest etykieta

NumberWithName

:

SELECT CUSTOMER.LastName, COUNT(*) NumberWithName
FROM CUSTOMER
GROUP BY CUSTOMER.LastName
HAVING COUNT(*)>1

Aliasy można stosować nie tylko dla funkcji sumarycznych, lecz dla każdej
pozycji w zbiorze wynikowym, np.:

SELECT CUSTOMER.LastName LName, COUNT(*) NumberWithName
FROM CUSTOMER
GROUP BY CUSTOMER.LastName

W tym zapytaniu zastąpiliśmy w zbiorze wynikowym nazwę kolumny

LastName

przez jej alias

LName

. Aliasów nie można jednak stosować w innych częściach

zapytania, takich jak klauzula

WHERE

czy

GROUP BY

. Wtedy musimy posłużyć

się nazwą kolumny lub wartością.

Aliasy tabel

Składnia instrukcji

SELECT

umożliwia korzystanie ze skrótu, zamiast podawania

pełnej nazwy tabeli. Nazywamy go aliasem tabeli. Jest on definiowany w klauzuli

FROM

instrukcji

SELECT

i

umieszczany bezpośrednio po prawej stronie

właściwej nazwy, tak jak w poniższym przykładzie:

SELECT C.LastName, COUNT(*) NumberWithName
FROM CUSTOMER C
GROUP BY C.LastName

Może dziwić fakt, że alias występuje na liście pól instrukcji

SELECT,

czyli zanim

go zdefiniowano. Powinniśmy jednak pamiętać, że odwołanie się do obiektów
bazy danych musi nastąpić przed wykonaniem zapytania.

background image

470

Część III

Perspektywy (Views)

Perspektywa SQL składa się z instrukcji

SELECT

, którą można traktować jak

tabelę i, w

dalszej kolejności, zapytania z

innymi instrukcjami

SELECT

.

W pewnych sytuacjach może być ona parametrem instrukcji

INSERT, DELETE,

i UPDATE

. Perspektywa nie zapamiętuje jednak żadnych danych. Jest ona tylko

pewną konstrukcją logiczną. Można o niej myśleć jako o małym programie
w języku SQL, uruchamianym przy każdym zapytaniu stworzonym na jej bazie.
Jest podobna w Oracle do procedury wyboru (omówionej w następnej sekcji "
Procedury pamiętane"). Gdy wykonywane jest zapytanie posługujące się
perspektywą, optymalizator zapytań tworzy najpierw perspektywę, łączy ją
z właściwym zapytaniem i optymalizuje obie czynności jako jedno zapytanie.

Perspektywy w SQL tworzy się instrukcją

CREATE VIEW,

np.:

CREATE VIEW MOCUSTOMERS AS
SELECT *
FROM CUSTOMER
WHERE State='MO'

Perspektywa może zostać wykorzystana w zapytaniu jak zwykła tabela, np.:

SELECT * FROM MOCUSTOMERS

Mimo braku klauzuli

WHERE

w

zapytaniu, zbiór wynikowy uwzględnia

oczywiście klauzulę

WHERE,

podaną w definicji perspektywy.

Instrukcja

SELECT

tworząca perspektywę, ma prawie takie same możliwości jak

podstawowa instrukcja

SELECT

. Nie może tylko korzystać z klauzuli

ORDER BY

.

To ograniczenie odnosi się nie tylko do systemu SQL Server ale także do Sybase,
InterBase oraz Oracle.

Tworząc modyfikowalną perspektywę można ograniczyć poprawianie lub
dodawanie wierszy do wartości spełniających zadane przez nią warunki . Serwer
blokuje wtedy wszelkie zmiany wykraczające poza perspektywę. W tym celu
wystarczy - w instrukcji

CREATE VIEW

- dodać klauzulę

WITH CHECK

OPTION

, np.:

CREATE VIEW MOCUSTOMERS AS
SELECT *
FROM CUSTOMER
WHERE State='MO'
WITH CHECK OPTION

W ramach zdefiniowanej perspektywy będzie można dodawać tylko takie rekordy,
dla których

State = 'MO'

. Nie ma także możliwości zmiany wartości pola

State

.

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

471

Procedury pamiętane (Stored Procedures)

Procedury pamiętane są kompilowanymi programami SQL, przechowywanymi
wraz z innymi obiektami baz danych. Do tworzenia procedur pamiętanych służy
instrukcja

CREATE PROCEDURE

. Poniżej przedstawiamy przykład takiej

procedury dla SQL Server:

CREATE PROCEDURE listcustomers
AS
BEGIN
SELECT LastName FROM CUSTOMER
END

Dla procedury z parametrami składnia instrukcji podlega drobnej modyfikacji:

CREATE PROCEDURE listcustomersbystate (@State varchar(2),

@LastNameMask varchar(30))

AS
BEGIN
SELECT LastName FROM CUSTOMER
WHERE State= @State AND LastName LIKE @LastNameMask
END

Skrypty

Instrukcje Data Definition Language (DDL), zawierające procedury pamiętane,
warto umieszczać w specjalnych plikach zwanych skryptami. Są to zwykłe pliki
tekstowe, zawierające ciągi poleceń SQL, więc można je tworzyć korzystając z
dowolnego edytora tekstów. Warto przypomnieć, że skrypty powinny zawierać
niezbędne instrukcje

USE

oraz polecenie GO, które kończy wykonanie pliku

wsadowego. W

SQL Server można wykonać skrypt SQL. W

tym celu

w programie ISQL należy kliknąć przycisk

Load SQL Script

. Przykład

skryptu podajemy poniżej (listing 15.1). Procedura zdefiniowana w skrypcie
działa podobnie jak polecenie DIR.

listing 15.1. Skrypt SQL zawierający procedurę pamiętaną

sp_dir

use master
go
/*
* DROP PROC dbo.sp_dir
*/
IF OBJECT_ID('dbo.sp_dir') IS NOT NULL
BEGIN

DROP PROC dbo.sp_dir

PRINT '<<< DROPPED PROC dbo.sp_dir >>>'

END
go

background image

472

Część III

create procedure sp_dir @mask varchar(30) = '%', @obtype

varchar(2) = 'U', @orderby varchar(3)='/N' as

/*
Stored procedure to list object catalog information similar

to the DOS DIR command.

Takes three parameters:

@mask = pattern of object names to list (supports SQL
wildcards); defaults to all objects
@obtype = type of objects to list (supports SQL wildcards);

default to user tables

@orderby = column on which to sort listing. The following

parameters are supported:

/N = sort by Name
/R = sort by number of Rows
/S = sort by total object Size
/D = sort by Date created
/DS = sort by total Size of Data pages
/IS = sort by total Size of Index pages
/US = sort by total Size of Unused pages
/RL = sort by maximum Row Length
/O = sort by Owner

The default order is by Name.
Parameters can be specified positionally, like so:
sp_dir 'TRA%','U','/S'
or by name, like so:
sp_dir @mask='TRA%',@obtype='U',@orderby='/S'
All parameters are optional. If no parameters are specified,

the following

command is executed:
sp_dir '%','U','/N'
*/
CREATE TABLE #sp_dir(

id int NOT NULL,

name varchar(30) NOT NULL,

type char(2) NOT NULL,

date_created datetime NOT NULL,

row_count int NOT NULL,

row_len_in_bytes int NOT NULL,

total_size_in_KB int NOT NULL,

data_space_in_KB int NOT NULL,

index_space_in_KB int NOT NULL,

unused_space_in_KB int NOT NULL,

owner varchar(30) NOT NULL)

insert into #sp_dir
select o.id,
o.name,

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

473

o.type,
date_created=

o.crdate,

row_count = isnull(rows,0),

row_len_in_bytes= isnull((select sum(length) from

syscolumns where id=o.id and o.type in ('U','S')),0),

total_size_in_KB = isnull((select sum(reserved) from

sysindexes where indid in (0, 1, 255) and id =

o.id),0)*2,

data_space_in_KB = isnull(((select sum(dpages) from

sysindexes where indid < 2 and id = o.id)+

(select isnull(sum(used), 0) from sysindexes where

indid = 255 and id = o.id)),0)*2,

index_space_in_KB = isnull(((select sum(used) from

sysindexes where indid in (0, 1, 255) and id = o.id)-

((select sum(dpages) from sysindexes where indid < 2

and id = o.id)+

(select isnull(sum(used), 0) from sysindexes where

indid = 255 and id = o.id))),0)*2,

unused_space_in_KB = isnull(((select sum(reserved) from

sysindexes where indid in (0, 1, 255) and id = o.id)-

(select sum(used) from sysindexes where indid in (0, 1,

255) and id = o.id)),0)*2,

owner=

user_name(o.uid)

from sysobjects o,
sysindexes i
where o.name like @mask and o.type like @obtype
and o.id*=i.id
and i.indid<=1
select @orderby=upper(@orderby)
if @orderby = '/N'

alter table #sp_dir add constraint PK_dir primary key

clustered (name,id)

else
if @orderby = '/R'

alter table #sp_dir add constraint PK_dir primary key

clustered (row_count,name)

else
if @orderby = '/S'

alter table #sp_dir add constraint PK_dir primary key

clustered (total_size_in_KB,name)

else
if @orderby = '/D'

alter table #sp_dir add constraint PK_dir primary key

clustered (date_created,name)

else
if @orderby = '/DS'

background image

474

Część III

alter table #sp_dir add constraint PK_dir primary key

clustered (data_space_in_KB,name)

else
if @orderby = '/IS'

alter table #sp_dir add constraint PK_dir primary key

clustered (index_space_in_KB,name)

else
if @orderby = '/US'

alter table #sp_dir add constraint PK_dir primary key

clustered (unused_space_in_KB,name)

if @orderby = '/RL'

alter table #sp_dir add constraint PK_dir primary key

clustered (row_len_in_bytes,name)

else
if @orderby = '/O'

alter table #sp_dir add constraint PK_dir primary key

clustered (owner,name)

alter table #sp_dir add sequencer int identity
alter table #sp_dir drop constraint PK_dir
alter table #sp_dir add primary key clustered (sequencer)
insert into #sp_dir
(id,name,type,date_created,row_count,row_len_in_bytes,total_

size_in_KB,data_space_in_KB,index_space_in_KB,unused_space_

in_KB,owner)

select power(2.0,31)-1,'TOTAL:','NA',getdate(),row_count=

sum(row_count),row_len_in_bytes=max(row_len_in_bytes),

total_size_in_KB=sum(total_size_in_KB),date_space=

sum(data_space_in_KB),

index_space_in_KB=sum(index_space_in_KB),unused_space_in_KB=

sum(unused_space_in_KB),'NA'

from #sp_dir
select name,type,date_created, row_count, row_len_in_bytes,

total_size_in_KB, data_space_in_KB, index_space_in_KB,

unused_space_in_KB, owner
from #sp_dir
drop table #sp_dir
go
IF OBJECT_ID('dbo.sp_dir') IS NOT NULL

PRINT '<<< CREATED PROC dbo.sp_dir >>>'

ELSE

PRINT '<<< FAILED CREATING PROC dbo.sp_dir >>>'

go

Instrukcja

USE

na początku pliku określa bazę danych , w której procedura

zostanie utworzona. Natomiast polecenie

GO,

kończące wykonanie programu

wsadowego, separuje oddzielne części skryptu.

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

475

UWAGA:

Pełny tekst skryptu zapisany jest na dysku CD dołączonym do książki.

Uruchamianie procedur pamiętanych

Procedury pamiętane systemu SQL Server uruchamiamy instrukcją

EXECUTE

.

Składnia instrukcji jest następująca:

EXECUTE procedurename parameters

Można skrócić

EXECUTE

do

EXEC

, a nawet opuścić, jeśli

EXEC

jest pierwszą

instrukcją w linii. Na przykład, uruchomienie procedury

listcustomers

w ISQL może wyglądać następująco:

listcustomers

Dopuszczalne jest przekazanie parametrów według kolejności, w jakiej występują
w definicji procedury:

exec listcustomersbystate 'MO', '%'

lub przez nazwę parametru:

exec listcustomersbystate @LastName Mask='%', @State='MO'

Procedury zdarzeń (Triggers)

Procedury zdarzeń są to (podobnie jak procedury pamiętane) podprogramy SQL,
uruchamiane gdy dane z danej tabeli są wprowadzane, modyfikowane lub
usuwane. Można skojarzyć procedurę zdarzenia ze specyficzną operacją
dokonywaną na tabeli: wstawianiem wiersza, modyfikacją lub usuwaniem. Oto
odpowiedni przykład w Transact-SQL:

CREATE TRIGGER SALEDelete ON CUSTOMER FOR DELETE
AS
BEGIN

DELETE FROM SALE

WHERE CustomerNumber=(SELECT CustomerNumber FROM

deleted);
END

Ta procedura zdarzenia likwiduje transakcje danego klienta w tabeli SALE, gdy
jego rekord jest usuwany z tabeli CUSTOMER (usuwanie kaskadowe).Operacja
usuwania z tabeli uruchamia "kaskadowy" proces eliminacji odpowiednich danych
w innych tabelach połączonych z nią za pomocą wspólnego klucza.

Warto zwrócić uwagę na użycie logicznej tabeli

deleted

. Zawsze gdy

uruchamiana jest procedura zdarzenia skojarzona z

DELETE

, to z wierszy

background image

476

Część III

przeznaczonych do usunięcia tworzona jest tabela logiczna o nazwie

deleted

.

W tej procedurze wykonywane jest najpierw podzapytanie do

deleted

, które

pozwala określić najpierw wartość

CustomerNumber

dla usuwanego rekordu.

Następnie usuwane są te wiersze z

tabeli SALE, mające taką wartość

CustomerNumber

.

Gdy zostaną uruchomione procedury związane z

INSERT

albo

UPDATE

, to

analogicznie serwer tworzy tabelę logiczną o nazwie

inserted

. Istnieje ona

tylko w pamięci serwera, ale jest dostępna dla procedury zdarzenia, jak każda
normalna tabela

Kursory (Cursors)

Koncepcja kursorów jest wynikiem zastosowania nastawionej na zbiory strategii
SQL w odniesieniu do przetwarzania wierszowego. Pozwalają one pracować
z jednym wierszem w danej chwili. Ponieważ ich tworzenie i obsługa jest w BDE
zautomatyzowana, więc w zasadzie nie ma potrzeby tworzenia własnych kursorów.
Jednak niekiedy mogą one okazać się użyteczne w procedurach pamiętanych.

Są cztery podstawowe operacje wykonywane na kursorze: deklarowanie (ang. declare) ,
otwarcie (ang. open) , pobranie (ang. fetch), zamknięcie (ang. close). Można go też
wykorzystać do zmodyfikowania lub usunięcia pojedynczego wiersza tabeli.

Deklaracja kursora składa się z

instrukcji

SELECT

i

(dla kursorów

modyfikowalnych) z listy modyfikowalnych kolumn. Oto przykład:

DECLARE CUSTOMER_SELECT CURSOR
FOR SELECT * FROM CUSTOMER

Zanim uzyskamy dostęp do wierszy przy użyciu kursora, musi on być najpierw
otwarty. Aby przygotować zapytanie zapisane w definicji kursora należy użyć
instrukcji

OPEN

:

OPEN CUSTOMER_

SELECT

Samo

OPEN

nie wprowadza jednak wierszy do aplikacji klienta. Niezbędna jest

instrukcja

FETCH

:

FETCH CUSTOMER_SELECT

W ten sposób otrzymamy jeden wiersz ze zbioru wynikowego kursora. Każde
następne

FETCH

pozwala uzyskać następny wiersz w zbiorze. SQL Server

posiada tylko kursory jednokierunkowe. Aby przejść do wierszy poprzednich
należy kursor zamknąć (

CLOSE

) i ponownie otworzyć (

OPEN

).

background image

Rozdział 15 Delphi na serwerze SQL Microsoft

477

UWAGA:

Mimo że SQL Server nie obsługuje kursorów dwukierunkowych, można ich użyć
w aplikacji stworzonej w Delphi. BDE emuluje bowiem dwukierunkowy kursor na
poziomie programowym - bez względu na to, czy serwer baz danych go obsługuje.
Dlatego można przewijać bazę danych w obie strony przy użyciu obiektów

TDataSets

, takich jak

TQuery i TTable.

Wiersze zwracane przez modyfikowalny kursor mogą być korygowane za pomocą
specjalnych wersji instrukcji

UPDATE i DELETE,

jak np.:

DECLARE CUSTOMER_UPDATE CURSOR
FOR SELECT *FROM CUSTOMER
FOR UPDATE OF LastName

UWAGA:

Należy się upewnić, czy na liście kolumn klauzuli

FOR UPDATE OF

umieszczono tylko te kolumny, które mają być rzeczywiście aktualizowane.
Umieszczenie na tej liście innych pól niepotrzebnie wiąże zasoby serwera.

Aby zmodyfikować lub usunąć bieżący wiersz modyfikowalnego kursora, należy
posłużyć się wyrażeniem

WHERE CURRENT OF

cursorname

, jak to pokazano

w poniższym przykładzie:

UPDATE CUSTOMER
SET LastName="Cane"
WHERE CURRENT OF CUSTOMER_UPDATE

lub

DELETE FROM CURSOR
WHERE CURRENT OF CUSTOMER_UPDATE

Gdy kończymy pracę z kursorem należy go zamknąć (instrukcją

CLOSE)

.

Zamknięcie kursora zwalnia wszystkie zasoby systemowe, których używał. Oto
przykład:

CLOSE CUSTOMER_UPDATE

Zamknięcie kursora nie zwalnia zasobów serwera używanych przez ten kursor.
Zasoby te należy zwalniać przy pomocy instrukcji

DEALLOCATE

. Oto przykład:

DEALLOCATE CUSTOMER_UPDATE


Wyszukiwarka

Podobne podstrony:
16 rozdzial 15 zpgg3d2etikxyjv3 Nieznany
16 rozdzial 15 g5h76jrz6jmvl3tj Nieznany
16 Rozdział 15 Szeregi potęgowe
15 rozdzial 15 5GAEWFHLCK7UHC25 Nieznany
15 rozdzial 15 ZA64UKL2SM6IJ6MS Nieznany (2)
16 Rozdział 15 Szeregi potęgowe
15 rozdzial 14 bkx5k7fq5uxxzgt7 Nieznany (2)
15 rozdzial 14 4zf4wab7vahl3rjb Nieznany (2)
rozdzial 15,16
FIN Rozdział 15, 16
Seducing Revenge Rozdział 15 16
Droga Dziewiątego Rozdział 15, 16 ,17
16 Ustalanie wyniku finansowego Nieznany (2)
05 rozdzial 04 nzig3du5fdy5tkt5 Nieznany (2)
normy do cw I PN EN 772 15 id 7 Nieznany
28 rozdzial 27 vmxgkzibmm3xcof4 Nieznany (2)
16 Wspieranie osoby niepelnospr Nieznany (2)
22 Rozdzial 21 KP4Q5YBIEV5DBSVC Nieznany (2)

więcej podobnych podstron