background image

 

Rozdział 17 

Delphi a Interbase 

W tym rozdziale przedstawimy Interbase RDBMS. Omówimy elementy SQL oraz 
niektóre zagadnienia dotyczące wspólnej pracy InterBase i Delphi. Opiszemy także 
podstawy administrowania serwerami InterBase. 

Zaczniemy oczywiście od uruchomienia serwera. 

Uruchamianie serwera InterBase 

Sposób uruchomienia serwera w dużym stopniu zależy od systemu operacyjnego. 
Mając na uwadze fakt, że nie jest to książka poświęcona wyłącznie systemowi 
InterBase, ograniczymy nasze rozważania tylko do najpopularniejszych - Windows 
95 i Windows NT. 

Po przeprowadzeniu standardowej instalacji, serwer InterBase uruchamia się 
automatycznie przy każdym starcie Windows. Na pasku zadań w Windows 95/NT 
pojawia się wtedy jego ikona ( lub ikona na pulpicie, jeśli używamy Windows NT 
3.51 ). 

Klikając ikonę na pasku zadań prawym przyciskiem myszy, uzyskujemy dostęp do 
podstawowych ustawień serwera. Jeśli np. wybierzemy - z menu rozwijalnego - 
pozycję 

Startup Configuration

, serwer będzie startował równolegle z Windows. Po 

wybraniu z kolei 

Properties

, będziemy mogli odczytać dopuszczalną i aktualną 

liczbę połączeń z serwerem. 

Łączenie się z serwerem 

W przeciwieństwie do omówionych dotychczas serwerów - aby uzyskać dostęp do 
serwerów InterBase, nie musimy definiować oddzielnego połączenia. Jeśli 
korzystamy z lokalnego serwera InterBase, oprogramowanie klienta (WISQL, 
BDE, itp. ) odwołuje się bezpośrednio do pliku bazy danych (nie ma potrzeby 
podawania jakichkolwiek danych odnoszących się do serwera). 

W przypadku odległego serwera (InterBase), niezbędne do połączenia informacje 
zależą od wyboru protokołu sieciowego. Jeśli łączymy się poprzez TCP/IP, wtedy 
plik HOSTS musi zawierać dane dotyczące naszego serwera, np.: 

100.10.15.12 marketing 

background image

506 

Część III 

Także plik TCP SERVICES powinien zostać uzupełniony o linię definiującą 
protokół dostępu InterBase: 

gds_db 3050/tcp 

Oczywiście odpowiednie zmiany w 

plikach dokonywane są automatycznie 

w czasie instalacji serwera. Gdy tylko skonfigurujemy dostęp poprzez TCP/IP, 
możemy korzystać  z WISQL  i innych  narzędzi InterBase. Należy jeszcze raz 
podkreślić,  że podane wymagania dotyczące plików inicjacyjnych odnoszą się 
tylko do TCP/IP. NetBEUI ani IPX/SPX nie nakładają tego typu wymagań. 

Konfigurowanie Aliasów BDE 

Teraz utworzymy alias BDE, który umożliwi aplikacjom w Delphi dostęp do 
serwera. Ten temat był już omawiany wcześniej, dlatego teraz przedstawimy tylko 
kolejne kroki, które pozwolą poprawnie go skonfigurować. 

Chcąc zbudować alias BDE powinniśmy wybrać jedno z 

narzędzi: BDE 

Administrator lub Delphi Database Explorer. Opisany poniżej sposób 
postępowania odnosi się do programu BDE Administrator. 

1.  Uruchamiamy BDE Administrator, klikamy prawym przyciskiem myszy fiszkę 

Databases

 i wybieramy 

New

. W oknie dialogowym 

New Database Alias

 

wybieramy, z listy typów aliasów, 

INTRBASE

 i klikamy 

OK

2. Wpisujemy nazwę nadaną tworzonemu aliasowi . 

3. Konfigurujemy go przez odpowiednie ustawienie parametrów na stronie 

Definition

 

4. Klikamy parametr 

SERVER NAME

 i wprowadzamy nazwę serwera oraz plik 

bazy danych, z którym chcemy się łączyć. Przyjęto konwencję, że ten plik ma 
rozszerzenie 

GDB

. Wprowadzane dane powinny przyjmować postać: 

MIS:/data/interbase/accounting.gdb 

gdzie 

MIS

 jest nazwą twojego serwera a  

/data/interbase/ 

accounting.gdb

 jest pełną ścieżką do pliku zawierającego bazę danych. 

WSKAZÓWKA 

Jeśli korzystamy z lokalnego serwera InterBase, pole 

SERVER NAME

 może 

pozostać niewypełnione. Wystarczy podać tylko pełną  ścieżkę dostępu do pliku 
zawierającego bazę danych., np. 

C:\DATA\INTERBASE\ACCOUNT.GDB

  

background image

 Rozdział 17 Delphi w Interbase 

507

 

5. Jeśli podamy nazwę  użytkownika (parametr

 USER NAME

), to będzie on 

domyślnie przyłączany do serwera. Podana nazwa będzie też umieszczana we 
wbudowanym w Delphi oknie dialogowym logowania. 

Jak widać jest kilka innych parametrów, którym możemy nadać odpowiednie 
wartości. W szczególności jeden z nich powinien być zawsze ustawiony dla 
aliasów InterBase - 

ENABLE BCD. D

ecyduje on bowiem o tym, jak BDE będzie 

interpretował pewne typy danych numerycznych. Chociaż paramatr ma w swej 
nazwie BCD, to jednak jego zasięg nie ogranicza się tylko do danych BCD 
(Binary-Coded Decimal), ale dotyczy także typów zmiennoprzecinkowych: 
DECIMAL i 

NUMERIC. Gdy 

ENABLE BCD

 jest ustawiony na 

FALSE

 

(ustawienie domyślne), pola DECIMAL lub NUMERIC będą traktowane przez 
Delphi jako integers (całkowite). W konsekwencji elementy rozpoznające rodzaj 
danych uniemożliwią nam wprowadzanie części dziesiętnych liczb. Dlatego 
powinniśmy ustawiać 

ENABLE BCD

 na 

TRUE

 we wszystkich aliasach. 

Po wybraniu 

Apply

 wprowadzone zmiany zostają zapamiętane. 

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

Poniżej zebraliśmy kilka praktycznych porad, które mogą okazać się przydatne 

przypadku napotkania problemów połączeniowych w naszych aplikacjach 

w Delphi. 

1. Skorzystamy najpierw z 

narzędzia Interbase Communication Diagnostic, 

umieszczonego w folderze InterBase 4.2. Klikamy przycisk 

Test

 ze strony 

DB

 

Connection

. Brak połączenia może być spowodowany złą konfiguracją aliasu 

BDE. W takiej sytuacji powinniśmy powrócić do programu BDE Administrator 
i upewnić się, czy podane ustawienia są poprawne. Jeśli nadal nie będziemy 
uzyskiwali połączenia z DB Connection, powinniśmy przejść na stronę 

NetBEUI

 lub 

Winsock

 ( wybierając odpowiednią fiszkę) i kliknąć 

Test

. Jeśli 

choć jeden będzie funkcjonował poprawnie, to prawdopodobną przyczyną 
braku połączenia są  złe ustawienia parametrów. Jeśli  żadna z prób się nie 
powiedzie (a mamy pewność,  że serwer jest uruchomiony), proponujemy 
kontynuowanie diagnostyki według zamieszczonego poniżej opisu. 

2. Jeśli InterBase WISQL umożliwia nawiązanie komunikacji z serwerem, 

natomiast nadal występują problemy połączeniowe w naszej aplikacji, to 
prawdopodobnie konfiguracja aliasu DBE jest niewłaściwa. W takiej sytuacji 
powinniśmy powrócić do programu BDE Administrator i upewnić się, czy 
podane ustawienia są poprawne ( w szczególności nazwa serwera). 

3. Brak  połączenia poprzez WISQL świadczy o wadliwym funkcjonowaniu 

protokołu sieciowego. Jeśli korzystamy z TCP/IP, łączność z serwerem można 
sprawdzić za pomocą programu PING, który jest dostarczany z Windows 95 / 

