background image

 

Rozdział 23  

Sterowanie współbieżnością 

Termin sterowanie współbieżnością  (ang.  concurrency control) odnosi się do 
jednoczesnego współdzielenia zasobów przez wielu użytkowników. Można tu 
wyróżnić dwa główne zagadnienia: izolowanie transakcji i systemy sterowania 
współbieżnością. Poziomy izolowania transakcji określają zakres dostępu 
w ramach danej transakcji dla modyfikacji bazy danych przeprowadzanych 
w innych współbieżnych transakcjach. Systemy sterowania współbieżnością ściśle 
wiążą się z zagadnieniem izolowania transakcji - kontrolują one nie tylko zakres 
dostępu w ramach jednej transakcji - dla aktualizacji przeprowadzanych przez inne 
transakcje, ale również sposób przeprowadzania tych aktualizacji.  

Izolowanie transakcji 

Terminem  Izolowanie transakcji (ang. transaction isolation) opisujemy 
mechanizm wykorzystywany przez systemy zarządzania bazami danych dla 
odseparowania danej transakcji od rezultatów działania innej. Izolowanie 
transakcji w Delphi odbywa się na trzech oddzielnych poziomach, z których każdy 
ma odrębny charakter. Poziomy te (w skrócie poziomy TIL, od słów Transaction 
Isolation
  Level) określają zakres dostępu, jakim możemy dysponować w ramach 
danej transakcji do modyfikacji dokonywanych w bazie danych przez inną 
transakcję współbieżną.  

System Delphi obsługuje za nas problemy związane z transakcjami, tzn. sam ustala 
domyślny poziom TIL oraz automatycznie rozpoczyna i zatwierdza transakcje 
podczas uaktualniania bazy danych przez naszą aplikację. Szerszą kontrolę 
zapewni nam Passthrough SQL lub komponent 

TDatabase

. Transakcjami zaleca 

się sterować za pośrednictwem komponentu 

TDatabase

, gdyż tylko wówczas, 

przetwarzane przez nas transakcje będą widoczne dla Delphi.  

Wybór odpowiedniego poziomu izolowania transakcji 

Większość serwerów baz danych, łącznie z InterBase, obsługuje trzy oddzielne 
poziomy izolowania transakcji. Ujmując rzecz ściśle z 

punktu widzenia 

zarządzania, w całej aplikacji powinno się stosować ten sam poziom TIL. Jeszcze 
lepszym podejściem jest przyjęcie tego samego poziomu TIL we wszystkich 
aplikacjach bazy danych. Taka strategia projektowa zapewni lepszą ochronę 

background image

650 

Część IV 

integralności bazy danych i sprawi, że efekty przeprowadzanych modyfikacji będą 
zgodne z naszymi oczekiwaniami.  

Z drugiej strony jednak, wymagania konkretnej aplikacji mogą uniemożliwić nam 
przyjęcie tak jednolitej strategii. Obstawanie przy niej za wszelką cenę może 
prowadzić do zbyt restrykcyjnej lub niepewnej ochrony integralności bazy danych. 
Mogłoby wówczas okazać się,  że główne czynności naszej bazy to niepotrzebne 
blokowanie (lock) tablic lub zapobieganie nieszkodliwym aktualizacjom.  

WSKAZÓWKA  

Implementując poziomy TIL musimy najpierw zapewnić integralność bazy danych, 
a dopiero potem optymalizować wydajność na drodze udoskonalania przyjętego 
schematu izolowania transakcji. 

Klasyczne problemy izolowania transakcji 

Problemy występujące przy wyborze schematów izolowania transakcji można 
podzielić na pięć grup: 

„odczyty  pozorne  (ang.  dirty reads): mogą wystąpić, gdy w ramach danej 

transakcji odczytuje się nie zatwierdzone modyfikacje wprowadzone w innej 
transakcji. Ponieważ nie zatwierdzoną modyfikację można wycofać (ang. roll 
back
), rezultatem jej odczytania w danej transakcji (jeszcze zanim została 
wycofana), będzie właśnie odczyt pozorny. 

„odczyty  niepowtarzalne  (ang.  nonrepeatable reads): występują wtedy, gdy 

danej transakcji zezwala się na modyfikację wierszy, do których odczytu ma 
dostęp inna transakcja. Odczyty takie nazywamy niepowtarzalnymi, ponieważ 
kolejne odczyty - z powodu modyfikacji wprowadzanych w danej transakcji ze 
strony innej transakcji - będą się między sobą różnić. Ze względu na swoją 
naturę, transakcja poziomu 

READ COMMITTED 

umożliwia odczyty 

niepowtarzalne, ponieważ modyfikacje dokonane w ramach innych transakcji 
mogą być odczytywane w trakcie zatwierdzania. 

„wiersze-widma (ang. phantom rows): pojawiają się wtedy, gdy w transakcji 

zezwala się na wybór niekompletnego zbioru nowych wierszy zapisanych 
w ramach innej transakcji. Poziom izolowania transakcji, określany jako 

READ 

COMMITTED, 

nie zapobiega występowaniu wierszy-widm.  

„zgubione aktualizacje (ang. lost updates): występują wtedy, gdy w ramach 

transakcji omyłkowo nadpisana zostanie (overwrite) modyfikacja dopiero co 
wprowadzona w innej współbieżnej transakcji.  

background image

 Rozdział 23 Sterowanie współbieżnością 

651

 

„efekty uboczne aktualizacji (ang. udpdate side effects): występują wówczas, gdy 

nie wprowadzono odpowiednich ograniczeń, mających na celu ochronę 
integralności i gdy wartości w wierszach są od siebie zależne. Kiedy w ramach 
dwóch (lub więcej) jednoczesnych transakcji odczytywane są i uaktualniane te 
same dane, niepożądane efekty uboczne mogą wystąpić wtedy, gdy pierwsza 
transakcja kopiuje wartość z jednego wiersza do innego, po czym druga 
zmienia tę wartość w pierwszym wierszu. Taki typ załamań integralności 
określany jest terminem transakcji przeplatanych (ang. interleaved 
transactions). 

Sposób rozwiązywania powyższych problemów przez poziom TIL (który 
wybieramy z 

wykorzystaniem własności 

TransIsolation 

komponentu 

TDatabase

), zależy od platformy RDBMS i poziomu TIL serwera (oba aspekty 

omawiamy w poniższych podrozdziałach). 

Zarządzanie transakcjami z wykorzystaniem TDatabase

 

Zarządzając transakcjami poprzez komponent 

