background image

 

Rozdział 18 

Delphi na serwerze SQL Sybase 

W tym rozdziale zapoznamy się z Sybase SQL Server RDBMS. Przedstawimy 
także specyficzne zagadnienia dotyczące współpracy serwera SQL i Delphi oraz 
odmianę języka SQL - Transact-SQL. 

Uruchamianie serwera  

Sposób uruchomienia serwera SQL zależy od systemu operacyjnego. W systemie 
UNIX zwykle uruchamiamy skrypt o nazwie 

RUN_nazwa_serwera,

 natomiast 

w Windows NT korzystamy z programu Sybase Services Manager. Program ten 
można znaleźć w grupie lub folderze Sybase SQL Server. 

Uruchomić i zatrzymać serwer można też za pomocą apletu 

Services

 w Windows 

NT Control Panel. Ponieważ serwer SQL działa jako usługa, więc można też 
skorzystać z polecenia 

Services

 w programie Windows NT Server Manager. 

Zatrzymanie serwera umożliwia także instrukcja 

SHUTDOWN 

języka Transact -

SQL. Wykorzystanie opcji 

NOWAIT

 spowoduje natychmiastowe przerwanie jego 

pracy, natomiast normalnie serwer jest zatrzymywany dopiero po zakończeniu 
realizowanych procesów. 

Łączenie się z serwerem 

Aby połączyć się z serwerem Sybase z aplikacji Delhi, funkcjonującej na 
komputerze klienta w 

systemie Windows 95 lub Windows NT, należy 

zainstalować oprogramowanie Sybase Net Library. Oprogramowanie to występuje 
w dwóch  różnych wersjach: osobna dla Windows 95 i Windows NT. Wersja dla 
NT (jest to część pakietu Sybase Open Client for Windows 95) instaluje się tylko 

tym systemie. Przy próbie zainstalowania jej w 

systemie Windows 95 

sygnalizowany jest błąd 422. 

Druga wersja jest nowsza i funkcjonuje poprawnie w obu systemach Windows. 
Oczywiście, jeśli jest taka możliwość, powinniśmy posługiwać się  właśnie tą 
wersją.  

background image

548 

Część III 

Net Library - błąd oprogramowania klienta 422 w NT 

Wersja przeznaczona tylko dla Windows NT niekiedy instaluje się niepoprawnie. 
Począwszy od 1996 r. program instalacyjny przy pierwszej instalacji sygnalizował 
często błąd 422. Okazało się, że rozwiązanie tego problemu jest proste. Mimo, że 
towarzyszący oprogramowaniu podręcznik Sybase Release Bulletin nakazuje 
opróżnić katalog 

TEMP

, nie należy tego robić. Przy pierwszej nieudanej próbie 

instalacji program pozostawia pewne pliki tymczasowe w katalogu 

TEMP

. Jeśli się 

ich nie usunie i uruchomi instalację ponownie, wszystko powinno przebiegać bez 
zakłóceń.  

Konfigurowanie oprogramowania klienta 

Niezależnie od wybranej wersji oprogramowania skonfigurujemy połączenie 
klienta posługując się programem Sybase SQLEDIT:  

1. Uruchamiamy SQLEDIT i wpisujemy nazwę serwera do pola tekstowego 

w oknie dialogowym Input Server Name. 

2. Klikamy 

Add

 - aby dodać wprowadzoną nazwę do listy Server Entry. 

3.  Z listy rozwijalnej 

Service Type

 wybieramy 

Query

4.  Z listy Platform wybieramy odpowiednio 

Windows

 

95

 lub 

NT

5. Następnie wybieramy odpowiedni sterownik z listy rozwijalnej 

Net-Library

 

Driver

. Najprawdopodobniej będzie to 

NLWNSCK (TCP/IP Winsock)

 albo 

NLWLINK

 

(IPX).

 

6. W oknie dialogowym 

Connection Information\Network Address

 wpisujemy 

adres sieciowy serwera i numer portu (oddzielone przecinkami). Będzie to 
adres TCP/IP, jeśli korzystamy z protokołu TCP/IP lub adres sieci IPX - jeśli 
używamy IPX. Oto przykładowy łańcuch połączeniowy : 

100.10.15.12, 3000 

W tym przykładzie 

100.10.15.12

 jest adresem TCP/IP a 

3000

 - numerem 