background image

508 

Część III 

NT. Powinniśmy spróbować  łączenia poprzez nazwę komputera zapisaną 
w pliku HOSTS lub poprzez jego adres IP. 

4. Jeśli korzystamy z nazwanych potoków (ang. named pipes) - do komunikacji 

z serwerem  pracującym w Windows NT powinniśmy użyć polecenia 

net 

view \\ servername

, gdzie 

servername

 jest nazwą komputera w sieci 

NT, na którym funkcjonuje serwer SQL. Po poprawnej realizacji 

net view

 

posłużymy się instrukcją 

netuse \\servername\IPC$,

 zamieniając 

servername

 na nazwę naszego serwera. Wystąpienie jakichkolwiek 

nieprawidłowości wskazuje na brak połączenia z komputerem serwera w sieci. 
W takim przypadku proponujemy zwrócić się do administratora systemu NT. 
Źródłem zakłóceń może być też wadliwa obsługa potoków na serwerze. 

5.  W sytuacji, gdy uzyskaliśmy połączenie poprzez adres IP, co nie powiodło się 

za pośrednictwem nazwy serwera, powinniśmy dokładnie sprawdzić dane 
w pliku 

HOSTS

, bowiem oprogramowanie klienta InterBase’a korzysta z niego 

przy wyszukiwaniu serwera. W pliku 

HOSTS

 musi się znaleźć odpowiednia 

linia z informacjami o każdym serwerze, z którym chcemy się połączyć np: 

100.10.15.12 marketing

 

6.  Gdy nie udało się nam połączyć z serwerem (ani poprzez nazwę ani adres IP), 

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. 

7. Może się też zdarzyć, że nie uzyskujemy połączenia poprzez WISQL, natomiast 

PING umożliwia nawiązanie komunikacji. W takim przypadku możemy 
skorzystać z Telnetu (dostarczanego wraz z Windows95/NT). Składnia wywołania 
wygląda następująco: 

TELNET 100.100.100.100

 

lub 

TELNET hostname

 

gdzie 

100.100.100.100

 jest adresem TCP/IP komputera serwera a hostname 

jego nazwą. Przyczyną braku połączenia poprzez Telnet może być wadliwe 
działanie demona inet na serwerze.  

8. Jeśli połączyliśmy się poprzez Telnet przy wciąż nie funkcjonującym WISQL, 

powinniśmy się upewnić, czy oprogramowanie serwera InterBase’a zostało 
poprawnie zainstalowane i uruchomione. Istotne jest także, czy plik 

TCP 

SERVICES

 zawiera niezbędne informacje. Powinny one przyjmować postać: 

background image

 Rozdział 17 Delphi w Interbase 

509

 

gds_db 3050/tcp

 

9. Jeśli nasze działania nie przyniosły pożądanego rezultatu, powinniśmy 

skonsultować się z administratorem sieci lub bazy danych. 

Wstęp do SQL  

Język SQL należy do podstawowych środków komunikowania się z serwerami baz 
danych. Niniejszy rozdział jest wprowadzeniem do własnej, bogatej odmiany SQL, 
wykorzystywanej przez InterBase. Zwrócimy tutaj uwagę na kilka właściwości, 
które różnią tę implementację SQL od dostarczanych 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 WISQL.  

Tworzenie bazy danych 

Zaczniemy od utworzenia bazy danych i umieszczenia w niej tymczasowych tabel, 
które zostaną wykorzystane w podanych dalej przykładach. W tym celu posłużymy 
się instrukcją 

CREATE DATABASE

. Jej składnia zależy od konkretnej 

implementacji SQL. Poniższy przykład przedstawia podstawową składnię 
InterBase’a: 

CREATE DATABASE "C:\DATA\IB\SALES" USER SYSDBA PASSWORD  

 masterkey 

Aby utworzyć bazę danych używając InterBase WISQL, korzystamy z polecenia 

Create

 

Database

 z menu 

File

. A oto sposób postępowania: 

1. Uruchamiamy program interaktywnej obsługi SQL (WISQL) - albo z grupy 

programów InterBase’a albo z paska zadań z InterBase Server Manager. 

2. Wybieramy polecenie 

Create

 

Database

 z menu 

File

.  

3. Wpisujemy pełną  ścieżkę dostępu do bazy, którą chcemy utworzyć (np. 

C:\DATA\IB\SALES.GB

) w 

polu 

Database

 okna dialogowego 

Create

 

Database

.  

4. Podajemy nazwę użytkownika i hasło. Domyślne wartości tych parametrów to: 

nazwa użytkownika- 

SYSDBA

; hasło - 

masterkey

5. Klikamy przycisk 

OK

. InterBase powinien utworzyć bazę danych i podłączyć 

nas do niej. Później - do łączenia się z istniejącą bazą - korzystamy z polecenia 

Connect to

 

Database

 z menu 

File

 . 

W oknie dialogowym 

Create DataBase

,w części 

Database Options

, można ustawić 

pewne parametry bazy, między innymi rozmiar stron (w bajtach) tworzących bazę. 

background image

510 

Część III 

Domyślnie jest przyjmowane 1024 (1K), a dopuszczalne są jeszcze wartości 2048, 
4096 lub 8192 ( np. 

PAGE_SIZE=2048

).  

Można też określić dodatkowe pliki, które zostaną utworzone wraz 
z podstawowym plikiem bazy danych oraz podać dla nich początkowe wielkości. 

WSKAZÓWKA 

Utworzenie bazy w WISQL jest możliwe tylko przez wybór odpowiedniej pozycji 
z menu. Mamy jednak do dyspozycji program ISQL (umieszczany domyślnie 
w kartotece 

C:\Program Files\Borland\IntrBase\Bin

), umożliwia-

jący wydawanie w linii poleceń instrukcji SQL, dostępnych w WISQL tylko za 
pośrednictwem odpowiednich pozycji menu. 

Cienie (shadows) 

Serwer InterBase pozwala tworzyć specjalny obiekt nazwany cieniem, do 
utrzymywania lustrzanego odbicia bazy danych na innym dysku (lub dyskach). 
Dzięki temu może być ona nadal dostępna - nawet w przypadku jej uszkodzenia na 
podstawowym dysku lub uszkodzenia samego dysku. 

Wydanie instrukcji 

CREATE SHADOW

 spowoduje stworzenie cienia dla aktywnej 

bazy danych. Składnia polecenia jest następująca: 

CREATE SHADOW 1 "sales.shd" LENGTH 5000 

Podajemy unikalny numer cienia jako pierwszy parametr instrukcji. Jeśli numer 
zostanie pominięty lub będzie się powtarzał, InterBase nie utworzy cienia. 

Dublowanie bazy wiąże się z podwojeniem liczby zapisów, które są wykonywane 
w czasie przeprowadzanych zmian. Należy się z tym liczyć przy wyborze 
lokalizacji dla cienia. Praca z powolnym dyskiem może istotnie obniżyć wydajność 
systemu 

Instrukcja 

CREATE SHADOW

 ma specjalny parametr pozwalający ustalić 

zachowanie się systemu, gdy cień będzie niedostępny. Po wybraniu wartości 

AUTO

 

aplikacje klienta będą nadal przyłączone do bazy, a cień zostanie skasowany. 
Ustawienie 

MANUAL 

zablokuje wszystkie połączenia - aż do chwili, gdy cień 

będzie ponownie dostępny lub zostanie usunięty (instrukcją 

DROP SHADOW). 

CONDITIONAL

 pozwoli kontynuować połączenia i 

spowoduje utworzenie 

nowego cienia. Jeśli parametr jest ustawiony na 

CONDITIONAL,

 wtedy 

w przypadku uszkodzenia bazy jej miejsce zajmie cień i jednocześnie zostanie 
utworzony cień nowo promowanego cienia. A oto jeszcze jeden przykład: 

background image

 Rozdział 17 Delphi w Interbase 

511

 

CREATE SHADOW 1 CONDITIONAL "d:\shadows\sales.shd" LENGTH  

 5000 ; 