TDatabase

 korzystać  będziemy 

z własności 

TransIsolation

 oraz metod: 

StartTransaction

Commit

 

RollBack

TransIsolation

 pozwala ustalić poziom izolowania transakcji w serwerze 

bazy danych dla konkretnego połączenia. Jak już wspominaliśmy, poziom TIL 
używany dla każdego połączenia w serwerze steruje dostępem innych transakcji 
współbieżnych do wprowadzonych przez nas modyfikacji, a także zdolnością 
naszej transakcji do obserwowania modyfikacji, wprowadzonych w innych 
transakcjach współbieżnych.  

TransIsolation

 może przyjmować jedną z trzech wartości: 

tiDirtyRead

tiReadCommitted

 oraz 

tiRepeatableRead (

domyślną jest 

tiRead 

Committed):

 

„

tiDirtyRead

 - widoczne są nie zatwierdzone modyfikacje, wprowadzone 

w ramach innych transakcji 

„

tiReadCommitted

 - widoczne są tylko modyfikacje zatwierdzone, 

wprowadzone w ramach innych transakcji 

„

tiRepeatableRead

 - nie są widoczne modyfikacje, dokonane przez inne 

transakcje w 

danych poprzednio odczytanych, co oznacza, że przy 

każdorazowym odczycie rekordu w transakcji, zwracany jest dokładnie ten sam 
rekord.  

Metoda 

StartTransaction

 komponentu 

TDatabase

 zaznacza początek 

transakcji, czyli grupy modyfikacji (bazy danych), którą chcemy wydzielić jako 

background image

652 

Część IV 

całość. Do bazy danych zostaną wprowadzone wszystkie te modyfikacje, albo 
żadna z nich.  

Metoda 

Commit

 zatwierdza modyfikacje bazy danych, które wystąpiły od chwili 

rozpoczęcia transakcji. Możemy ją traktować jak komendę 

save 

(zachowaj)

 

bazy danych.  

Rollback

 unieważnia zmiany wprowadzone do bazy danych od momentu 

uruchomienia transakcji. Możemy ją traktować jak komendę 

undo

 bazy danych.  

Własność 

TransIsolation a

 

p

oziomy izolowania transakcji w DBMS

 

Poziomy TIL, obsługiwane przez własność 

TransIsolation

 komponentu 

TDatabase,

 mogą być inne w naszym serwerze bazy danych, bądź też w ogóle 

przez niego nie obsługiwane. Kiedy jeden z żądanych przez nas poziomów 
własności 

TransIsolation

 nie jest obsługiwany przez serwer, przekazywany 

jest do następnego, wyższego poziomu izolowania transakcji.  

Tabela 23.1. Własność 

TransIsolation

 a

 

poziomy izolowania transakcji 

w różnych platformach DBMS

 

Wartość własności 

TransIsolation 

InterBase

 

Oracle Sybase 

i Microsoft 

tiDirtyRead

 

odczyt 
zatwierdzonych 
(read committed) 

odczyt 
zatwierdzonych 
(read committed) 

odczyt 
zatwierdzonych 
(read committed) 

tiReadCommitted

 

odczyt 
zatwierdzonych 
(read committed) 

odczyt 
zatwierdzonych 
(read committed) 

odczyt 
zatwierdzonych 
(read committed) 

tiRepeatableRead

 

 odczyt 

powtarzalny 
(repeatable read) 

błąd odczytu 
powtarzalnego 
(TYLKO DO 
ODCZYTU) 
(poziom 
nieobsługiwany) 
(repeatable read 
(READ ONLY) 
Error) 

Sterowanie transakcjami z wykorzystaniem języka SQL 

Przetwarzaniem transakcji przez serwer można również sterować za pomocą 
języka Passthrough SQL. W tym celu należy posłużyć się komendami SQL, które 

background image

 Rozdział 23 Sterowanie współbieżnością 

653

 

służą do zmiany sposobu przetwarzania transakcji w serwerze. W poniższych 
przykładach użyliśmy składni SQL w wersji InterBase.  

UWAGA: 

Pamiętajmy,  że jeśli za pomocą 

SQLPASSTHRUMODE

 ustawiliśmy poziom 

izolowania transakcji na 

SHARED AUTOCOMMIT

 lub 

SHARED NOAUTO-

COMMIT

, to nowy poziom TIL może wpłynąć (mimowolnie) na inne transakcje 

zainicjowane przez naszą aplikację. 

Izolowanie transakcji 

Aby ustawić, pracując za pomocą SQL,  żądany poziom TIL, należy skorzystać 
z komendy 

SET TRANSACTION

. Np. obsługiwane w systemie InterBase trzy 

poziomy TIL to 

SNAPSHOT

SNAPSHOT TABLE STABILITY i 

READ 

COMMITTED 

(opisane poniżej). W 

celu zainstalowania jednego z 

nich 

powinniśmy użyć komendy SQL 

SET TRANSACTION ISOLATION LEVEL

.  

„

SNAPSHOT

: Baza danych przyjmuje postać obrazu statycznego (snapshot) - 

w odniesieniu do momentu rozpoczęcia transakcji. Modyfikacje dokonane 
przez inne aktywne transakcje nie są widoczne. 

„

SNAPSHOT TABLE STABILITY

: Zablokowanie tabel, odczytywanych lub 

zapisywanych w ramach danej transakcji, z umożliwieniem odczytu (tzn. 
dostępu typu read-only) innym transakcjom.  

„

READ COMMITTED

: w bazie danych widoczne są ostatnio zatwierdzone 

wersje istniejącego wiersza (rekordu), przy czym można wprowadzać takie 
modyfikacje, które nie spowodują konfliktu z 

innymi, współbieżnymi 

transakcjami. Dla tego poziomu można określić dwa parametry: 

♦ 

NO RECORD_VERSION

 (parametr domyślny): Ukazuje tylko ostatnią 

wersję wiersza (czyli rekordu). Jeśli dla komendy 

SET TRANSACTION

 

podamy parametr 

WAIT

, wówczas transakcja zaczeka na zatwierdzenie lub 

wycofanie ostatniej wersji rekordu, po czym ponownie dokona odczytu.  

♦ 

RECORD_VERSION

: Odczytuje ostatnią  zatwierdzoną wersję wiersza - 

nawet jeśli w bazie danych znajduje się nowsza, nie zatwierdzona wersja.  

Izolowanie transakcji na serwerze. Klasyczne problemy izolowania 
transakcji 