portu. W razie wątpliwości numer sieci i portu można uzyskać od administratora 
sieci lub systemu. 

UWAGA 

Aby uzyskać połączenie można wykorzystać plik konfiguracyjny użytkownika. 
Jeśli w pliku HOSTS jest linia  

100.10.15.12 marketing 

to do uzyskania połączenia wystarczy wprowadzić 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

549 

marketing 3000

 

7.  Po ustawieniu informacji o połączeniu klikamy przycisk 

Add Service

, dodając 

w ten sposób usługę do listy 

Connection Service Entry

8. Po dodaniu usługi jesteśmy gotowi do jej przetestowania. Klikamy przycisk 

Ping

, umieszczony po prawej stronie w dolnej części ekranu. Ping próbuje 

połączyć się a następnie zamknąć połączenie z ustalonym wcześniej serwerem. 
Jeśli oprogramowanie klienta zostało właściwie skonfigurowane, nie powinny 
wystąpić żadne problemy. 

9. Aby zachować informację o połączeniu naciskamy CTRL+S . 

WSKAZÓWKA 

Ten sam sposób postępowania przy konfigurowaniu połączenia obowiązuje wtedy, 
gdy instalujemy oprogramowanie czy też uruchamiamy SQLEDIT niezależnie . 

SYBPING 

Do przetestowania połączenia z serwerem można użyć programu Sybase’a 
o nazwie SYBPING. Powinien się on znajdować w folderze SYBASE. Klikamy 
nazwę naszego serwera na liście prezentowanej w programie, a następnie przycisk 

Ping

. Jeśli nasz komputer połączy się z serwerem, to konfiguracja połączenia jest 

poprawna. 

Sterowniki dla Win 3.x 

Podamy teraz kilka istotnych wskazówek dla osób korzystających z 16-bitowego 
oprogramowania klienta (Win 3.x): 

„Wcześniejsze (niż System 10) wersje Sybase SQL Server korzystają 

z biblioteki  połączeniowej Sybase DB-Library. Natomiast System 10 oraz 
wersje późniejsze posługują się CT-Library. DB-Library umożliwia łączenie się 
z serwerami  System  10, ale nie obsługuje jego specyficznych własności. 
Połączenia w Delphi 1.0 nawiązywane są poprzez DB-Library; Delphi 2.0 
i późniejsze mogą korzystać z 

obu bibliotek. Zanim przystąpimy do 

konfiguracji, powinniśmy się więc upewnić, jaka biblioteka jest używana przez 
naszego klienta. 

„Stworzono wiele różnych wersji bibliotek DB-Library. W czasie naszej pracy 

nie mieliśmy żadnych kłopotów z wersją 3-1-94, ale oczywiście nie znaczy to, 
że jest ona najlepsza. Chcielibyśmy jednak ostrzec, że niekiedy mogą wystąpić 
nieprawidłowości przy konwersji typów. Problem taki pojawił się wtedy, gdy 

background image

550 

Część III 

korzystaliśmy z Delphi 1.0 i bibliotek Sybase DB-Library z czerwca 1994 r. 
(konwersja pomiędzy typami datetime i string była niepoprawna). Dlatego 
pierwszym krokiem w 

przypadku wystąpienia problemów związanych 

z konwersją powinna być zmiana wersji biblioteki DB-Library. 

„

Począwszy od Systemu 10 zmieniono metodę usuwania zapytań. Obecnie 
bazuje ona na Out-Of -Band Data (OOBD). Obsługa OODB jest wbudowana 
w protokół IPX, ale w przypadku TCP/IP trzeba się o tym upewnić. Np. tylko 
późniejsze wersje LAN WorkPlace TCP/IP obsługują OOBD; wcześniejsze 
wymagają odpowiedniej aktualizacji.

 

„

Jeśli korzystamy z OOBD do usuwania zapytań w środowisku Win 3.x, to 
powinniśmy użyć parametru połączeniowego 

URGENT

. Zwykle dodaje się 

URGENT

 na końcu łańcucha połączeniowego:

 



100.10.15.12,3000, URGENT

 

 Wtedy zapytania, przerywane funkcją 

dbcancel()

 z biblioteki DB-Library 

zwracają natychmiast sterowanie do aplikacji.

 

„Należy także pamiętać, aby korzystać z programu WSYBPING a nie SYBPING 

- ten ostatni obsługuje bowiem tylko połączenia 32-bitowe. 