Oczywiście zastosowane słowo kluczowe 

CONDITIONAL

 określa zachowanie się 

bazy, gdy cień będzie niedostępny. 

Instrukcja CONNECT 

Instrukcja InterBase SQL - CONNECT - umożliwia łączenie się z istniejącą bazą 
danych. Posiada ona następującą składnię: 

CONNECT ServerAndDatabasePath USER "ValidUser" PASSWORD  

 "UserPassword" 

ServerAndDatabasePath

 oznacza pełną  ścieżkę do serwera i bazy danych, 

z którą chcemy się połączyć. Jeśli serwer jest uruchomiony lokalnie, wystarczy 
podać tylko ścieżkę do bazy danych. 

ValidUser

 i 

UserPassword

 to 

odpowiednio: nazwa użytkownika i hasło na naszym serwerze bazy danych. 
W przypadku lokalnej bazy danych instrukcja 

CONNECT

 może mieć postać: 

CONNECT "C:/DATA/IB/SALES.GDB" USER "SYSDBA" PASSWORD  

 "masterkey" 

Każdorazowo, gdy łączymy się z inną bazą danych, jesteśmy odłączani od 
bieżącej. 

DISCONNECT

 umożliwia zerwanie połączenia (ustalonego wcześniej poleceniem 

CONNECT)

. Typowy przykład:  

DISCONNECT ALL; 

Ten sam efekt osiągniemy zastępując 

ALL

. przez 

DEFAULT

.W WISQL jesteśmy 

zmuszeni do korzystania z poleceń 

Connect to DataBase

 i 

Disconnect 

from Database

 z menu 

File

  (

CONNECT

 i 

DISCONNECT

 nie działają bowiem 

w WISQL bezpośrednio, ale można ich użyć w linii poleceń w ISQL). 

Tworzenie tabel  

Gdy jesteśmy już podłączeni do bazy, możemy rozpocząć definiowanie obiektów. 
Prawie każde pojęcie odnoszące się do relacyjnych baz danych może być 
zaprezentowane za pomocą 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ę korzystając z programu WISQL, aby utworzyć tabelę CUSTOMER: 

background image

512 

Część III 

CREATE TABLE CUSTOMER 

CustomerNumber int 

NOT 

NULL, 

LastName Char(30), 
FirstName char(30), 
StreetAddress char(30), 
City char(20), 
State char(2), 
Zip char(10), 

Dalej utwórzmy tabelę SALE : 

CREATE TABLE SALE 

SaleNumber int  NOT 

NULL, 

SaleDate date, 
CustomerNumber int 

NOT 

NULL, 

ItemNumber int  NOT 

NULL, 

Amount float 

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

CREATE TABLE ITEM 

ItemNumber int 

NOT 

NULL, 

Description char(30), 
Price float 

Tabele zewnętrzne 

InterBase wykorzystuje pewne interesujące odmiany instrukcji 

CREATE TABLE

Jedna z takich alternatywnych postaci umożliwia tworzenie tabel zewnętrznych 
w stosunku do bazy, np.: 

CREATE TABLE SALE_HISTORY EXTERNAL FILE  

 C:\DATA\IB\SALEHIST.DAT 


SaleNumber int 

NOT 

NULL, 

SaleDate date, 
CustomerNumber int 

NOT 

NULL, 

ItemNumber int 

NOT 

NULL, 

Amount float 

Jeśli określony w poleceniu plik zewnętrzny nie istnieje, wtedy InterBase go 
utworzy, w przeciwnym wypadku pozostanie nie zmieniony. Dzięki tej instrukcji 
można odwoływać się do danych utworzonych poza InterBase - np. tabeli, w której 
przechowuje się dane z innego systemu DBMS. Jeśli wyspecyfikowana struktura 

background image

 Rozdział 17 Delphi w Interbase 

513

 

tabeli w InterBase będzie zgodna ze strukturą danych zewnętrznych, można 
skopiować wiersze z pliku zewnętrznego do wewnętrznej tabeli poleceniem:  

INSERT...SELECT.

 

Kolumny obliczane (Computed columns) 

Instrukcja 

CREATE TABLE

 w InterBase umożliwia także definiowanie kolumn 

obliczanych. Poniżej przedstawiamy tabelę ITEMS, rozszerzoną o dodatkową 
kolumnę obliczaną - GovernmentPrice : 

CREATE TABLE ITEM 

ItemNumber int 

NOT 

NULL, 

Description char(30), 
Price float 
GovernmentPrice COMPUTED BY (Price-Price* .15) 

W tym przypadku w kolumnie 

GovermentPrice

 uwzględniono 15% zniżki (w 

stosunku do ceny w kolumnie 

PRICE

 ) dla klientów rządowych. Ponieważ nie 

został w niej podany typ danych, Interbase ustala go na podstawie typu z kolumn 
wykorzystywanych do obliczeń. Definicje kolumn wykorzystywanych przez 
kolumnę obliczaną muszą oczywiście - na liście kolumn polecenia 

CREATE 

TABLE - 

wystąpić przed nią.  

 Podobne  możliwości daje oczywiście instrukcja SELECT czy perspektywa. 
Jednak w tym wypadku wpisujemy obliczenia bezpośrednio do definicji tabeli. 
Jest to dobry sposób na ustalenie pewnych zależności obliczeniowych. Wtedy 
bowiem aplikacje i inne obiekty SQL mogą po prostu pobierać obliczone wartości 
z kolumny obliczanej. 

Kolumny tablicowe (Array Columns ) 

W uzupełnieniu prostych typów danych InterBase pozwala na zdefiniowanie 
kolumn, które są tablicami danych zadanego typu. Zdefiniowanie kolumny jako 
tablicy pozwala traktować naturalnie zgrupowane elementy danych jako zbiór. 
Przedstawiamy przykład zmodyfikowanej tabeli CUSTOMER, która zawiera 
kolumnę tablicową: 

CREATE TABLE CUSTOMERARRAY 

CustomerNumber int 

NOT 

NULL, 

LastName Char(30), 
FirstName char(30), 
StreetAddress char(30)[3], 
City char(20), 

background image

514 

Część III 

State char(2), 
Zip char(10), 

Należy zwrócić uwagę na oznaczenie "[3]" po prawej stronie w definicji kolumny 
StreetAddress. Zgodnie z podanym opisem będzie ona interpretowana jako 
trzyelementowa tablica elementów typu char (30). Umożliwia więc zapisanie 
poszczególnych części adresu w trzech osobnych liniach. 

Oczywiście możemy też podać zakres indeksów tablicy, np. 

CREATE TABLE CUSTOMERARRAY 

CustomerNumber int 

NOT 

NULL, 

LastName Char(30), 
FirstName char(30), 
StreetAddress char(30)[1:3], 
City char(30), 
State char(2), 
Zip char(10), 

Także w tym przypadku StreetAddress jest jednowymiarową, trójelementową 
tablicą lecz indeksowaną liczbami od 1 do 3, a nie - jak w poprzednim przykładzie 
- domyślnie od 0 do 2. 

Tablice w kolumnach mogą być także wielowymiarowe ( największy wymiar 16). 
Prezentuje to prosty przykład: 

CREATE TABLE HOURLY_SAMPLES 

SampleNo int 

NOT 

NULL, 

SampleDate DATE 

NOT 

NULL, 

WeeklySampleValues int[6,23] 

Zdefiniowana powyżej kolumna 

WeeklySampleValues

 jest dwuwymiarową 

tablicą, w której można gromadzić dane typu int, odnoszące się do każdej godziny, 
przez cały tydzień. 

background image

 Rozdział 17 Delphi w Interbase 

515

 

OSTRZEŻENIE 