Metody rozwiązywania klasycznych problemów izolowania transakcji zależne są 
od - obsługiwanego na serwerze - poziomu TIL (tablica 23.3).  

background image

654 

Część IV 

Tabela 23.2. Poziomy izolowania transakcji w InterBase  

Poziom TIL 

Problem 

Rozwiązanie 

SNAPSHOT 

(obraz statyczny

)

 

zgubione aktualizacje  W innych transakcjach nie można 

uaktualniać wierszy, które są 
uaktualniane w ramach danej 
transakcji 

 

pozorne odczyty 

Nie można odczytywać modyfikacji 
dokonanych w ramach innych 
transakcji; w innych transakcjach 
widoczna jest poprzednia wersja 
wiersza uaktualnionego w ramach 
danej transakcji 

 niepowtarzalne 

odczyty 

Możliwość odczytywania tylko tej 
wersji wiersza, którą zatwierdzono 
przy rozpoczęciu transakcji 

 wiersze-widma 

Możliwość odczytywania tylko tej 
wersji wiersza, którą zatwierdzono 
przy rozpoczęciu transakcji  

 efekty 

uboczne 

aktualizacji 

Nie można odczytywać modyfikacji 
dokonanych w ramach innych 
transakcji; w innych transakcjach 
widoczna jest poprzednia wersja 
wiersza uaktualnionego w ramach 
danej transakcji 

READ 
COMMITTED 

(odczyt 
zatwierdzonych) 

zgubione aktualizacje  W innych transakcjach nie można 

uaktualniać wierszy, które są 
uaktualniane w ramach danej 
transakcji 

 

pozorne odczyty 

W innych transakcjach widoczna 
jest poprzednia lub zatwierdzona 
wersja wiersza uaktualnionego 
w ramach danej transakcji 

 niepowtarzalne 

odczyty 

dozwolone z definicji 

 wiersze-widma 

wiersze-widma 

mogą się pojawić, 

ponieważ na tym poziomie TIL 
widoczne są zatwierdzone 
modyfikacje, wprowadzone 

background image

 Rozdział 23 Sterowanie współbieżnością 

655

 

Poziom TIL 

Problem 

Rozwiązanie 
w innych transakcjach 

 efekty 

uboczne 

aktualizacji 

W innych transakcjach widoczna 
jest poprzednia lub zatwierdzona 
wersja wiersza uaktualnionego 
w ramach danej transakcji 

SNAPSHOT 
TABLE 
STABILITY 

(stabilność obrazu 
statycznego tabeli) 

zgubione aktualizacje  Inne transakcje nie mogą 

uaktualniać tabel, których dotyczy 
ten poziom 

 

pozorne odczyty 

Inne transakcje nie mają dostępu do 
uaktualnianych tabel, których 
dotyczy ten poziom 

 niepowtarzalne 

odczyty 

Możliwość odczytywania tylko tej 
wersji wiersza, którą zatwierdzono 
przy rozpoczęciu transakcji; inne 
transakcje nie mają dostępu do 
uaktualnianych tabel, których 
dotyczy ten poziom 

 

wiersze-widma 

Inne transakcje nie mają dostępu do 
tabel, których dotyczy ten poziom 

 efekty 

uboczne 

aktualizacji 

Inne transakcje nie mogą 
uaktualniać tabel, których dotyczy 
ten poziom 

Wybór odpowiedniego poziomu izolowania transakcji 

Jak widać, prawie każdy aspekt izolowania transakcji wiąże się (w taki czy inny 
sposób) z wszystkimi (trzema) poziomami izolowania transakcji w InterBase. 
Poziom, który powinniśmy wybrać, zależy w dużym stopniu od wymagań naszej 
aplikacji. 

SNAPSHOT 

(obraz statyczny)

 

to domyślny poziom TIL. Dla większości aplikacji 

należy zastosować właśnie poziom 

SNAPSHOT

 albo 

READ COMMITTED 

(odczyt 

zatwierdzonych).

 

Zastosowanie poziomu 

SNAPSHOT TABLE STABILITY

 

może - w nieokreślony bliżej sposób - zablokować dostęp innym użytkownikom do 
potrzebnych im tabel. Stąd powinniśmy go raczej unikać.  

Decydując się na wybór pomiędzy poziomami 

SNAPSHOT

 a 

READ COMMITTED

 

powinniśmy odpowiedzieć sobie na pytanie, czy w trakcie działania naszej 

background image

656 

Część IV 

transakcji mają być widziane zatwierdzone aktualizacje, przeprowadzane 

ramach innych transakcji. Jeśli nie mają, powinniśmy wybrać poziom 

SNAPSHOT.

  W

 

przeciwnym razie

 - READ COMMITTED

. Ogólnie rzecz 

biorąc, z poziomem TIL 

READ COMMITTED

 wiąże się mniejsza liczba konfliktów 

blokowania (lock contention).  

Sterowanie transakcjami 

Sterowanie transakcjami w InterBase przebiega w oparciu o następujące komendy 
języka SQL: 

SET TRANSACTION

COMMITT

 i 

ROLLBACK

SET 

TRANSACTION

 ma wiele zastosowań, a 

jednym z 

nich jest (jak już 

wspominaliśmy) ustawianie poziomu izolowania transakcji. Działanie komendy 

COMMIT

 jest bardzo podobne do metody 

Commit

  własności 

TDatabase - 

funkcjonuje więc

 

jak komenda 

save

 (Zachowaj) bazy danych. Analogicznie, 

komenda 

ROLLBACK

 funkcjonuje jak metoda 

Rollback

 tej własności - 

unieważnia modyfikacje wprowadzone do bazy danych od chwili wystąpienia 
ostatniej komendy 

COMMIT

.  

Komenda SET TRANSACTION 

Komendę tą stosujemy do rozpoczęcia transakcji, np. 

SET TRANSACTION 

Jeżeli chcemy rozpocząć transakcję typu tylko do odczytu  (read-only), musimy 
dodać opcjonalne słowo kluczowe 

READ ONLY

, np. 

SET TRANSACTION READ ONLY 