„Wersja DB-Library dla Windows 3.x wymaga trybu rozszerzonego Windows, 

chociaż  nie  jest  to  w żaden sposób sygnalizowane. Po prostu - jeśli 
uruchomimy DB-Library w trybie standardowym, to nie uzyskamy połączenia. 

Tworzenie aliasów BDE 

Po połączeniu się z serwerem, możemy utworzyć aliasy BDE, dzięki którym nasze 
aplikacje w Delphi będą miały dostęp do bazy. Ten temat był już omawiany 
wcześniej, dlatego teraz przedstawimy tylko sam sposób tworzenia aliasu BDE 
w zależności od konfiguracji oprogramowania sterującego bazą . 

Aliasy BDE można utworzyć za pomocą programu BDE Administrator lub Delphi 
Database Explorer. Poniższe kroki zostały wykonane w 

programie BDE 

Administrator. 

1. Uruchamiamy program DBE Administrator, klikamy prawym przyciskiem 

fiszkę 

Databases

 i z menu wybieramy 

New

. W oknie dialogowym 

New

 

Database

 

Alias

 wybieramy - z listy rozwijalnej - SYBASE Alias type i klikamy 

OK

2.  Gdy zostanie dodany nowy alias wpisujemy jego nazwę. 

3.  Konfigurujemy go, ustawiając wartości parametrów na stronie 

Definition

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

551 

4. Wybieramy parametr 

SERVER NAME

 i wpisujemy nazwę naszego serwera 

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

5. Dodatkowo  możemy ustawić parametr 

USER

 

NAME

, podając nazwę 

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

Aby zapisać zmiany, klikamy przycisk 

Apply

 i zamykamy sesję Administratora. 

Specjalne ustawienia aliasu BDE 

Omówione poniżej ustawienia wartości parametrów aliasu BDE mogą okazać się 
istotne, gdy łączymy się z serwerem SQL Sybase. 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. 

APPLICATION NAME (Nazwa aplikacji) 

Wartością Parametr 

APPLICATION NAME

 jest nazwa, pod którą nasz proces 

będzie widoczny w tablicy procesów Serwera SQL. 

CONNECT TIMEOUT (Czas do zerwania połączenia) 

Decyduje jak długo klient będzie ponawiał próby nawiązania połączenia 
z serwerem SQL. Domyślnie jest przyjmowane 60 s. Zwiększenie tego parametru 
może poprawić funkcjonowanie stacji klienckich, szczególnie gdy łączymy się 
przez 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

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. 

background image

552 

Część III 

TDS PACKET SIZE (Rozmiar pakietów TDS) 

Stosowany do ustalenia rozmiaru pakietów Tabular Data Stream (TDS). 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, 
w wyniku 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ć 
procedury pamiętanej 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 SQL Server 

max 

network packet size

 (lub niewłaściwa dodatkowa pamięć 

addition 

network memory

 ) 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 (Nazwa bazy danych) 

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 takiego podejścia. Jednym z powodów 
jest 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 polach BLOB) 

Można zablokować rejestrację zmian w polach 

BLOB

, ustawiając wartość tego 

parametru na 

FALSE

. Minimalizujemy wówczas wymagania pamięciowe, 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

553 

poprawiając wydajność. Ustawienie 

FALSE

 powoduje, że 

BLOB

 są transmitowane 

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. Opcję 

set select into/bulk 

copy ustawiamy 

za pomocą procedury pamiętanej 

sp_dboption

.  

MAX QUERY TIME (Maksymalny czas wykonania zapytania) 

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

TIMEOUT

 określa czas zakończenia zapytania synchronicznego. Tryb 

asynchroniczny ustawimy, zwiększając parametr 

DRIVER FLAG do 2048.

 

Domyślnie SQL Links ogranicza czas zakończenia zapytania asynchronicznego do 
300 s. 

Różnorodne zagadnienia dotyczące Sybase SQL Server 

Oprócz konfiguracji aliasów są jeszcze pewne niuanse, o których powinniśmy 
wiedzieć, tworząc aplikacje w Delphi korzystające z baz danych Sybase’a. Poniżej 
poruszymy kilka spraw dotyczących współpracy serwera SQL Sybase’a 
z aplikacjami Delphi. 

Typy danych numeric i decimal 

W system Sybase 10 wprowadzono nowe numeryczne i dziesiętne typy danych. 
BDE tłumaczy je na swój typ danych zmiennoprzecinkowych 