Zanim wykorzystamy kolumny tablicowe, w naszych bazach danych warto 
przeanalizować pewne konsekwencje implementacyjne. Kolumny tablicowe są 
istotnie powtarzającymi się grupami. Mówiąc formalnie naruszają pierwszą postać 
normalną. Wymagają także dodatkowego nakładu pracy przy podstawianiu (nie 
można podstawić wartości kolumny tablicowej używając SQL), formatowaniu (np. 
przy raportach) oraz przy zapytaniach. Jest także wiele ograniczeń nałożonych na 
kolumny tablicowe, które nie dotyczą innych typów danych ( np. nie mogą być one 
przekazywane lub zwracane przez procedury pamiętane). Warto ostrożnie 
wyważyć wszystkie za i przeciw wprowadzaniu do struktury swojej bazy danych 
takich, utrudniających manipulowanie danymi, elementów. 

 Specjalne wartości domyślne kolumn (special column defaults) 

Oprócz definiowania stałych jako wartości domyślnych kolumn, InterBase 
umożliwia użycie trzech specjalnych słów kluczowych, definiujących wartość 
domyślną w kolumnie: 

USER,TODAY and NOW

USER 

oznacza nazwę 

aktualnego użytkownika; 

TODAY

 - aktualną datę (analogicznie do funkcji Delphi - 

Date

), natomiast

 NOW

 - aktualną datę i czas ( zgodna z funkcją 

Now

 w Delphi). 

Poniżej przedstawiamy przykład zastosowania omówionych elementów: 

CREATE TABLE REPORTLOG 
(ReportUser VARCHAR(20) DEFAULT USER, 
ReportData DATE DEFAULT "TODAY", 
ReportDateTime DEFAULT "Now" 

Warto zaznaczyć,  że 

"TODAY"

 oraz

 "NOW

" muszą być podane w cudzysłowie, 

natomiast 

USER

 nie. Tekst 

USER

 podany w cudzysłowie będzie oznaczał wartość 

"USER"

 a nie nazwę aktualnego użytkownika. 

Dodawanie i usuwanie kolumn 

Dodając lub usuwając kolumnę z istniejącej tabeli korzystamy z instrukcji 

ALTER 

TABLE

. W przeciwieństwie do niektórych serwerów baz danych (np. Microsoft 

SQL Server) InterBase Server umożliwia usuwanie kolumn. Składnia dla 
dodawania kolumn wygląda następująco : 

ALTER TABLE CUSTOMER 
ADD PhoneNumber char(10) 

natomiast dla usuwania: 

ALTER TABLE CUSTOMER 
DROP PhoneNumber  

background image

516 

Część III 

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 

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 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 jest nazywana 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. 

background image

 Rozdział 17 Delphi w Interbase 

517

 

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 pracujące po stronie 
użytkownika, 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 generowanych przez wyjątki obsługiwane w 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ć więzy 

INVALID_STATE,

 

zdefiniowane powyżej, wprowadzamy następującą instrukcję w 

programie 

WISQL: 

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

Ponieważ istniejące więzy pozwalają 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 InterBase 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 systemie 

InterBase 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) 

background image

518 

Część III 

Domyślnie indeks jest uporządkowany według rosnących wartości klucza. 
InterBase pozwala także na uporządkowanie malejące, jeśli przy tworzeniu 
indeksu użyjemy słowa kluczowego 

DESCENDING

 np. 

CREATE DESCENDING INDEX SALE03 ON SALE (Amount) 

Tak utworzony indeks przyspiesza wykonanie niektórych zapytań, np.: 

SELECT * FROM SALE 
ORDER BY Amount DESCENDING 

Włączanie i wyłączanie indeksu 

InterBase dysponuje użytecznym mechanizmem wyłączania indeksu. Przyspiesza 
to wprowadzanie zmian do tabeli podstawowej. Wyłączony indeks włącza się ( 
i przy tym przebudowuje) dopiero po wprowadzeniu całej partii nowych danych. 
Dzięki temu można szybko dodać do tabeli dużą liczbę wierszy, unikając 
aktualizacji indeksu przy każdym nowym wierszu. Oto przykład wyłączenia 
indeksu: 

ALTER INDEX SALE02 INACTIVE 

i ponownego włączenia: 

ALTER INDEX SALE02 ACTIVE 

Ponowne uaktywnienie indeksu wymusza jego przebudowę. Należy zaznaczyć, że 
wyłączenie indeksu jest możliwe dopiero po jego całkowitym zwolnieniu - nie 
może on być wykorzystywany przez klucz główny lub obcy. Aby wyłączyć indeks 
używany przez więzy należy najpierw je usunąć. 

Wprowadzanie danych 

Instrukcja 

INSERT

  służy do wprowadzania danych do tabeli InterBase. 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ę WISQL): 

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) 

background image

 Rozdział 17 Delphi w Interbase 

519

 

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 i: 

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) 

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) 

background image

520 

Część III 

Wartości specjalne 

InterBase umożliwia korzystanie z domyślnych wartości specjalnych kolumn 

USER

TODAY

 i 

NOW

, jak to zaprezentowano w poniższym przykładzie: 

INSERT INTO REPORTLOG VALUES(USER, "TODAY", "NOW") 

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 rekordy: 

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) 

Podobnie jak 

INSERT

, instrukcja 

UPDATE

 umożliwia wykorzystanie specjalnych 

wartości stałych kolumnowych InterBase, jak pokazano w przykładzie: 

UPDATE SALE 
SET Amount=Amount-(Amount*.25) 
WHERE SaleDate>="TODAY"-30 

Dzięki 

TODAY

 dokonano wyboru aktualizowanych wierszy. W ten sposób zostaną 

zmodyfikowane wszystkie transakcje sprzedaży z ostatnich 30 dni. 

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 

background image

 Rozdział 17 Delphi w Interbase 

521

 

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

 

InterBase WISQL otwiera transakcję automatycznie (używając odpowiednika 
instrukcji 

SET TRANSACTION

) przy pierwszym załadowaniu. Przy opuszczaniu 

programu użytkownik jest proszony o 

zatwierdzenie dokonanych zmian. 

W każdym momencie pracy z WISQL można zapisać lub wycofać wprowadzone 
zmiany, posługując się odpowiednio poleceniami 

Commit Work

 

Rollback Work

 

z menu 

File

UWAGA: 

Oprócz zwykłej modyfikacji danych kontrola transakcji w InterBase uwzględnia 
także instrukcje DDL (Data Definition Language). Za ich pomocą można 
definiować i tworzyć obiekty bazy danych - takie, jak tabele i indeksy. Jeśli obiekt 
zostanie utworzony wewnątrz transakcji, to w przypadku jej wycofania jest 
usuwany. Ta zmiana jest natychmiast widoczna w całej bazie i na każdym 
poziomie izolacji transakcji (Transaction Isolation Level-TIL), aktywnym w danej 
chwili. 

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

522 

Część III 

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  

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 tutaj 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 wykonują pewne obliczenia na zbiorach danych. Przykładami 
takich funkcji są 

COUNT, SUM, AVG, MIN

 oraz 

MAX

. Podamy teraz kilka 

przykładów ich zastosowania: 

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%' 

background image

 Rozdział 17 Delphi w Interbase 

523

 

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

UPPER

.  

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

Formułując odpowiednie warunki w klauzuli 

WHERE

 określamy dodatkowe tabele 

w klauzuli 

SELECT FROM

 i łączymy pola będące ze sobą w relacji

.

 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 - do złączenia tabel 

CUSTOMER i SALE poprzez pole 

CustomerNumber

 - użyto znaku równości. 

Tabela wymieniona po lewej stronie znaku równości nazywana jest tabelą 
zewnętrzną, a po prawej - 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 złączenie lewostronne. Jest to najczęściej używane złączenie 
w zapytaniach SQL 

Złączenia wewnętrzne(inner)a 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 
spełniony jest warunek złączenia. Natomiast złączenie 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 

background image

524 

Część III 

wewnętrznej dołączane będą 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.  

Składnia złączenia w ANSI 

Składnia ANSI definiuje specjalne operatory złączeń, umieszczane w klauzuli 

FROM

. Składnię dla lewostronnego wewnętrznego złączenia zaprezentujemy na 

przykładzie: 

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

Należy zaznaczyć, że domyślnie przyjmuje się słowo kluczowe 

INNER

 (złączenie 

wewnętrzne). Jak pokazano wcześniej, InterBase także używa takiej składni do 
zdefiniowania lewostronnego wewnętrznego złączenia : 