Wiele platform RDBMS obsługuje również transakcje nazwane (named 
transactions
). Umożliwia to m.in. zagnieżdżanie transakcji. W języku InterBase 
SQL komendy do modyfikacji danych (takie jak 

INSERT

UPDATE

 i 

DELETE

mogą się bezpośrednio odwoływać do transakcji nazwanych. Oto składnia 
przykładowego wyrażenia w InterBase, poprzez które uruchamiamy transakcję 
nazwaną:  

SET TRANSACTION :UpdateCustomers 

Zauważmy,  że zmienna 

:UpdateCustomers

 musi być wcześniej 

zadeklarowana i zainicjowana. 

A oto składnia wyrażenia w języku Sybase Transact-SQL, wpisanego w tym 
samym celu: 

BEGIN TRANSACTION UpdateCustomers 

background image

 Rozdział 23 Sterowanie współbieżnością 

657

 

Komendy COMMIT i ROLLBACK 

Komenda 

COMMIT 

języka SQL powoduje utrwalenie modyfikacji, dokonanych 

w trakcie danej transakcji. Zachowuje ona modyfikacje, które wprowadziliśmy do 
bazy danych i zapewnia, że będą one widoczne dla innych użytkowników. 
Z drugiej strony komenda 

ROLLBACK

 unieważnia wszystkie modyfikacje, które 

dana transakcja mogła wprowadzić do bazy danych. W większości systemów 
usuwa ona po prostu wszystkie modyfikacje - oczekujące na zatwierdzenie 

wprowadzone do protokołu transakcji lub wycofań. Obydwie z 

wyżej 

wymienionych komend wpływają na modyfikacje, które wystąpiły od ostatniej 
komendy 

COMMIT. 

Nie można natomiast wycofać  (rollback) modyfikacji już 

zatwierdzonych.  

UWAGA: 

Między komendami 

SET TRANSACTION a COMMIT 

lub 

ROLLBACK

 powinno 

umieszczać się tylko komendy bezpośrednio związane z modyfikacją danych. 
Przeglądanie i inne czynności związane z odczytywaniem informacji powinny być 
przeprowadzane przed zastosowaniem komendy 

SET TRANSACTION

. Takie 

podejście zapewnia możliwie najkrótszy czas blokady, w którym inni użytkownicy 
nie będą mieli dostępu do zasobów bazy danych.  

Edytor WISQL, udostępniany przez InterBase, automatycznie rozpoczyna 
transakcję (poprzez wydanie komendy, będącej odpowiednikiem InterBase 

SET 

TRANSACTION

) przy pierwszym uruchomieniu. Tuż przed zakończeniem pracy 

z WISQL zostaniemy zapytani o to, czy chcemy zatwierdzić efekty naszej pracy. 
Możemy je w każdej chwili zatwierdzić lub wycofać - opcją 

Commit Work

 lub 

Rollback Work

 - z menu 

File

 edytora WISQL.  

Systemy sterowania współbieżnością 

System  sterowania współbieżnością możemy np. odnieść do zarządzania 
magistralą kolejową. Gdyby jeździł po niej tylko jeden pociąg, to w ogóle nie 
byłoby problemu ze współbieżnością; jeżeli jednak pojawi ich więcej, trzeba 
szybko opracować jakiś plan, który zapobiegnie kolizjom a jednocześnie zapewni, 
że towary będą odbierane i ładowane na czas. Takie same problemy napotykamy 
przy bazach danych. Musimy znaleźć sposób zapobiegania ,,kolizjom’’ (tzn. 
wzajemnemu nadpisywaniu) aktualizacji wprowadzanych przez poszczególnych 
użytkowników, przy zachowaniu oczekiwanej przez nich wydajności.  

Oczywiście nie będzie problemów ze współbieżnością, jeśli do bazy danych 
kierować  będziemy jedynie zapytania. Pojawiają się one dopiero wówczas, gdy 
dwóch lub więcej użytkowników zechce zmodyfikować te same dane w tym 

background image

658 

Część IV 

samym czasie. Kiedy modyfikacje jednego użytkownika zostają zgubione lub nie 
może on ich wprowadzić z powodu modyfikacji przeprowadzanych przez innego 
użytkownika - wtedy mówimy, że wystąpiła zgubiona aktualizacja (lost update
lub konflikt aktualizacji (update conflict). Efektywne systemy sterowania 
współbieżnością rozwiązują te problemy z 

zachowaniem użyteczności 

i funkcjonalności bazy danych.  

Wyróżniamy dwa podstawowe typy systemów sterowania współbieżnością: 
pesymistyczny i 

optymistyczny. Często określa się je jako systemy 

optymistycznego i pesymistycznego  blokowania  (locking) zasobów bazy danych, 
gdyż  właśnie blokowanie wykorzystywane jest najczęściej przy rozwiązywaniu 
problemów współbieżności. Systemy te rozróżnia się według leżących u ich 
podstaw założeń, odnoszących się do prawdopodobieństwa konfliktu, czyli próby 
modyfikacji tych samych danych w kilku transakcjach współbieżnych w tym 
samym czasie. W pesymistycznym systemie sterowania współbieżnością zakłada 
się prawdopodobieństwo takiego konfliktu, podczas gdy w 

systemie 

optymistycznym uważa się go za zdarzenie nienormalne i traktuje jako wyjątek. 

optymistycznym systemie sterowania współbieżnością przyjmuje się, iż 

większość zapytań jest typu tylko do odczytu  (read-only) i że aktualizacje 
konkretnej danej w tym samym czasie dokonywane są rzadko.  

Pesymistyczne sterowanie współbieżnością 

Konsekwencją przyjętego - w pesymistycznym systemie sterowania współbież-
nością - dużego prawdopodobieństwa występowania konfliktów aktualizacji jest 
blokowanie dostępu do zasobów wykorzystywanych w danej transakcji. Inne 
transakcje, które potrzebują tych zasobów, muszą zaczekać, aż transakcja 
blokująca zakończy przetwarzanie. 

Chociaż pesymistyczny system sterowania może korzystać zarówno z blokad 
odczytu (read-inhibit lock), jak i blokad zapisu (write-inhibit lock), w praktyce 
stosowane są jedynie te ostatnie. Zwykle mamy do czynienia z blokadami, które 
zezwalają tylko na odczyt danych, lub takimi, które zezwalają zarówno na ich 
odczyt, jak i zapis. Dostęp do danych można blokować na różnych poziomach - od 
wielu tabel na raz do pojedynczych kolumn.  

Najczęściej stosuje się (platforma SQL Server) blokowanie tabel, wierszy i stron. 
Blokady takie mogą czasem wystąpić jedna po drugiej. Oznacza to, że po 
blokadzie kilku wierszy może wystąpić blokada całej, zawierającej je tabeli. 

Nazywamy to eskalacją blokad  (lock escalation). Występuje ona wówczas, gdy 
serwer bazy danych oceni, że liczba już istniejących blokad usprawiedliwia 
zastąpienie ich blokadą całej tabeli. 

background image

 Rozdział 23 Sterowanie współbieżnością 

659

 

W praktyce nigdy nie spotykamy się z blokadami indywidualnych kolumn; 
obciążają one zasoby i 

nie służą  żadnemu konkretnemu celowi. Rzadko 

uzasadnienie znajduje zapobieganie modyfikacji kolumny przecinającej się 
z wierszem, który nie jest uaktualniany w innej transakcji. 

Ogólnie - z im większą precyzją blokujemy dostęp do bazy danych, tym więcej 
użytkowników może go do niej jednocześnie uzyskać. Z kolei - im więcej 
korzystających jednocześnie z bazy użytkowników, tym większa staje się liczba 
blokad. Jeżeli zablokowaliśmy tylko jedną tabelę, mamy tylko jedną blokadę. 
Jeżeli jednak zablokujemy wiele wierszy w wielu tabelach, to wystąpi wiele 
blokad. Im więcej blokad, tym więcej zasobów serwera musi zostać 
wykorzystanych do zarządzania blokadami i tym dłuższy staje się czas konieczny 
do ich zakładania i usuwania. Podobnie jak w wielu innych przypadkach, tak i tutaj 
- większa funkcjonalność oznacza również zwiększone obciążenie zasobów.  

Z powodu naturalnej tendencji do blokowania całych tabel, pesymistyczne systemy 
sterowania podatne są  na  zatory  (deadlock; przyp.tłum**. prawidłowym polskim 
tłumaczeniem słowa  deadlock w programowaniu współbieżnym jest  termin 
blokada, tutaj jednak ma on inne znaczenie, i dlatego zdecydowano się tłumaczyć 
je jako zator). Zator występuje wówczas, gdy transakcja nr 1 blokuje tabele 
wymagane w transakcji nr 2 i odwrotnie. Ponieważ w żadnej z tych transakcji nie 
można zablokować tabel, które są potrzebne do jej zakończenia, obie pozostają 
zamknięte niczym w śmiertelnym uścisku, i żadna z nich nigdy się nie skończy bez 
interwencji z zewnątrz.  

Z reguły w serwerach baz danych realizuje się pesymistyczną strategię sterowania. 
Jest to zaszłość z czasów, gdy żadne zasoby nie istniały na poziomie stacji 
roboczej, z 

którego to powodu nie można było stosować mechanizmów 

optymistycznych. Ponieważ w 

samym Delphi przyjęto właśnie strategię 

optymistyczną, w pracy z naszymi aplikacjami możemy zetknąć się z dwiema 
strategiami.  

Większość serwerów baz danych automatycznie blokuje za nas odpowiednie 
elementy danych w trakcie wprowadzania modyfikacji do bazy. Kiedy np. 
modyfikujemy wiersz, większość serwerów blokuje go, aby uniemożliwić innym 
użytkownikom jego aktualizację w tym samym czasie. Serwery pracujące na 
niektórych platformach mogą zablokować całą tabelę, jeżeli usuwamy dużą liczbę 
wierszy.  

Zarówno w Sybase, jak i Microsoft SQL Server przyjęto blokowanie całych tabel 
i stron, a nie pojedynczych wierszy, co z kolei ma miejsce w platformach takich 
jak Oracle. Kiedy próbujemy zmodyfikować dany wiersz, strona, na której się on 
znajduje, zostaje zablokowana na czas uaktualniania. Aktualizacje zwykle 
występują w grupach dotyczących wierszy zgromadzonych w danym obszarze 
bazy danych. Jeśli przyjąć blokowanie tych wierszy według stron, zwykle jedna 

background image

660 

Część IV 

blokada wystarcza dla kilku wierszy. Zmniejsza to obciążenie zasobów serwera, 
ale może uniemożliwić dostęp do wierszy, których dana aktualizacja nie dotyczy. 
W takiej strategii szczególny problem sprawiają aktualizacje pojedynczych 
wierszy. Pomijając kwestie opłacalności, podejście przyjęte przez firmę Microsoft 
w platformie SQL Server okazuje się w praktyce równie dobre, co przyjęte 
w platformach innych producentów DBMS.  

UWAGA: 

Ostatnio w platformie Microsoft SQL Server pojawiła się możliwość blokowania 
wierszy w pewnych, dość rzadkich, sytuacjach, ale udogodnienie to działa 
znacznie gorzej, niż obsługa blokowania wierszy zrealizowana w systemie Oracle. 
Również w Sybase testuje się obecnie mechanizm blokowania wierszy, który 
jednak nie jest jeszcze dostępny na rynku.  

Optymistyczne sterowanie współbieżnością 

W systemach optymistycznego sterowania współbieżnością zakłada się,  że 
aktualizacje przeważnie nie wpadają we wzajemne konflikty i że większość 
użytkowników bazy danych odczytuje informacje, ale ich nie uaktualnia. Poniższy 
przykład powinien pomóc w zrozumieniu takiego systemu. Wyobraźmy sobie 
głównego redaktora nadzorującego redagowanie rękopisów książek, które mają 
zostać opublikowane. Kiedy otrzymuje on rękopis, powiela go i 

rozsyła 

podwładnym, którzy z kolei dodają do swoich kopii poprawki i zwracają je. 
Następnie redaktor główny musi zintegrować wszystkie modyfikacje i wprowadzić 
je do oryginału. Ci redaktorzy, którzy otrzymają poprawiony rękopis, nie muszą 
dzięki temu powtarzać wykonanej już pracy.  

Powyższy przykład z kilku powodów dobrze ilustruje działanie optymistycznego 
systemu sterowania współbieżnością. Po pierwsze zauważmy, że oryginał rękopisu 
nigdy nie opuszcza głównego redaktora. Podobnie w systemach optymistycznego 
sterowania najpierw modyfikuje się  kopię danych. Sam serwer wprowadza 
modyfikację w taki sposób, żeby nie nadpisała (overwrite) ona modyfikacji innych 
użytkowników.  

Zauważmy również naturalny podział modyfikacji, wprowadzanych przez 
poszczególnych redaktorów. Jeden z nich sprawdza np. rękopis pod względem 
ortografii, inny może być odpowiedzialny za poprawność merytoryczną i tak dalej. 
Z reguły dwóch redaktorów nigdy jednocześnie nie redaguje tego samego rękopisu 
pod tym samym względem. Oto podstawowe założenie każdego optymistycznego 
systemu sterowania współbieżnością. Aktualizacje bazy danych przedsiębiorstwa 
dzieli się zwykle według oddziału, poziomu zarządzania lub innych kryteriów. 

background image

 Rozdział 23 Sterowanie współbieżnością 

661

 

Sprawia to, że system optymistyczny działa dobrze, ponieważ zasoby bazy danych 
nie są początkowo blokowane.  

Optymistyczne systemy zarządzania współbieżnością w Delphi 

Optymistyczne sterowanie współbieżnością udostępniane jest w Delphi przez 
własność 

UpdateMode

 komponentów 

TTable

 oraz 

TQuery DataSet

Podobnie jak w innych optymistycznych systemach współbieżnych, Delphi 
otrzymuje z serwera kopię wiersza, udostępnia ją nam do modyfikacji, po czym 
przesyła mu ją z powrotem. Przekaz ten realizujemy poprzez komendę 

UPDATE

 

w SQL. Komenda w przedstawionej tutaj postaci zawiera klauzulę 

WHERE

, służącą 

wyszukaniu wiersza do modyfikacji. Poprzez 

UpdateMode

 definiowane są te 

kolumny tabeli, które mają zostać uwzględnione przez klauzulę 

WHERE

.  

Własność 

UpdateMode

 może przyjmować jedną z trzech wartości: 

„

upWhereAll

 (wartość domyślna) - Klauzula 

WHERE

 uwzględni każdą 

kolumnę w tabeli. 

„

upWhereChanged

 - Klauzula 

WHERE

 uwzględni kolumny według klucza 

DataSet

 oraz kolumny, które uległy zmianie. 

„

upWhereKeyOnly

-

 

Klauzula 

WHERE

 uwzględni tylko kolumny według 

klucza z 

DataSet

 (tego ustawienia należy używać tylko wówczas, gdy mamy 

wyłączność do tabeli bazy danych). 

Obydwa komponenty - 

TTable

 i 

TQuery -

 propagują własność 

UpdateMode

Jej ustawieniem domyślnym jest 

upWhereAll

, co oznacza, że uaktualnienie 

wiersza w tabeli spowoduje wygenerowanie klauzuli 

WHERE

, wyświetlającej 

wszystkie jej kolumny. Może to okazać się bardzo kłopotliwe, zwłaszcza 
w przypadku dużych tabel. Alternatywne i szybsze rozwiązanie polega na nadaniu 
własności 

UpdateMode

 wartości 

upWhereChanged

. W 

wyniku takiego 

ustawienia generowana będzie klauzula 

WHERE

, która uwzględni jedynie kluczowe 

i zmodyfikowane pola tabeli.  

Przypuśćmy,  że nasza aplikacja w Delphi zmodyfikowała już pole 

LastName

 

tabeli CUSTOMER. Poniżej zamieściliśmy kod SQL, wygenerowany przy 
ustawieniu 

upWhereAll

 (zwróćmy uwagę na długość klauzuli 

WHERE)

UPDATE CUSTOMER 
SET LastName=’newlastname’ 
WHERE CustomerNumber=1 
AND LastName=’Doe’ 
AND FirstName=’John’ 
AND StreetAdress=’123 SunnyLane’ 
AND City=’Anywhere’ 
AND State=’OK.’ 
AND Zip=”73115’ 

background image

662 

Część IV 

A oto kod wygenerowany przy ustawieniu 

upWhereChanged

UPDATE CUSTOMER 
SET LastName=’newlastname’ 
WHERE CustomerNumber=1 
AND LastName=’Doe’ 

Zauważmy, o ile krótszy jest drugi listing. Zwróćmy również uwagę na to, że 
dzięki umieszczeniu w klauzuli 

WHERE 

oryginalnej wartości pola 

LastName

w drugim przypadku uniknięto możliwości nadpisania (overwrite) ewentualnej 
modyfikacji tego pola, wprowadzonej przez innego użytkownika.  

Jeżeli inny użytkownik zmodyfikuje pole 

LastName

 w czasie, jaki upłynął 

między odczytem wiersza a uaktualnieniem go przez bieżącego użytkownika, 
komenda 

UPDATE,

 wygenerowana w efekcie ustawienia 

upWhereChanged,

 

zakończy się niepowodzeniem, i o to nam właśnie chodzi. Oczywiście jeszcze 
większą odporność na nieoczekiwane konflikty daje ustawienie 

upWhereAll

Inny użytkownik mógłby usunąć wiersz po przeczytaniu go przez naszą aplikację, 
a potem dodać nowy rekord do tabeli, który przez przypadek może mieć ten sam 
klucz i tę samą wartość 

LastName

, co poprzedni rekord. Jeśli nasz rekord użyłby 

swojej komendy 

UPDATE

, to uaktualniłaby ona niewłaściwy rekord. Taki 

scenariusz jest jednak bardzo mało prawdopodobny.  

Inna wartość 

UpdateMode

, a mianowicie 

upWhereKeyOnly

, jeszcze bardziej 

redukuje odporność na konflikty. Ponieważ wówczas sprawdza się wartości klucza 
tylko dla tego pola, które modyfikujemy, nie uwzględnia się jego ewentualnej 
modyfikacji przez innego użytkownika - w czasie, jaki upłynął od pierwszego 
odczytu rekordu. Pominięcie takiej możliwości może być bezpiecznym 
założeniem, ale (tak jak poprzednio), niestety nie zawsze. W 

większości 

zastosowań z wieloma użytkownikami niebezpiecznie jest zakładać,  że inny 
użytkownik nie zmodyfikował jeszcze rekordu, który odczytaliśmy w aplikacji 
klienckiej. Tak więc optymalizację taką przeprowadzamy w rzadkich przypadkach 

tylko wówczas, kiedy jest ona niezbędna. Ponieważ klauzula 

WHERE,

 

wygenerowana przez ustawienie 

upWhereKeyOnly,

  jest  z natury  rzeczy 

krótsza, działa ona szybciej od klauzuli obejmującej dodatkowe kolumny. 
Niemniej jednak, przed skorzystaniem z tej opcji powinniśmy skonsultować się 
z administratorem bazy danych - by nie narazić się na utratę danych.  

W praktyce powinniśmy się trzymać ustawienia 

upWhereAll (

chyba że mamy 

konkretny powód, żeby wykorzystać inne). Dobrą alternatywą jest wartość 

upWhereChanged -

 jeżeli tabela, z którą pracujemy, ma dużo kolumn, 

a ustawienie 

upWhereAll

 istotnie spowalnia pracę.  

background image

 Rozdział 23 Sterowanie współbieżnością 

663

 

Zarządzanie protokołem transakcji 

Zastanówmy się, jak serwery baz danych unikają ciągłego zachowywania 
modyfikacji, na wypadek zaniku zasilania lub uszkodzenia sprzętu? Czy serwer 
dysponuje czymś w rodzaju listy wycofań? Skąd serwer wie, które transakcje mają 
zostać unieważnione podczas odtwarzania bazy danych po awarii?  

Odpowiedź na wszystkie te pytania jest następująca. Otóż modyfikacje 
wprowadzane przez nas do bazy danych są  najpierw wprowadzane do protokołu 
transakcji (lub protokołu wycofań) bazy danych, a dopiero potem zatwierdzane 
w samej bazie, po pomyślnym zakończeniu transakcji. Oznacza to, że protokół 
transakcji jest czymś więcej niż tylko protokołem, to jego dotyczą wszystkie 
operacje! Tak więc, jeżeli zanik zasilania serwera wystąpił, zanim w bazie danych 
zatwierdzono naszą modyfikację, to żadnej zmiany nie trzeba wycofywać, gdyż 
w ogóle nie zaistniała ona w tej bazie. Ponieważ nasze nie zatwierdzone 
modyfikacje były wprowadzane tylko do protokołu transakcji, oprogramowanie 
serwera może w łatwy sposób unieważnić nie zakończoną transakcję, bez 
odwoływania się do samej bazy danych.  

Jednym z zagadnień, o którym często zapominają twórcy aplikacji baz danych, jest 
zarządzanie protokołem transakcji. Zaniedbuje się je zwykle i odkłada na bok, jako 
należące do dziedziny administrowania bazami danych.  

Istnieje jednak kilka zagadnień związanych z zarządzaniem protokołami transakcji, 
z którymi kompetentny twórca oprogramowania dla baz danych typu klient/serwer 
musi się zapoznać i które powinien brać pod uwagę w swoich aplikacjach. 
Pierwsze z 

nich wiąże się z 

koniecznością minimalizowania informacji 

przechowywanych w protokole transakcji. Drugie - z zaleceniem rozbijania dużych 
transakcji na mniejsze, w celu uniknięcia zapełnienia protokołu. Oba zagadnienia 
omówiono po kolei w poniższych podrozdziałach.  

Minimalizowanie informacji pamiętanych w protokole 

Częstym błędem popełnianym przez twórców oprogramowania typu klient/serwer, 
którzy wywodzą się ze świata prostych baz danych (tzn. opartych na prostych 
plikach,  flat files) - takich jak dBASE czy Paradox - jest przeprowadzanie 
aktualizacji grupowych z użyciem konstrukcji programowych, a nie języka SQL. 
Dobrze ilustruje to poniższy przykład. Przypuśćmy,  że musimy zamienić - 
z małych na wielkie - litery wszystkich pól nazwisk w tabeli CUSTOMER. Ci 
z nas, którzy zdobywali pierwsze doświadczenia pracując z językiem dBASE, 
mogliby prawdopodobnie napisać taki oto fragment w języku Object Pascal: 

With taCUSTOMER do begin 
 First; 
 

While not EOF do begin 

background image

664 

Część IV 

  FieldByName(‘LastName’).AsString 

:= 

 UpperCase(FieldByName(‘LastName’).AsString); 
  Next; 
 end; 
end; 

Rezultatem takiej strategii jest nie tylko mała szybkość działania, ale też 
niepożądane, oddzielne rozpoczynanie i zatwierdzanie transakcji dla każdego 
kolejnego wiersza w tabeli. Ponadto, jeśli podczas wykonywania pętli wystąpiłby 
jakiś problem, to tylko część wierszy zostałaby uaktualniona - litery w niektórych 
nazwiskach zostałyby zamienione na wielkie, a w innych nie uległy zmianie. 
Problemowi częściowej aktualizacji można by zaradzić przez wywołanie metody 

StartTransaction

 naszego komponentu 

TDataBase

 jeszcze przed 

wejściem do pętli. Jednak proces aktualizacji w dalszym ciągu przebiegałby 
z niską wydajnością. W każdym, pojedynczym obiegu pętli zostaje wygenerowana 
oddzielna instrukcja 

UPDATE

  języka SQL, zakończona swoją  własną klauzulą 

WHERE

, której celem jest odnalezienie kolejnego wiersza. Przy bardzo dużej tabeli 

CUSTOMER proces ten może ciągnąć się niezwykle długo. 

Daleko lepszym podejściem, i to takim, w którym protokół transakcji wykorzystuje 
się we właściwy sposób, jest wprowadzenie naszej modyfikacji z wykorzystaniem 
komponentu 

TQuery

 lub 

TStoreProc

. Odpowiednik powyższego kodu, 

zapisany w języku SQL, wygląda następująco: 

UPDATE CUSTOMER 
SET LastName=UPPER(LastName) 

Ponieważ samo powyższe wyrażenie zostanie potraktowane jako pojedyncza 
transakcja, do tabeli zostaną wprowadzone albo wszystkie aktualizacje, albo 
żadna. Ponadto nie będą już miały miejsca ciągłe inicjacje i zatwierdzenia 
protokołu, w schemacie transakcja po transakcji.  

Innym sposobem ograniczenia informacji w protokole, które generuje transakcja, 
jest unikanie niekwalifikowanych komend 

DELETE

 języka SQL. Każdy wiersz do 

usunięcia (poprzez 

DELETE)

 jest najpierw kopiowany do protokołu transakcji tak, 

żeby można ją było, w razie potrzeby, unieważnić. W przypadku dużych tabel 
można łatwo przekroczyć dopuszczalną wielkość protokołu. Jeżeli chcemy usunąć 
wszystkie wiersze tabeli, powinniśmy użyć komendy serwera do opróżniania tabel 
(table-truncation command), a nie 

DELETE

. Wiele serwerów obsługuje komendę 

podobną do komendy 

ZAP

  języka dBASE, która szybko opróżnia tabelę z jej 

zawartości. Przykładowo - w produktach Sybase i Microsoft SQL Server istnieje, 
służąca do tego celu, komenda 

TRUNCATE TABLE

. Nie tylko działa ona szybciej 

od 

DELETE

, ale nie jest zachowywana do protokołu transakcji, w związku z czym 

nie wiążą się z nią żadne operacje na protokole. Jeżeli nasz serwer nie udostępnia 
komendy do opróżniania tabel, mogłoby okazać się,  że lepiej jest po prostu 

background image

 Rozdział 23 Sterowanie współbieżnością 

665

 

,,fizycznie’’ usunąć całą tabelę i ponownie ją utworzyć - w zależności od metody 
jej stosowania gdzie indziej w bazie danych i w innych aplikacjach.  

Platformy Sybase i Microsoft oferują inny sposób ograniczenia operacji na 
protokole, a mianowicie zastąpienie 

INSERT SELECT

 (przy kopiowaniu wierszy 

z tablicy do tablicy) komendą 

SELECT INTO

. Wiersze wstawiane za pomocą 

INSERT SELECT

 przechowywane są w protokole transakcji, stąd też nie 

powinniśmy wprowadzać dużych porcji danych. Komendę 

SELECT INTO

 

zrealizowano na platformie SQL Server jako operację nie wiążącą się 
z protokołem, gdyż nie powoduje ona dodawania do niego żadnych pozycji. 
Składnia powyższa nie będzie obsługiwana na wszystkich platformach DBMS (nie 
obsługuje jej np. InterBase). Stąd opisywana technika może okazać się 
niewykonalna w konkretnym systemie.  

Komenda 

SELECT INTO

 według składni dialektu Transact-SQL platformy SQL 

Server ma następującą postać: 

SELECT LastName, FirstName 
INTO NEWCUSTOMER 
FROM CUSTOMER 

Tworzy się tutaj tabelę o nazwie NEWCUSTOMER i wstawia do niej kolumny 

LastName

 i 

FirstName

 z tabeli CUSTOMER. Kod powyższy odpowiada 

funkcjonalnie następującej składni: 

CREATE TABLE NEWCUSTOMER 
(LastName CHAR(30), 
FirstName CHAR(30)) 

INSERT INTO NEWCUSTOMER (LastName, FirstName) 
SELECT LastName, FirstName 
FROM CUSTOMER 

Komendę 

SELECT INTO

 można wykorzystać dla uniknięcia tworzenia pozycji 

w protokole transakcji, zgodnie z niżej przedstawionym scenariuszem: 

„chcemy utworzyć tabelę, a następnie skopiować do niej wiersze z innej tabeli 

„Wszystkie kolumny w nowej tabeli mają otrzymać wartości z tabeli źródłowej 

„Nie zależy nam na możliwości wycofania wstawionych wierszy  

Podział dużych transakcji na mniejsze 

W niektórych sytuacjach musimy przeprowadzić modyfikacje wielkich ilości 
danych, prowadzące w 

nieunikniony sposób do utworzenia wielu pozycji 

w protokole transakcji lub wycofań. Takie aktualizacje mogą spowodować 
zapełnienie protokołu i problemy w serwerze. Mogą one również prowadzić do 

background image

666 

Część IV 

blokad, uniemożliwiających innym użytkownikom dostęp do całych tabel. Z tego 
powodu dobrze jest rozbić dużą transakcję na mniejsze. Można to przeprowadzić 
na kilka sposobów - ten, który wybierzemy, zależy od posiadanej platformy DBMS 
i konkretnych potrzeb naszej aplikacji.  

Obszerną aktualizację można rozbić na kilka mniejszych, dzieląc poziomo 
komendy służące do modyfikacji danych - tzn. rozbijając jedną komendę 

UPDATE

 

lub 

DELETE

 na kilka komend dotyczących tego podzbioru wierszy, do którego 

odnosiła się oryginalna klauzula 

WHERE

. Gdyby tabela CUSTOMER 

w poprzednim  przykładzie miała milion rekordów i chcielibyśmy zamienić litery 
w polu nazwiska każdego z nich, moglibyśmy napisać następujący kod:  

SET TRANSACTION; 
UPDATE CUSTOMER 
SET LastName=UPPER(LastName) 
WHERE CustomerNumber between 1 and 100000; 
COMMIT; 

SET TRANSACTION; 
UPDATE CUSTOMER 
SET LastName=UPPER(LastName) 
WHERE CustomerNumber between 100001 and 200000; 
COMMIT; 

SET TRANSACTION; 
UPDATE CUSTOMER 
SET LastName=UPPER(LastName) 
WHERE CustomerNumber between 200001 and 300000; 
COMMIT; 

Jest to prosty sposób praktycznej realizacji tak obszernej transakcji.  

Inną metodą ograniczenia liczby wierszy, których dotyczy wyrażenie z komendą 

UPDATE

 lub 

DELETE

, jest redukcja tych wierszy, które komenda ta może zmienić 

w jednym przebiegu. Nie jest to możliwe we wszystkich platformach DBMS (np. 
umożliwia to SQL Server, a już nie InterBase).  

W tym celu musimy użyć komendy właściwej dla posiadanego serwera, która 
służy do ograniczania liczby wierszy przetwarzanych przez 

UPDATE

 lub 

DELETE

Komendę  tę możemy powtarzać tyle razy, ile potrzeba dla przetworzenia całej 
tabeli. Poniżej zamieściliśmy przykład w języku Transact-SQL systemu SQL 
Server.  

SET ROWCOUNT 50000 /* Limits the UPDATE to 50000 rows at a  

 time*/ 

WHILE (EXISTS (SELECT * FROM ORDERS WHERE Amount<>0)) 
BEGIN 
 

UPDATE ORDERS SET Amount=0 

WHERE Amount<>0/* Keeps the UPDATE from looping infinitely */ 

background image

 Rozdział 23 Sterowanie współbieżnością 

667

 

END 

Z uwagi na fakt, iż modyfikowane wiersze ciągle znajdują się w tabeli z każdym 
obiegiem pętli, musimy znaleźć sposób ich usunięcia po przetworzeniu. Jednym 
z nich  może być wykorzystanie klauzuli 

WHERE

 komendy 

UPDATE

 - tak, żeby 

modyfikowane wiersze były przez tę klauzulę ignorowane po ich zmodyfikowaniu. 
Innymi słowy musimy sprawdzać kolumny Amount z użyciem testu <> 0, 
ponieważ w komendzie 

UPDATE 

polu Amount nadajemy wartość 0. Dzięki temu 

komenda ta, modyfikując dany wiersz, zaznacza go jako już przetworzony. 
Zaznaczanie takie zapobiega dwukrotnemu przetworzeniu wiersza.  

A oto przykład z komendą 

DELETE

SET ROWCOUNT 50000 /* Limits the DELETE to 50000 rows at a  

 time*/ 

WHILE (EXISTS (SELECT * FROM ORDERS WHERE Amount=0)) 
BEGIN 
 

DELETE FROM ORDERS 

WHERE Amount=0 
END