fldFloat

. Należy 

jednak zauważyć, że oba typy są pobierane z serwera w ich naturalnym formacie 
(xltNONE), więc nie występuje żadna utrata danych przy translacji. 

Tabele w innych bazach 

Możemy uzyskać dostęp do obiektów w innych bazach danych, podając pełną 
specyfikację obiektu. Np. można otworzyć tabelę ACCOUNTING.DEMO.

 

CUSTOMER, umieszczoną w bazie danych ACCOUNTING, której właścicielem 
jest użytkownik DEMO, podając tylko jej pełną nazwę. 

Rozwiązywanie problemów połączeniowych z serwerem Sybase 

Poniżej zebraliśmy kilka praktycznych porad, które mogą okazać się przydatne 
w przypadku napotkania problemów połączeniowych. 

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

spróbować nawiązać komunikację poprzez program Sybase SYSPING. 

background image

554 

Część III 

Uzyskanie połączenia  świadczyłoby o 

tym, że prawdopodobnie źle 

skonfigurowaliśmy alias BDE. W takiej sytuacji powinniśmy powrócić do 
programu BDE Configuration i upewnić się,  że podane ustawienia są 
poprawne. (w szczególności nazwa serwera). 

WSKAZÓWKA 

Warto zwrócić uwagę na różnicę pomiędzy programem WSYBPING i SYBPING. 
WSYBPING jest przeznaczony tylko do obsługi połączeń  16-bitowych, więc nie 
należy używać go z 32-bitowymi aplikacjami Delphi. 

2. 

Gdy próba nawiązania połączenia poprzez SYSPING kończy się 
niepowodzeniem, możemy przypuszczać,  że przyczyną problemów może być 
protokół sieciowy. Jeśli korzystamy z TCP/IP, to połączenie z serwerem można 
sprawdzić za pomocą programu PING, który jest dostarczany z Windows 95 
i Windows.  Powinniśmy spróbować  łączenia poprzez nazwę komputera, 
zapisaną w pliku HOSTS oraz poprzez jego adres IP. 

3. Jeśli łączymy się poprzez adres IP, co nie udaje się poprzez nazwę, powinniśmy 

sprawdzić plik HOSTS. Tymczasowo można zmienić odwołanie sieciowe 
(programem SYBEDIT), 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 sprawdzić, czy numer IPX sieci serwera 

jest zgodny z podanym w SYBEDIT. Jeśli nadal nie możemy uzyskać 
połączenia albo jeśli wystąpiły kłopoty z 

jeszcze innym protokołem 

połączeniowym, radzimy zwrócić się o pomoc do administratora sieci. 

5. Jeśli korzystamy z TCP/IP i w żaden sposób (ani poprzez nazwę ani adres IP) 

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  źle skonfigurowano sam protokół. W takim przypadku 
proponujemy zwrócić się do administratora sieci. 

6. W sytuacji, gdy nie uzyskaliśmy połączenia poprzez SYBPING, natomiast 

PING funkcjonuje poprawnie, należy sprawdzić, czy numer portu w odwołaniu 
sieciowym jest zgodny z numerem ustawionym na serwerze. Informacje 
o portach przypisanych do serwera można uzyskać od administratora bazy 
danych. W Systemie 10 można je ustawiać, posługując się narzędziem Sybase - 
SYBINIT. 

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ść). Np. autor 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

555 

spotkał się z wersją Sybase System 10 for Novell Netware, która nie 
funkcjonowała poprawnie ze swoim macierzystym protokołem Novell IPX, 
natomiast nie sprawia żadnych kłopotów z TCP/IP. 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. 

Wstęp do SQL  

Ten paragraf jest wprowadzeniem do odmiany SQL, używanej przez serwery 
Sybase SQL, zwanej Transact-SQL. Zwracamy w nim szczególną uwagę na kilka 
cech, które różnią  tę implementację SQL od dostarczanej przez innych 
sprzedawców. Aby móc poznawać w praktyce prezentowane elementy języka, 
wystarczy zapewnić sobie dostęp do uruchomionego serwera i umieć się z nim 
komunikować poprzez program 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ć w języku Transact-
SQL: 

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:\sybasesql\data\saltrn00.dat', 
vdevno=5, 
size = 2048 

Parametr 

name 

definiuje nazwę logiczną (ang. logical handler), która występuje 