SELECT CUSTOMER.CustomerNumber, SALE.Amount 
FROM CUSTOMER, SALE 
WHERE 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

. Oczywiście nie ma potrzeby rozróżniania 

prawostronnego i 

lewostronnego złączenia wewnętrznego, bowiem zgodnie 

z podaną zasadą dają one ten sam wynik. 

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

background image

 Rozdział 17 Delphi w Interbase 

525

 

W tym zapytaniu 

CUSTOMER

 i 

SALE

  złączone są 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 

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 złączana jest ze sobą dla 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, dla SALE, korzysta z dwóch różnych aliasów. 

background image

526 

Część III 

Złączenia pełne (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, wtedy łą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 DISTINCT C.CustomerNumber, S.SaleNumber 
FROM CUSTOMER C FULL JOIN SALE S 
ON 1=1 

UWAGA 

Powyższy prosty kod zawiera tożsamość  1=1, bowiem analizator składniowy 
InterBase wymaga klauzuli ON we wszystkich złączeniach, nawet pełnych, dla 
których praktycznie nie jest to konieczne. 

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 realizacji zapytania. W niektórych systemach zatrzymanie procesu 
jest możliwe tylko przez zrestarowanie komputera serwera. Należy więc unikać 
tworzenia iloczynu kartezjańskiego - zwłaszcza wtedy, 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) 

background image

 Rozdział 17 Delphi w Interbase 

527

 

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

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

. InterBase 

Server oraz ANSI SQL wymagają, 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, InterBase 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ą 

background image

528 

Część III 

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. 

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

background image

 Rozdział 17 Delphi w Interbase 

529

 

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

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

background image

530 

Część III 

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. 

Dodatkowe ograniczenia obowiązują przy tworzeniu perspektyw 
modyfikowalnych. 

„Instrukcja 

SELECT

 może dotyczyć tylko jednej tabeli lub innej 

modyfikowalnej perspektywy. 

„Kolumny, które nie zostały włączone do perspektywy, muszą dopuszczać 

wartość 

NULL

, aby było możliwe wykonanie instrukcji 

INSERT.

 

„Instrukcja 

SELECT

  użyta w 

definicji perspektywy nie może zawierać 

podzapytań, predykatu 

DISTINCT

, klauzuli 

HAVING

, funkcji sumarycznych, 

złączeń, funkcji użytkownika oraz procedur pamiętanych. 

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

 

Procedury pamiętane (Stored Procedures) 

Procedury pamiętane są kompilowanymi programami SQL (często zawierającymi 
wiele instrukcji SQL), przechowywanymi wraz z innymi obiektami bazy danych. 
W InterBase  występują dwa podstawowe rodzaje tych procedur: procedury 
wyboru
 (ang. select procedures) i procedury wykonywalne (ang. executable 
procedures).  
Procedur wyboru można użyć zamiast tabel lub perspektyw 
w instrukcji SELECT. Ponadto muszą one zwracać wynik ( jedną lub wiele 
wartości). Natomiast w przypadku procedur wykonywalnych nie jest to konieczne. 

Procedury pamiętane tworzy się instrukcją 

CREATE PROCEDURE

. Poniżej 

przedstawiamy przykład procedury w InterBase: 

background image

 Rozdział 17 Delphi w Interbase 

531

 

CREATE PROCEDURE listcustomers RETURNS (LastName CHAR(30),  

 FirstName CHAR(30)) 

AS BEGIN 
FOR SELECT LastName, FirstName FROM CUSTOMER 
INTO :LastName, :FirstName 
DO SUSPEND; 
END 

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

CREATE PROCEDURE listcustomersbystate (LastName CHAR(30),  

 FirstName CHAR(30)) 

RETURNS (LastName CHAR(30), FirstName CHAR(30)) 
AS BEGIN 
FOR SELECT LastName, FirstName FROM CUSTOMER 
WHERE State= :State and LastName like :LastNameMask 
INTO :LastName, :FirstName 
DO SUSPEND; 
END 

Procedury wyboru (Select Procedures) 

W procedurze wyboru rodzaj zwracanych danych definiuje się po słowie 
kluczowym 

RETURNS

 (jak to pokazano w poniższym przykładzie): 

CREATE PROCEDURE listcustomers (State CHAR(2)) 
RETURNS (LastName CHAR(30)) 
AS  
BEGIN 
FOR SELECT LastName 
FROM CUSTOMER 
WHERE State= :State 
INTO :LastName 
DO  
 SUSPEND; 
END 

Warto zwrócić uwagę, że w powyższym przykładzie konstrukcja 

FOR  

SELECT ... DO

 powoduje przekazanie wierszy wynikowych do miejsca 

wywołania. Instrukcja 

SUSPEND

 przerywa wykonanie procedury - aż do chwili, 

gdy wywołanie zażąda następnego wiersza. Wartości przypisane parametrom 
wyjściowym zostają przekazane przed przerwaniem realizacji procedury. 

Procedury wykonywalne 

Procedury wykonywalne różnią się od procedur wyboru tym, że nie wymagają 
użycia instrukcji 

RETURNS

. Oto przykład procedury wykonywalnej w InterBase 

SQL: 

background image

532 

Część III 

CREATE PROCEDURE insertitem (ItemNumber Integer, Description  

 Char(30), Price Float) 

AS 
BEGIN 
 

INSERT INTO ITEM (ItemNumber, Description, Price) 

 

VALUES (:ItemNUmber, :Description, :Price); 

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 ich tworzyć korzystając 
z dowolnego edytora tekstów. Warto tu przypomnieć,  że powinny one zawierać 
wszystkie niezbędne instrukcje 

CONNECT i SET TERM

. Skrypt SQL można 

wykonać, wybierając polecenie 

Run an

 

SQL Script

 z menu 

File

 programu 

WISQL. Przykład zawiera listing 17.1 

Listing 17.1. Procedura pamiętana w

 skrypcie SQL. 

CONNECT "C:\DATA\IB\SALES.GDB" USER "SYSDBA" PASSWORD  

 "masterkey"; 

SET TERM ^ ; 
CREATE PROCEDURE GET_CUSTOMER_BY _STATE (State CHAR(2)) 
RETURNS (LastName CHAR(30)) 
AS 
BEGIN 
 

FOR SELECT LastName 

 FROM 

CUSTOMER 

 

WHERE State= :State 

 INTO 

:LastName 

 DO 
  SUSPEND; 
END^ 
SET TERM ; ^  
EXIT; 

Instrukcja 

CONNECT 

na początku pliku ustala połączenie z bazą danych. 

Następnie 

SET TERM

 zmienia znak kończący instrukcje SQL - z domyślnego 

średnika (;) na (^). Zmiana ta powoduje, że instrukcje zawarte w przechowywanej 
procedurze nie są wykonywane, gdy jest ona dopiero tworzona - za pomocą 

CREATE PROCEDURE

. Instrukcja 

SET TERM

 przywraca domyślny znak 

zakończenia instrukcji. 

background image

 Rozdział 17 Delphi w Interbase 

533

 

Uruchamianie procedur pamiętanych 

Procedury pamiętane InterBase uruchamiamy instrukcją 

EXECUTE PROCEDURE

Jej składnia przedstawia się następująco: 

EXECUTE PROCEDURE procedurename parameters 

W miejsce 

procedurename

  należy podać nazwę uruchamianej procedury, 

a w miejsce 

parameters

 jej parametry np.: 

EXECUTE PROCEDURE GET_CUSTOMER_BY_STATE "MO" 

Ta instrukcja uruchamia procedurę pamiętaną 

GET_CUSTOMER_BY_STATE

 

z parametrem "MO". 

Wyjątki (Exceptions) 

Wyjątki są mechanizmami przekazującymi aplikacji określone przez programistę 
sygnały o błędach, jakie powstały w czasie wykonania procedur pamiętanych 
i procedur  zdarzeń. W 

InterBase wyjątki definiujemy instrukcją 

CREATE 

EXCEPTION,

 natomiast instrukcja 

EXCEPTION

  służy do ich przechwycenia 

i wygenerowania odpowiedniego komunikatu. W listingu 17.2 pokazano sposób 
definiowania wyjątku i jego wykorzystania w procedurze przechowywanej. 

Listing 17.2. Wyjątek InterBase w

 

procedurze pamiętanej.

 

CONNECT "C:\DATA\IB\SALES.GDB" USER "SYSDBA" PASSWORD  

 "masterkey"; 

CREATE EXCEPTIONSALE_TOO_LOW; 
"The sale amount is to low. Only purchases of $1 or more are 
allowed"; 

SET TERM ^ ; 
CREATE PROCEDURE insertsale(SaleNumber int, SaleDate date,  

 CustomerNumber int, ItemNumber int, Amount float) 

AS 
BEGIN 
 If 

(:Amount<1)THEN 

  EXCEPTION 

SALE_TOO_LOW; 

 ELSE 
  INSERT 

INTO 

SALE 

 

 

VALUES (:SaleNumber, :SaleDate, :CustomerNumber,  

 

 

 :ItemNumber, :Amount); 

END^ 
SET TERM ; 
EXIT; 

background image

534 

Część III 

Ta procedura ustala minimalną kwotę sprzedaży - 1$. Instrukcja wyboru 

IF...THEN

 sprawdza, czy kolumna 

Amount

 w wierszu przygotowanym do 

wprowadzenia zawiera kwotę mniejszą niż 1$. Natomiast instrukcja 

EXCEPTION

 

została użyta do zgłoszenia wyjątku 

SALE_TOO_LOW

, gdy kwota ta jest mniejsza 

niż 1 $. 

Procedury zdarzeń (Triggers) 

Procedury zdarzeń  są to (podobnie jak procedury pamiętane) podprogramy SQL, 
uruchamiane gdy dane z zadanej tabeli są wprowadzane, modyfikowane lub 
usuwane. Procedura zdarzenia jest skojarzona ze specyficzną operacją 
dokonywaną na tabeli (zdarzeniem): wstawianiem wiersza, modyfikacją lub 
usuwaniem. Oto odpowiedni przykład w InterBase SQL: 

CREATE TRIGGER SALEDelete FOR CUSTOMER 
BEFORE DELETE 
AS 
BEGIN 
 

DELETE FROM SALE 

 WHERE 

CustomerNumber=OLD.CustomerNUmber; 

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. 

Należy zwrócić uwagę na użycie zmiennej kontekstowej 

OLD. O

dwołuje się ona 

do bieżącej wartości kolumny w danym wierszu przed wykonaniem operacji 

UPDATE

 lub 

DELETE

. Zmienna kontekstowa 

NEW 

odwołuje się do nowej 

wartości, która ma być wprowadzona realizacją 

INSERT

 lub 

UPDATE

Warto też odnotować wystąpienie słowa kluczowego 

BEFORE

. Procedura 

zdarzenia może być uruchomiona przed (before) lub po (after

INSERT, 

UPDATE, DELETE

W InterBase z danym zdarzeniem można skojarzyć do 32768 procedur zdarzenia. 
Dlatego w sytuacji, gdy jedno zdarzenie aktywuje wiele procedur obsługi, 
powinno się ustalić kolejność ich uruchamiania. Służy do tego słowo kluczowe 

POSITION, 

które zastosowano w poniższym przykładzie: 

CREATE TRIGGER SALEDelete FOR CUSTOMER 
BEFORE DELETE 
POSITION 0 
AS 
BEGIN 
 

DELETE FROM SALE 

background image

 Rozdział 17 Delphi w Interbase 

535

 

 WHERE 

CustomerNumber=OLD.CustomerNUmber; 

END 

Numery mogą przyjmować wartości od 0 do 32767, a procedura o niższym 
numerze wykonywana jest najpierw. Procedury zdarzeń z tym samym numerem 
uruchamiane są w porządku losowym. 

Funkcje 

InterBase umożliwia rozszerzanie swojej odmiany SQL - przez dołączenie 
własnych funkcji. Zasadniczo można zdefiniować  własne funkcje w zewnętrznej, 
współdzielonej bibliotece funkcji ( w Windows jest to DLL) i udostępniać je za 
pomocą instrukcji 

DECLARE EXTERNAL FUNCTION

. Przy takim podejściu są 

one przechowywane w 

naszej bazie danych i 

zachowują się jak funkcje 

wbudowane. Oto przykład prostej biblioteki DLL, w której zdefiniowano dwie 
funkcje. 

library IBUDFLIB; 

uses 
 SysUtils, 
 Classes; 

function MaxInt(var Int1, Int2 :Integer) : Integer; far cdecl  

 export; 

begin 
 

if (Int1>Int2) then Result:=Int1 

 else 

Result:=Int2; 

end; 

function MinInt(var Int1, Int2 :Integer) : Integer; far cdecl  

 export; 

begin 
 

if (Int1<Int2) then Result:=Int1 

 else 

Result:=Int2; 

end; 
 exports 
 MaxInt, 
 MinInt; 

begin 
end. 

A teraz przedstawimy przykład skryptu SQL, który udostępni te funkcje dla 
InterBase: 

background image

536 

Część III 

CONNECT "C:\DATA\IB\SALES.GDB" USER SYSDBA PASSWORD  

 masterkey; 

DECLARE EXTERNAL FUNCTION MAXINT 
INTEGER, INTEGER 
RETURNS INTEGER BY VALUE 
ENTRY_POINT "MaxInt" MODULE_NAME "IBUDFLIB.DLL"; 

DECLARE EXTERNAL FUNCTION MININT 
INTEGER, INTEGER 
RETURNS INTEGER BY VALUE 
ENTRY_POINT "MinInt" MODULE_NAME "IBUDFLIB.DLL"; 

EXIT; 

Teraz podamy kilka uwag praktycznych dla osób chcących wzbogacać InterBase 
SQL o własne funkcje,: 

„InterBase pobiera funkcje z biblioteki DLL, posługując się nazwą funkcji 

i rozróżniając w niej wielkie i małe litery. Jeśli nie zadba się o poprawne użycie 
wielkich i małych liter w nazwie funkcji, w instrukcji 

DECLARE EXTERNAL 

FUNCTION

 InterBase nie będzie w stanie jej zlokalizować. 

„Parametry są przekazywane przez zmienną (zwracamy uwagę na słowo 

kluczowe VAR na liście parametrów funkcji MaxInt i MinInt) a nie przez 
wartość. Dlatego zadeklarowane funkcje powinny spodziewać się wskaźników 
do zmiennych, a nie ich wartości. 

„InterBase przyjmuje, że funkcja jest zdefiniowana z wywołaniem 

cdecl

. Nie 

należy więc używać wywołań 

far pascal

stdcall

or

 oraz 

safecall,

 bowiem nie będą funkcjonowały poprawnie. 

„Wyniki mogą być zwracane przez zmienną lub przez wartość. Jeśli wybierzemy 

przekazanie przez zmienną, należy pamiętać, aby zwrócić wskaźnik do wartości 
a nie samą wartość, inaczej bowiem InterBase zatrzyma się próbując wykonać 
funkcję. 

Zdefiniowane funkcje 

MinInit

 i 

MaxInit

 możemy przetestować, wykonując 

następującą instrukcję w WISQL: 

SELECT MaxInt(3,5) FROM CUSTOMER; 

Składnia InterBase SQL wymaga użycia nazwy co najmniej jednej tabeli 
w instrukcji 

SELECT

. W 

podanym przykładzie wykorzystaliśmy tabelę 

CUSTOMER. Funkcja 

MaxInt

  będzie wykonywana dla każdego wiersza tabeli, 

więc uruchamiając podany kod możemy otrzymać wielokrotnie powtarzaną 
wartość. 

background image

 Rozdział 17 Delphi w Interbase 

537

 

WSKAZÓWKA 

Możemy utworzyć jednowierszową tabelę InterBase, którą wykorzystamy do 
tworzenia zapytań nie odnoszących się do tabel. Tabela taka może być 
wykorzystana w instrukcji 

SELECT

 wtedy, gdy praktycznie nie korzystamy 

z danych  z żadnej tabeli. Oto przykład utworzenia i użycia takiej tabeli (każde 
polecenie należy wykonać osobno w WISQL): 

CREATE TABLE DUAL 
(A CHAR(1)); 
INSERT INTO DUAL (A) VALUES ('A'); 

SELECT 22/7 AS ApproximatePI 
FROM DUAL; 

Deklarację zewnętrznej funkcji można usunąć z bazy za pomocą instrukcji 

DROP 

EXTERNAL FUNCTION. 

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 mogą się one okazać  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. 

UWAGA 

Nie można wykonywać tych instrukcji samodzielnie w programie WISQL. Są one 
przeznaczone do użycia tylko w procedurach pamiętanych. 

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

background image

538 

Część III 

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. InterBase posiada 

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

CLOSE

) i ponownie otworzyć (

OPEN

) kursor. 