w odwołaniach do danego urządzenia. 

background image

556 

Część III 

Parametr

 physname

 określa szczegółowo fizyczne położenie i nazwę urządzenia 

plikowego. Może to być plik w systemie plikowym lub surowa partycja 
(w przypadku systemu operacyjnego, który przewiduje taką możliwość). 

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). Jednak, aby móc skorzystać z numerów powyżej  10 
powinniśmy najpierw powiększyć maksymalną liczbę dopuszczalnych urządzeń za 
pomocą procedury pamiętanej

 sp_configure

  

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:\sybasesql\data\sallog00.dat', 
vdevno=6, 
size = 512 

Po tych przygotowaniach możemy przystąpić do utworzenia bazy. Instrukcja 

CREATE DATABASE

 w poniższym przykładzie korzysta ze zdefiniowanych 

wcześniej urządzeń : 

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

Instrukcja USE 

Instrukcja USE w języku SQL służy do wyboru aktywnej bazy. Składnia instrukcji 
jest następująca: 

USE dbname 

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 SQL - 

CREATE TABLE

. Wprowadźmy 

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

CREATE TABLE CUSTOMER 

CustomerNumber int 

NOT 

NULL, 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

557 

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 

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 i usuwanie kolumn 

Do dodania lub usunięcia kolumny z istniejącej tabeli służy instrukcja 

ALTER 

TABLE

. Aby dodać kolumnę korzystamy z następującej składni: 

ALTER TABLE CUSTOMER 
ADD PhoneNumber char(10) 

NULL 

natomiast aby ją usunąć: 

ALTER TABLE CUSTOMER 
DROP PhoneNumber  

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. 

OSTRZEŻENIE 

Instrukcja 

ALTER TABLE... DROP

 jest realizowana w Sybase tylko jako 

nieudokumentowany element języka. Powinniśmy brać to pod uwagę, korzystając 
z niej w swoich programach. 

background image

558 

Część III 

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 CustomerNumber. 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ę 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 Sybase 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 

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 po stronie użytkownika, które 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

559 

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 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 następującą instrukcję w programie ISQL: 

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

Ponieważ pozwalają one tylko na wprowadzenie wartości 

'OK', 'AR' 

i 'MO',

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

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 

SaleDate -

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

w Sybase SQL Server musi być unikalna w bazie, w której jest on umieszczony. 

Instrukcja 

CREATE UNIQE INDEX

 tworzy indeks zapewniający unikalność 

klucza np: 

CREATE UNIQE INDEX SALE01 ON SALE (SaleNumber) 

Wprowadzanie danych 

Instrukcja 

INSERT

 służy do wprowadzania danych do tabeli. 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. 

background image

560 

Część III 

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: 

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) 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

561 

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: 

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 7%. Modyfikację tę można wykonać następująco: 

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

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' 

background image

562 

Część III 

Kontrola Transakcji 

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

BEGIN TRANSACTION

. Instrukcja 

COMMIT

 

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

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). 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 instrukcja 

INSERT

 nie zostanie wykonana 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. 

SELECT

 * zwraca cała 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  

Instrukcji 

SELECT

 w Sybase 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: 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

563 

SELECT SaleNumber, SaleDate, Amount+15 AmountPlusShipping  
FROM SALE 

Funkcje sumaryczne 

Funkcje sumaryczne wykonują pewne obliczenia na zbiorach danych. Przykładami 
takich funkcji są 

COUNT, SUM, AVG, MIN

 oraz 

MAX

. 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 
można zastosować - do zamiany wszystkich liter w kolumnie i poszukiwanym 
wzorcu na duże - funkcję 

UPPER

.  

Oto dalsze przykłady: 

SELECT * FROM SALE 
WHERE Amount>500 

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

background image

564 

Część III 

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 

Customer-

Number

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

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  złączenie. Jest to 
najczęściej używane złączenie w zapytaniach SQL. 

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

Wspomniane powyżej złączenie lewostronne 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 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 
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 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

565 

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 

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: 

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 

background image

566 

Część III 

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 jest tworzona 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

 

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ętrznego 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 

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: 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

567 

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 zrestarowanie 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 przechodzi się wiersz po wierszu przez całą tabelę. 
Podejście SQL jest zupełnie inne. 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ściowe wierszy, do których 

odnoszą się funkcje sumaryczne użyte w klauzuli 

SELECT

. Następny przykład 

pokazuje zastosowanie klauzuli 

GROUP BY

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

background image

568 

Część III 

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

. Sybase 

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, Sybase 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 zależność 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 

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. 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

569 

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

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

background image

570 

Część III 

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 jest wykonywane zapytanie posługujące się 
perspektywą, optymalizator zapytań tworzy najpierw perspektywę,  łączy ją 
z właściwym zapytaniem i optymalizuje obie czynności wykonując je 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ę do wszystkich omawianych w tym rozdziale serwerów 
baz danych. 

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ł 18 Delphi na serwerze SQL Sybase 

571 

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. Skrypty SQL w Sybase SQL Server uruchamia się, podając w linii 
poleceń programu ISQL instrukcję (jak w poniższym przykładzie): 

ISQL -Usa -Imyscript.sql -Omyscript.out 

Parametr -I określa nazwę skryptu do wykonania; -O określa nazwę pliku 
wynikowego dla skryptu. Przykład został przedstawiony w listingu 18.1. Polecenie 

sp_dir

 w procedurze pamiętanej działa analogicznie, jak DIR w systemie DOS . 

Listing 18.1. Skrypt SQL zawierający procedurę pamiętaną 

sp_dir 

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

DROP PROC dbo.sp_dir 

background image

572 

Część III 

 

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

END 
go 
create procedure sp_dir @mask char(30) = '%', @obtype char(2)  

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

select o.id, 
 o.name, 
 o.type, 
 date_created=o.crdate, 
 

row_count = rowcnt(i.doampg), 

--  

row_len=1*null, 

 

size = convert(numeric(13),(reserved_pgs(i.id,  

 

 i.doampg)+reserved_pgs(i.id, i.ioampg))), 

 

data_space = convert(numeric(13),data_pgs(i.id,  

 

 i.doampg)), 

 

index_space = convert(numeric(13),data_pgs(i.id,  

 

 i.ioampg)), 

 

unused_space = convert(numeric(13),((reserved_pgs(i.id,  

 

 i.doampg)+reserved_pgs(i.id, i.ioampg))- 

 

 (data_pgs(i.id, i.doampg)+data_pgs(i.id,  

 

 i.ioampg)))), 

 owner=user_name(o.uid), 
 sequencer=0.0 
into #sp_dir 
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) 

/* 
declare @tablename char(30),@row_len int 
set rowcount 1 
while exists (select @tablename=name from #sp_dir where type  

 in ('U','S') and row_len = null) 

begin 
 

exec sp_estspace @tablename,1,@rowlength=@row_len  

 

 output 

 

update #sp_dir set row_len=@row_len 

 where 

name=@tablename 

end 
set rowcount 0 
*/ 

if @orderby = '/N' 
begin 
 

create clustered index sp_dirind on #sp_dir (name,id) 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

573 

end 
else 
if @orderby = '/R' 
begin 
 

create clustered index sp_dirind on #sp_dir 

(row_count,id) 
end 
else 
if @orderby = '/S' 
begin 
 

create clustered index sp_dirind on #sp_dir (size,id) 

end 
else 
if @orderby = '/D' 
begin 
 

create clustered index sp_dirind on #sp_dir  

 

 (date_created,id) 

end 
else 
if @orderby = '/DS' 
begin 
 

create clustered index sp_dirind on #sp_dir  

 

 (data_space,id) 

end 
else 
if @orderby = '/IS' 
begin 
 

create clustered index sp_dirind on #sp_dir  

 

 (index_space,id) 

end 
else 
if @orderby = '/US' 
begin 
 

create clustered index sp_dirind on #sp_dir  

 

 (unused_space,id) 

end 
else 
if @orderby = '/O' 
begin 
 

create clustered index sp_dirind on #sp_dir (owner,id) 

end 

alter table #sp_dir drop sequencer 

alter table #sp_dir add sequencer numeric(10,0) identity 

insert into #sp_dir 
(name,row_count,size,data_space,index_space,unused_space,id, 

 type,date_created)) 

background image

574 

Część III 

select 
'TOTAL:',row_count=isnull(sum(row_count),0),size=isnull(sum 

 (size),0),date_space=isnull(sum(data_space),0),index_space 

 =isnull(sum(index_space),0),unused_space=isnull(sum( 

 unused_space),0),id=0,type=@obtype,date_created=getdate() 

from #sp_dir 