UWAGA 

Mimo  że InterBase 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ć w obie strony w obiektach 

TDataSets

 takich jak 

TQuery i TTable

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

background image

 Rozdział 17 Delphi w Interbase 

539

 

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  

Administrowanie bazą Interbase 

Zwykle osoby tworzące bazy danych wykonują tylko najprostsze czynności 
administracyjne. W naszych rozważaniach ograniczymy się do podstawowych, 
niezbędnych narzędzi administrowania bazą. Oczywiście skomplikowane 
problemy najlepiej pozostawić do rozwiązania administratorom bazy danych.  

Archiwizacja i odzyskiwanie danych 

Podstawową troską projektantów, administratorów i użytkowników baz danych 
jest bezpieczeństwo danych. Musimy więc przede wszystkim zabezpieczyć bazę na 
wypadek uszkodzenia dysków, na których jest umieszczona. W tym celu tworzymy 
kopie bazy danych. Nie wystarczy jednak po prostu skopiować pliki z danymi. 
Część z nich może być bowiem akurat w użyciu i znajdować się częściowo 
w pamięci serwera. Dlatego zwykła kopia, sporządzona narzędziami systemu 
operacyjnego, nie daje prawdziwego obrazu stanu bazy w danym momencie i nie 
gwarantuje jej spójności. Dopiero Server Manager zapewni nam właściwą 
archiwizację danych. Postępujemy następująco : 

1.  Uruchamiamy program Server Manager 

2. Klikamy 

File\Server Login

 i łączymy się z serwerem. 

3. Wybieramy 

Tasks\Backup

 - aby wyświetlić okno dialogowe do archwizacji  

4. W lini 

Database Path

 wpisujemy pełną  ścieżkę dostępu do danych, które 

chcemy archiwizować. Gdy archiwizujemy odległą bazę na platformie innej niż 
NetWare, wybieramy 

Remote

5. Wpisujemy nazwę pliku lub urządzenia, którego chcemy użyć, w linii 

Backup

 

File or

 

Device

. W Windows NT można wpisać domyślne urządzenie taśmowe 

jako 

\\.\tape0

6. Aby rozpocząć archiwizację klikamy 

OK

. Sytuację ilustruje rys 17.1. 

Odzyskiwanie zarchiwizowanych danych jest po prostu odwróceniem poprzednio 
opisanego procesu. Wybieramy opcję 

Restore

 z menu 

Tasks

, określając swoją 

kopię jako 

Restore Source

, a swoją bazę jako

 Restore Destination

 

background image

540 

Część III 

Zarządzanie kontami użytkowników 

Aby móc łączyć się z bazą danych, trzeba być zarejestrowanym jako użytkownik 
i znać swoje hasło. Do utworzenia konta nowego użytkownika można użyć 
polecenia 

Task\User Security

 z menu programu Server Manager. Postępujemy 

wtedy zgodnie z poniższą procedurą: 

1.  Uruchamiamy program Serwer Manager. 

2. Klikamy 

File\Sever Login

 i łączymy się z serwerem 

3. Wybieramy 

Tasks\User Security

, aby wyświetlić okienko dialogowe InterBase 

Security. 

4. Klikamy przycisk 

Add User

 . 

5. Wprowadzamy nazwę i hasło dla nowego użytkownika. 

6. Klikamy 

OK

, aby dodać nowego użytkownika. Ilustruje to rysunek 17.2 

 

Rysunek 17.1 
Wykorzystanie 
Server Managera 
do archiwizacji 
bazy danych 

 

Rysunek 17.2 
Dodawanie nowego 
uzytkownika 
InterBase 
w programie Server 
Manger. Okno 
dialogowe User 
Configuration 

background image

 Rozdział 17 Delphi w Interbase 

541

 

Po utworzeniu konta użytkownika możemy się  łączyć z dowolną bazą danych 
InterBase’a. Jednak korzystanie z bazy jest możliwe dopiero po nadaniu praw 
dostępu do poszczególnych obiektów bazy. 

Konfiguracja serwera 

Jest parę sposobów dojścia do okna dialogowego 

Interbase Server Configuration

Umożliwia to Server Manager poleceniem 

Config

 z menu 

Task\Server

. Możemy 

też kliknąć prawym przyciskiem myszy ikonę 

InterBase Server

 na pasku zadań 

Windows i wybrać polecenie 

Properties

W oknie dialogowym 

Server Configuration

 parametry serwera InterBase zostały 