select name,type,date_created, row_count, size, data_space, 
index_space, 
unused_space, owner 
from #sp_dir 
order by sequencer 
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 

sp_dir

 zwraca listę obiektów wszystkich typów z danej bazy danych. Może ona 

zostać posortowana według dowolnej ze zwracanych kolumn. Wynik działania 
procedury 

sp_dir

 został przedstawiony na rysunku 18.1. 

Instrukcja 

USE 

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

zostanie utworzona. Natomiast polecenie 

GO, 

kończące wykonanie zadania 

wsadowego, separuje oddzielne części skryptu. 

 

Rysunek 18.1 
Można użyć 
procedury sp_dir 
do utworzenai listy 
obiektów na 
serwerze 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

575 

Uruchamianie procedur pamiętanych 

Procedury pamiętane SQL Server uruchamiamy instrukcją 

EXECUTE

. Składnia 

instrukcji jest następująca: 

EXECUTE

 procedurename parameters 

Możemy skrócić 

EXECUTE

 do 

EXEC

, a nawet opuścić, jeśli 

EXEC

 jest pierwszą 

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

listcustomer

 za 

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

listcustomers 

Dopuszczalne jest przekazanie parametrów wg. kolejności, w jakiej występują 
w definicji procedury: 

exec listcustomersbystate 'MO', '%' 

lub przez nazwę parametru: 

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

Procedury systemowe 

Procedury systemowe  są to specjalne procedury pamiętane, których nazwa 
rozpoczyna się od 

sp_

 i wykonywane kontekstowo na bieżącej bazie danych. Np. 

sp_spaceused

 jest procedurą systemową. Jeśli wykonamy ją wtedy, gdy jest 

aktywna baza pubs2, to otrzymamy informację na temat pamięci używanej przez 
bazę pubs2. Jeśli wykonamy ją dla bazy głównej (master database), otrzymamy 
dla niej analogiczną informację. Procedury systemowe mogą być umieszczone 
w bazie  głównej (master database) albo w 

bazie procedur systemowych 

(sybsytemproc database

WSKAZÓWKA 

Jeśli zamierzamy uruchomić procedurę systemową dla bazy innej niż bieżąca, 
wygodniej jest - zamiast zmiany kontekstu poleceniem USE - poprzedzić nazwę 
procedury nazwą bazy, dla której chcemy ją uruchomić. Załóżmy,  że chcemy 
wykonać procedurę systemową dla bazy głównej a pubs2 jest aktywna. Można to 
zrobić następująco: 

master..sp_dir '%', 'S' 

Chociaż 

sp_dir

 jest umieszczona w bazie sybsystemprocs, lecz przy jej 

uruchomieniu kontekst będzie chwilowo zmieniony na bazę  główną. Wynik 
działania jest pokazany na rysunku 18.2. 

background image

576 

Część III 

Warto też zwrócić uwagę na dwie kropki pomiędzy nazwą bazy danych a nazwą 
procedury. Jest to standardowe w Sybase wywołanie procedury pamiętanej. 
Przyjmuje się wtedy domyślnie,  że właścicielem procedury (który powinien być 
także wyspecyfikowany) jest 

dbo 

(database owner)  

Procedury zdarzeń (Triggers) 

Procedury zdarzeń  są to (podobnie jak procedury pamiętane) podprogramy SQL, 
uruchamiane wtedy, 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. 

 

Rysunek 18.2 
Można użyć 
uproszczonej 
metody wskazania 
bazy danych, dla 
której chcemy 
uruchomić 
procedurę 
pamiętaną 

background image

 Rozdział 18 Delphi na serwerze SQL Sybase 

577 

Warto zwrócić uwagę na użycie logicznej tabeli DELETE. Zawsze, gdy 
uruchamiana jest procedura zdarzenia skojarzona z 

DELETE

, to z wierszy 

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 są usuwane 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 zorientowanej na zbiory 
koncepcji 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, w procedurach pamiętanych, mogą okazać 
się użyteczne. 

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. Sybase SQL Server 

background image

578 

Część III 

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

CLOSE

) i ponownie otworzyć (

OPEN

) kursor. 

UWAGA 

Mimo  że Sybase 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 w obiektach 

TDataSets,

 takich jak 

TQuery i TTable,

 

można przewijać w obie strony. 

Wiersze zwracane przez modyfikowalny kursor mogą być zmodyfikowane 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