podzielone na dwie klasy: IB (InterBase) Settings i OS (Operating system
Settings. Ustawienia IB składają się z dwóch parametrów:

 Database Cache

 

Client map size

Database cache

 określa liczbę stron 

zarezerwowanych na pamięć cache dla baz. W zasadzie wszystkie bazy można by 
umieścić w pamięci RAM, jeśli cache będzie odpowiednio duży. Przyspieszyłoby 
to znacznie wszelkie operacje na bazie. Jednak ustalając zbyt duży cache 
w stosunku do zasobów komputera powodujemy, że system operacyjny przenosi 
część pamięci cache na dysk, zmniejszając w ten sposób wydajność serwera. 

Client map size

 określa rozmiar bufora rezerwowanego dla każdego 

połączonego z bazą klienta. Zwykle nie ma potrzeby zmieniania tego parametru - 
można go zwiększyć przy transmisjach dużych porcji danych (np. BLOB) do i 
z aplikacji klienta. 

Ustawienia OS zawierają trzy pozycje: 

Min Process Working Set

,

 Max Process

 

working

 

Set

 i 

Process

 

Priority

 

Class

Min

 i 

Max Process working Set

 kontrolują 

wielkość oddanej do dyspozycji serwera bazy danych pamięci fizycznej. 
Podwyższając wartość maksymalną pozwalamy na zwiększenie ustawień Database 
Cache, jednak zbyt duża wartość tego parametru może powodować  błędy 
stronnicowania (ang. page faults). W Windows NT można sprawdzić - poprzez NT 
Performance Monitor - czy parametry pracy serwera wymagają korekty. Aby 
określić, czy ustawienia 

Min

 i 

Max

 

process

 

Working

 

Set

 są właściwe, wykonujemy 

następujące kroki: 

1. Jeśli InterBase server jest już uruchomiony, włączamy program NT 

Performance Monitor (najlepiej na tym samym komputerze) 

2.  Wybieramy nowy wykres i naciskamy CTRL+I - aby dodać nowy licznik do 

wykresu. 

3. Wybieramy 

Object Process, Instance

IBSERVER, Counter:Page File Bytes

 

i klikamy 

Add

4.  Dodajemy licznik dla 

IBSERVER

 

Page

 

faults/sec

 i 

% Processor

 

Time

. (patrz 

rysunek 17.3) 

background image

542 

Część III 

Jeśli stwierdzimy dużą ilość błędów stronicowania (Page Faults) lub % Processor 
Time jest mniejszy niż 50%, można zwiększyć zakres Process Working Set, co 
powinno poprawić wydajność serwera. Wprowadzone zmiany zostaną 
uwzględnione przy następnym starcie serwera. 

Process Priority Class określa priorytet procesów inicjowanych przez serwer 
InterBase wśród innych procesów aktualnie uruchomionych na komputerze 
serwera. Zmieniając priorytet na High można uzyskać znaczny wzrost wydajności 
- szczególnie na komputerach wydzielonych (ang. dedicated) wyłącznie do pracy 
z serwerem InterBase, oraz w sytuacjach, gdy duża ilość aplikacji jest aktywna na 
komputerze serwera. Zmiany tego ustawienia realizowane są natychmiast po 
kliknięciu 

Apply

Przeglądanie statystyki blokad 

Systemy klient/serwer wykorzystują pesymistyczny mechanizm sterowania 
współbieżnością, więc są wyposażane w różnorodne narzędzia obsługujące 
blokady i rywalizację o dostęp. InterBase, jak większość wielodostępnych DBMS, 
odpowiednio blokuje zasoby - aby zapobiec jednoczesnemu, niekontrolowanemu 
procesowi wprowadzania zmian przez wielu użytkowników. Oczywiście niezbędne 
są narzędzia do kontrolowania i 

usuwania problemów związanych 

z wielodostępem. Blokady zakładane przez serwer InterBase’a można obejrzeć 
wybierając polecenie 

Tasks\Lock Manager Statistics

 w programie Server Manager 

(patrz rysunek 17.4).  

 

Rysunek 17.3 
Użycie NT 
Performabce 
Monitor do 
określenia potrzeb 
serwera 
InterBaze’a 
w zakresie RAM. 

background image

 Rozdział 17 Delphi w Interbase 

543

 

Program zarządzający blokadami w InterBase korzysta ze specjalnej tablicy do ich 
śledzenia. Są w 

niej informacje na temat statusu wszystkich blokad. 

LOCK_HEADER BLOCK podaje szeroki zakres danych podsumowujących - jak 
np. aktualny i maksymalny rozmiar zablokowanych tablic, liczbę zakleszczeń itp. 
W części szczegółowej dotyczącej poszczególnych procesów możemy sprawdzić, 
który proces zarządził daną blokadę, które blokady zostały zwolnione, a które 
czekają itd. Wszystkie te informacje mogą się okazać pomocne w rozwiązywaniu 
problemów związanych z blokadami, jak np. zakleszczenie (ang. deadlock

Sprawdzanie integralnośći bazy 

Oprócz regularnej archiwizacji danych należy - dla zapewnienia sobie 
wiarygodności danych w bazie - podjąć inne środki. Powinno się regularnie 
sprawdzać ich poprawność, najlepiej bezpośrednio przed lub po dokonaniu 
archiwizacji. Umo¿liwia to polecenie menu 

Maintance\Database Validation

 

w InterBase Server Manager. Rysunek 17.5 pokazuje okno dialogowe 

Database

 

Validation

Database Validation wykonuje trzy podstawowe funkcje: inwentaryzuje 
uszkodzone struktury na dysku, pokazuje strony z błędną alokacją i zwalnia 
miejsce na dysku po "osieroconych" stronach. Standardowo korekty w strukturze, 

 

RYSUNEK 17.4 
Okno Lock 
Manager Statistics 
wyświetla ważne 
informacje 
dotyczące blokad 
InterBase. 

 

Rysunek 17.5 
Sprawdzanie bazy - 
okienko dialogowe 
Database 
Validation 

background image

544 

Część III 

które nie dotyczą danych użytkownika wykonywane są automatycznie. Można 
jednak zablokować przeprowadzanie zmian przez wybranie opcji 

Read-only 

validation

Każdy błąd wykryty przez program sprawdzający wyświetlany jest w okienku. Po 
kliknięciu przycisku 

Repair

 program podejmie próbę ich naprawy. Program 

sprawdzający może także warunkowo pomijać wykryty błąd sumy kontrolnej. Aby 
naprawić uszkodzoną bazę wykonujemy kroki opisane poniżej 

1. Kopiujemy bazę z pominięciem programu ServerManager Backup. Można użyć 

zwykłego polecenia 

Copy

 albo Eksploratora Windows. Oprogramowanie 

InterBase nie archiwizuje bowiem uszkodzonych baz. 

2. Dla sprawdzenia uszkodzeń w bazie wybieramy polecenie 

Maintenance\ 

Database

 

Validation

3. Po zakończeniu weryfikacji klikamy przycisk 

Repair

 - aby zaznaczyć lub 

naprawić uszkodzone struktury 

4. Jeśli powstały błędy sumy kontrolnej ponownie wybieramy polecenie 

Database

 

Validation

, tym razem jednak z opcją

 Ignore checksum errors

 ustawioną na 

TRUE

5. Kiedy proces się zakończy klikamy 

Repair

 - aby naprawić nowo wykryte 

uszkodzenia. 

6. Uruchamiamy Database Validation, wybierając tym razem opcję 

Read

-

only

 

validation

. Warto zwrócić uwagę na fakt, że zwolnione stronice nie są już 

wyświetlane, a niepełne rekordy zaznaczone jako uszkodzone. Każdy rekord 
zaznaczony jako uszkodzony nie będzie archiwizowany przez program Backup 
z Server Managera. 

7. Archiwizujemy naprawioną bazę programem archiwującym z Server Managera. 

Uszkodzone rekordy zostaną oczywiście wyłączone z archiwizacji. 

8. Odzyskujemy zarchiwizowaną bazę. Ta operacja pozwoli na przebudowanie 

pewnych struktur wewnętrznych i usunięcie uszkodzonych części z bazy. 

9. Uruchamiamy Database Validation po raz ostatni - aby sprawdzić, czy 

rzeczywiście baza została naprawiona. Tym razem wybieramy opcję 

Read

-

only

 

validation

Statystyki bazy danych 

Opisywane tutaj polecenia Server Managera wymagają aktywnego połączenia 
z daną bazą. Musimy być więc zarejestrowani na serwerze oraz połączeni z bazą 
(polecenie

 File\Database Connect

 w Server Manager).  

background image

 Rozdział 17 Delphi w Interbase 

545

 

Polecenie 

Task\Database Statistics

 wyświetla różnorodne informacje odnoszące 

się do bazy. Są  wśród nich między innymi: data utworzenia bazy, najstarsza 
aktywna transakcja, rozmiar strony, liczba zdefiniowanych cieni. Niektóre z tych 
danych są także dostępne poprzez polecenia 

Maintance\Database Properties

Rysunek 17.6 pokazuje okno utworzone przez 

Database

 

Statistics

Gdy okno 

Database Statistics

 jest na ekranie, można wybrać polecenie 

Database

 

Analysis

 z menu 

View

 - aby zobaczyć ilość stron alokowanych dla bazy i ich 

wypełnienie. Ułatwi to nam zrozumienie, jak struktura logiczna bazy wpływa na 
fizyczne rozmieszczenie danych. 

Czyszczenie bazy (Database sweeping) 

Polecenie 

Maintenance\Database Sweep

 zwalnia miejsce po starych wersjach 

rekordów z odrzuconych transakcji. Chociaż nie jest to niezbędne, należy zamknąć 
bazę na czas jej oczyszczenia - ze względu na wpływ tej operacji na działanie 
programów użytkowników. Oczyszczanie dużych baz może bowiem drastycznie 
spowolnić realizację innych procesów. 

Odzyskiwanie transakcji (Transaction Recovery) 

Każda transakcja, obejmująca zakresem wiele baz, jest w InterBase zatwierdzana 
dwufazowo. Zapewnia to zachowanie niepodzielności transakcji dla wszystkich 
baz składowych. Dwufazowe zatwierdzanie może się nie udać ze względu na 
zerwanie połączenia sieciowego lub awarii dysku, która uniemożliwi dostęp do 
części baz. Polecenie 

Maintaince\Transaction Recovery

 ma za zadanie obsługę 

transakcji pozostających w „zawieszeniu” po nieudanym procesie dwufazowego 
zatwierdzania. Pozwala ono odzyskać te transakcje i - w zależności od sytuacji - 
wycofać lub zatwierdzić. 

 

Rysunek 17.6 
Okno Database 
Statistics 
z podstawowymi 
informacjami 
o bazie.