Rozdział 12.
Rozszerzenia języka
Transact-SQL
W rozdziale 11. została omówiona modyfikacja danych przy pomocy poleceń INSERT, UPDATE i
DELETE. Polecenia te powinny być używane rozważnie, szczególnie polecenie DELETE. Rzadko
(jeżeli w ogóle) należy przyznawać użytkownikom uprawnienie DELETE. Bardzo łatwo o
przypadkowe usunięcie danych. Zaleca się stosowanie procedur składowanych, które wykonują
usuwanie i zapewniają zachowanie integralności danych. Procedury składowane zostaną
omówione szerzej w rozdziale 15.
Niniejszy rozdział koncentruje się na własnościach programowania języka Transact-SQL (T-
SQL). Na początek zostaną przedstawione pliki wsadowe i skrypty. Następnie transakcje w SQL
Serverze oraz elementy języka kontroli przepływu, takie jak bloki IF...ELSE i polecenia WHILE.
Kolejnym omawianym zagadnieniem są typy blokad używane w SQL Serverze dla kontroli
współbieżności.
Pliki wsadowe (batches)
Plik wsadowy (batch) jest zbiorem poleceń T-SQL interpretowanych przez SQL Server jako
całość. Polecenia są zgrupowane razem a polecenie GO oznacza koniec pliku wsadowego.
Następujący przykład pokazuje zadanie wsadowe uruchomione z SQL Server Query Analyzera:
USE PUBS
SELECT au_id, au_lname FROM authors
SELECT pub_id, pub_name FROM publishers
INSERT publishers VALUES ('9998','SAMS Publishing', 'Seattle',
'WA','USA')
GO
Zadania wsadowe muszą spełniać następujące zasady:
" Wszystkie polecenia SQL są kompilowane jako grupa.
" Jeżeli wystąpi błąd w składni w dowolnym miejscu zadania wsadowego, całe zadanie jest
usuwane.
" Niektóre polecenia mogą być używane w pliku wsadowym, podczas gdy dla innych nie jest to
możliwe. Następujące polecenia CREATE mogą być połączone w pojedynczym pliku
wsadowym:
CREATE DATABASE
CREATE TABLE
CREATE INDEX
Następujące polecenia nie mogą być łączone z innymi w tym samym pliku wsadowym:
CREATE RULE
CREATE TRIGGER
CREATE PROCEDURE
CREATE VIEW
W przypadku próby ich łączenia, wystąpi błąd 111, przedstawiony poniżej:
Server Msg 111, Level 15, State 1
'CREATE VIEW' must be the first statement in a query batch.
" Nie można zmienić tabeli a następnie skorzystać z nowych kolumn w obrębie tego samego
zadania wsadowego.
" Polecenia SET mają skutek natychmiastowy, z wyjątkiem opcji QUOTED_IDENTIFIER i
ANSI_NULLS.
Korzystając z zadań wsadowych należy mieć na uwadze jeszcze kilka innych kwestii. Jak zostało
wcześniej wspomniane, gdy zadanie jest poddawane przetwarzaniu, jest poddawane analizie
składniowej, optymalizowane, kompilowane a następnie wykonywane. Jeżeli podczas
wykonywania, pojedyncze polecenie nie powiedzie się, zadanie wsadowe będzie kontynuowało
wykonywanie następnego polecenia aż do ukończenia całego procesu. Przykładowo, jeżeli zadanie
wsadowe posiada pięć poleceń CREATE TABLE i polecenie numer 3 powoduje powstanie błędu,
SQL Server utworzy tabele 1,2,4 i 5. Tabela numer 3 nie zostanie utworzona.
Skrypty
Skrypt jest zbiorem jednego lub więcej zadań wsadowych, które są zapisywane jako grupa.
Skrypty są wykonywane na ogół jako część pewnej jednostki pracy, która ma zostać zrealizowana,
jak np.: ładowanie lub konserwacja danych. Wydruk 12.1 pokazuje przykładowy skrypt.
Wydruk 12.1 Przykładowy skrypt
USE PUBS
SELECT au_id, au_lname FROM authors
SELECT pub_id, pub_name FROM publishers
INSERT publishers VALUES ('9997','SAMS Publishing', 'Seattle', 'WA','USA')
GO
SELECT * FROM stores
GO
DELETE publishers WHERE pub_id = '9997'
GO
Zadania wsadowe i skrypty nie mają nic wspólnego z transakcjami, które zostaną omówione w
kolejnej sekcji. Firma Microsoft dostarcza różnego rodzaju skryptów w katalogu
...\MSSQL\INSTALL; skrypty te mogą być używane jako przykłady. Należy znalezć pliki z
rozszerzeniem .SQL są one świetnymi przykładami poprawnych skryptów T-SQL.
Transakcje
Transakcja jest jednostką wykonywania, w której wszystkie polecenia są wykonane poprawnie lub
w przeciwnym przypadku nie jest wykonywane żadne z poleceń. Transakcje są stale używane, ale
użytkownik może nie być świadomy ich użycia. Przykładowo, kasjer w banku przelewa $50 z
rachunku rozliczeniowego na rachunek bieżący. Kasjer może obciążać rachunek na $50 a
następnie zapisać oszczędności na $50. Co się stanie, jeżeli kasjer zapomni przełożyć pieniądze na
rachunek bieżący? Większość ludzi byłaby bardzo zmartwiona z tego powodu. Spodziewaliby się,
że jeżeli pieniądze zostały zdjęte z rachunku rozliczeniowego powinny wpłynąć do oszczędności.
Jest to transakcja. Jednostka pracy przetwarza wszystkie polecenia kończąc je sukcesem, lub
kończy się niepowodzeniem i wycofuje wszystko co zostało wykonane. Możemy się spodziewać
transakcji w codziennym życiu, ale jako programista SQL Servera, użytkownik musi ręcznie
zaprogramować transakcje aby działały prawidłowo. Administrator musi rozumieć transakcje,
ponieważ mogą one powodować przepełnienie dzienników transakcji jeżeli są używane
nieprawidłowo.
Transakcje są tworzone przy zachowaniu następujących czterech własności, które złożone razem
zwane są własnościami ACID:
" Atomic (Niepodzielność) Mówi się, że transakcja jest niepodzielna (atomic) gdy zostaje
wykonana w całości lub jest w całości usuwana. Jeżeli jedno z poleceń nie powiedzie się,
wszystkie polecenia będące częścią transakcji zakończą się niepowodzeniem.
" Consistent (Spójność) Mówi się, że transakcja pozostawia bazę danych w spójnym stanie
po udanym lub nieudanym wykonaniu transakcji. Zmiany wykonane przez transakcję są
spójne z jednego stanu w drugi.
" Isolated (Odosobnienie) - Mówi się, że transakcja jest odosobniona gdy nie zachodzi w
reakcję lub nie powoduje konfliktu z innymi transakcjami w bazie danych.
" Durable (Trwałość) Mówi się, że transakcja jest trwała jeżeli gwarantowane jest, że
wykonane działania pozostaną kompletne bez względu na to co się stanie z baza po
poprawnym zakończeniu transakcji. Jeżeli wystąpi awaria zasilania i serwer bazy danych
ulegnie awarii, istnieje gwarancja ze transakcja będzie kompletna po ponownym
uruchomieniu serwera.
Transakcje gwarantują, że wykonane działania zakończą się sukcesem lub niepowodzeniem w
całości, jak opisano w poprzednim akapicie. Blokady są jedną z gwarancji takiego działania.
Podczas wykonywania transakcji, żadne inne transakcje nie mogą modyfikować danych, które
są zmieniane przez tę transakcję, aż do czasu gdy użytkownik zdecyduje że zmiany są trwałe.
Gdy użytkownik modyfikuje dane zakłada blokady wyłączne (exclusive lock) na danych z
którymi pracuje. Odwrotnie, nie można czytać danych innej transakcji, jeżeli wykonuje ona
działania modyfikujące dane. Użytkownik żąda w tej sytuacji blokady współdzielonej (shared
lock) na tych danych, ale inna transakcja używa blokady wyłącznej na tych samych danych i
powoduje że nikt inny nie może czytać tych danych. Blokady zostaną omówione szczegółowo
w dalszej części książki.
Typy transakcji
Używane są trzy typy transakcji: jawne (explicit), niejawne (implicit) i automatyczne (automatic -
często zwane autocommit).
Transakcje jawne
Transakcje jawne są konfigurowane przez użytkownika ręcznie. Do oznaczenia początku i końca
transakcji jawnej używane są zarezerwowane słowa. Są to m.in.: BEGIN TRAN[SACTION],
COMMIT TRAN[SACTION], COMMIT WORK, ROLLBACK TRAN[SACTION], ROLLBACK WORK i
SAVE TRAN[SACTION].
Aby rozpocząć transakcję jawną należy wpisać BEGIN TRAN (lub BEGIN TRANSACTION). Aby
poinformować SQL Server, że transakcja jest wykonana i cała operacja powinna zostać
zachowana, należy wpisać COMMIT TRAN (lub COMMIT WORK). Typowa transakcja może
wyglądać następująco:
BEGIN TRAN
UPDATE authors
SET city= 'San Jose' WHERE au_lname = 'Smith'
INSERT titles
VALUES ('BU1122', 'Teach Yourself SQL Server 2000 in 21 days',
'business','9998'. $35.00, $1000.00, 10,4501, 'A great book!',
'8/1/2000')
SELECT * from titleauthor
COMMIT TRAN
Potrzeba również czasem anulować transakcje. Aby to zrobić, należy użyć polecenia ROLLBACK
TRAN (lub ROLLBACK WORK). Następnie należy rozważyć następujący przykład polecenia
ROLLBACK TRAN:
BEGIN TRAN
Delete sales where title_id = 'BU1032'
IF @@ERROR >0
ROLLBACK TRAN
ELSE
COMMIT TRAN
Polecenie ROLLBACK TRAN anuluje całkowicie transakcję. Wszelkie operacje, które zostały
wykonane do tego momentu zostają wycofane (rollback) lub anulowane. Można tworzyć również
w transakcji punkty zwane savepoints (omówione w dalszej części), a następnie sukcesywnie
wycofywać do kolejnych takich punktów. Przykład najlepiej ilustruje opisywaną sytuację:
BEGIN TRAN
UPDATE table1 SET col1 = 5 WHERE col2 = 14
SAVE TRAN savepoint1
INSERT table2 values (3,16)
IF @@error > 0
ROLLBACK TRAN savepoint1
DELETE table3 WHERE col1 > 2
IF @@eerror > 0
ROLLBACK TRAN
ELSE
COMMIT TRAN
Warto zauważyć, że w poleceniu SAVE TRAN występuje nazwa savepoint. Poprzez umieszczenie
punktu savepoint1 w pierwszym wycofaniu, zaznacza się, że zamiast wycofania całej transakcji
od początku, nastąpi wycofanie do określonego nazwanego punktu w tym przypadku
savepoint1. Polecenie INSERT table2 było by wycofane ale transakcja będzie
kontynuowana. Zasadniczo, INSERT będzie usunięte z transakcji. Ponieważ nie ma nazwy przy
drugiej operacji wycofania, ROLLBACK TRAN powróci do polecenia BEGIN TRAN.
Teraz zostaną omówione polecenia TRANSACTION, rozpoczynające się od BEGIN i COMMIT.
BEGIN TRAN [transaction_name]
COMMIT TRAN [transaction_name]
COMMIT [WORK]
Można opcjonalnie przypisać transakcji nazwę (transaction_name). Nazwa transakcji musi być
prawidłowym identyfikatorem SQL Servera. Można zastąpić zmienną, wszędzie gdzie występuje
transaction_name, dopóki jest ona tłumaczona na poprawną nazwę transakcji. Nie ma potrzeby
nazywania transakcji, z wyjątkiem najbardziej zewnętrznego polecenia BEGIN w transakcji. Nie
można określić nazwy transakcji w poleceniu COMMIT WORK:
Transakcje mogą być zagnieżdżone, chociaż to zagnieżdżanie jest jedynie własnością składni.
Naprawdę transakcje nie mogą być zagnieżdżane. Można wpisać wiele transakcji w skrypcie,
ale faktycznie używana jest tylko jedna transakcja.
Składnia polecenia SAVE TRANSACTION wygląda następująco:
SAVE TRAN [savepoint_name]
savepoint_name oznacza nazwę punktu zachowania, do którego następuje wycofanie części
operacji w transakcji, bez anulowania całej transakcji. Nazwa savepoint musi być poprawnym
identyfikatorem SQL Servera.
Składnia polecenia ROLLBACK TRANSACTION jest następująca:
ROLLBACK TRAN [transaction_name | savepoint_name]
ROLLBACK WORK
Opis opcjonalnych nazw jest podobny jak w przypadku przedstawionych wcześniej poleceń
BEGIN/COMMIT TRAN i SAVE TRAN. Również w tym przypadku polecenie ROLLBACK WORK nie
pozwala na określenie w tym miejscu nazwy transakcji ani nazwy punktu savepoint.
Zmienna globalna @@trancount Zmienna globalna @@trancount ma zastosowanie
bezpośrednio do tych transakcji. Po wpisaniu BEGIN TRAN zmienna @@trancount zwiększa się o
jeden. Polecenie SAVE TRAN nie wpływa na tę zmienną. ROLLBACK TRAN może mieć kilka
różnych wyników w zależności czy jest podana nazwa transakcji czy też nie. Jeżeli nie jest podana
nazwa transakcji, zmienna @@trancount jest ustawiana z powrotem na 0 (całość operacji jest
wycofywana). Jeżeli jest określona nazwa ostatniej transakcji, zmienna @@trancount jest
obniżana o jeden. Polecenie ROLLBACK WORK zawsze zeruje zmienną @@trancount oraz anuluje
całość operacji wykonywanych od pierwszego BEGIN TRAN. Aby przekonać się jak działa ta
zmienna należy przeanalizować następujący kod:
SELECT @@TRANCOUNT -- It should return 0.
BEGIN TRAN t1
SELECT @@TRANCOUNT - It should return 1.
SAVE TRAN savepoint1
SELECT @@TRANCOUNT - It still is set to 1.
ROLLBACK TRAN savepoint1
SELECT @@TRANCOUNT - It still is set to 1.
BEGIN TRAN t2
SELECT @@TRANCOUNT - It should return 2.
ROLLBACK TRAN
SELECT @@TRANCOUNT It's back to 0.
Dozwolone i niedozwolone polecenia Niektóre polecenia nie są dozwolone jako cześć transakcji
jawnej, są to:
ALTER DATABASE RESTORE DATABASE
DROP DATABASE CREATE DATABASE
RECONFIGURE RESTORE LOG
BACKUP LOG UPDATE STATISTICS
Inne polecenia mogą pojawiać się razem w transakcji, chociaż nie zmienia to żadnej z reguł
omówionych wcześniej.
Punkty zachowania Nie są podane limity ilości punktów zachowania (savepoints), które mogą
występować w pojedynczej transakcji. Gdy transakcja jest zatwierdzona, nie ma możliwości
wycofania.
W pojedynczej transakcji nazwy savepoints mogą się powtarzać, jednak tylko ostatnie wystąpienie
punktu zachowania jest faktycznie używane przy wycofywaniu do punktu w czasie. Przykładowo,
następujące polecenie ROLBACK TRAN transave1powoduje wycofanie jedynie do drugiego
wystąpienia punktu transave1. Pierwsze wystąpienie nazwy jest ignorowane, gdyż pojawia się
drugie wystąpienie.
BEGIN TRAN
INSERT
UPDATE
SAVE TRAN transave1
DELETE
INSERT
SELECT
SAVE TRAN transave1
INSERT
DELETE
IF @@ERROR <> 0
ROLLBACK TRAN transave1
ELSE
COMMIT TRAN
Gdy polecenie ROLLBACK TRAN jest używane w procedurze wyzwalanej i nie są określone
żadne nazwy punktów zachowania, całość transakcji jest wycofywana i pozostała część pliku
wsadowego nie jest wykonywana. Jednak, wykonywanie działań może być kontynuowane z
następnym zadaniem wsadowym. Należy to przetestować pisząc odpowiedni kod i
sprawdzając, czy ten szczególny rodzaj wycofania jest obsługiwany. Rozsądnie jest wywołać
powstanie błędu danego typu w procedurze wyzwalanej powodującego powrót do wołającej
procedury i oprzeć się na wywoływanym działaniu, aby prawidłowo określić stan transakcji.
Podejście to jest znacznie bezpieczniejsze.
Zdalne procedury składowane Wywołania zdalnych procedur nie są normalnie rozważane jako
część transakcji:
BEGIN TRANSACTION
UPDATE table1 SET col1= 5 WHERE col1 = 1
DELETE table1 WHERE col1 = 5
EXEC server2.pubs..usp_insertpublisher parm1 parm2 parm3
COMMIT TRAN
Jeżeli procedura składowana pubs..usp_insertpublisher na serwerze 2 miała wywołać
polecenie ROLLBACT TRAN nie będzie miała wpływu na lokalną transakcję.
Jednak, opcja konfiguracji serwera REMOTE_PROC_TRANSACTIONS, normalnie ustawiona na 0
(wyłączona), kontroluje czy zdalne procedury składowane są automatycznie wykonywane. Aby
włączyć automatyczną rejestrację zdalnych procedur składowanych w transakcjach należy
uruchomić następujący kod:
EXEC sp_configure "REMOTE_PROC_TRANSACTIONS", 1
RECONFIGURE WITH OVERRIDE
Przykład ten wpływa na wykonywanie wszelkich zdalnych procedur składowanych na serwerze,
więc należy go uruchamiać po odpowiednim zastanowieniu. Transakcje rozproszone zostaną
omówione w dalszej części rozdziału w sekcji transakcje rozproszone .
Transakcje automatyczne
Nawet jeżeli wydaje się, że transakcje nie są używane, mogą być one wykonywane w tle
(niewidoczne dla użytkownika). Wszelkie wykonania polecenia modyfikacji danych w SQL
Serverze to transakcje niejawne (implied transactions).
W następującym zadaniu wsadowym każde polecenie SQL jest oddzieloną transakcją. Czyli to
poniższe zadanie wsadowe to faktycznie trzy osobne transakcje. Jeżeli jedno z tych wyrażeń nie
powiedzie się, nie ma wpływu na inne. Każde polecenie kończy się sukcesem lub niepowodzeniem
(jedynie własnym) bez względu na inne polecenia w pliku wsadowym.
INSERT table1 VALUES (1,'abcde')
UPDATE table1 SET col1 = 5 WHERE col1 = 1
DELETE FROM table1 WHERE col1 = 5
GO
Transakcje mogą także prowadzić do poprawy wydajności. SQL Server może znacznie lepiej
wykorzystywać dysk ponieważ transakcja nie jest rejestrowania w dzienniku transakcji dopóki nie
zostanie ukończona. Dlatego, korzystnie jest grupować polecenia. Jeżeli poprzednie polecenia
zostaną zgrupowane w jedną transakcję, zamiast dziewięciu, następuje jedynie pięć wpisów do
dziennika transakcji.
BEGIN TRAN
INSERT table1 VALUES (1,'abcde')
UPDATE table1 SET col1 = 5 WHERE col1 = 1
DELETE FROM table1 WHERE col1 = 5
COMMIT TRAN
Jeżeli zwiększy się grupowanie poleceń, transakcje mogą znacząco podnieść wydajność poleceń
modyfikacji danych.
Transakcje niejawne
Transakcje niejawne są tworzone aby zachować zgodność ze standardem American National
Standards Institute (ANSI). Gdy włączona jest opcja transakcji niejawnych, wybrane polecenia T-
SQL automatycznie uruchamiają polecenie BEGIN TRAN. Należy natomiast jawnie zatwierdzić lub
wycofać te polecenia przy pomocy polecenia COMMIT lub ROLLBACK TRAN.
Transakcje niejawne można włączyć z poziomu sesji następującym poleceniem:
SET IMPLICIT_TRANSACTIONS ON
Do końca sesji, następujące polecenia będą wymagały bezpośredniego zatwierdzenia (lub
wycofania):
ALTER TABLE GRANT REVOKE
CREATE DROP DELETE
SELECT INSERT UPDATE
TRUNCATE TABLE FETCH OPEN
Aby wyłączyć transakcje niejawne należy wpisać polecenie:
SET IMPLICIT_TRANSACTIONS OFF
Przykładowo, następujące dwa fragmenty kodu zródłowego są identyczne (w znaczeniu
transakcji). Warto zauważyć, że można zweryfikować poniższe polecenia przy pomocy zmiennej
@@trancount.
CREATE TABLE table1 (col1 int not null)
BEGIN TRAN
INSERT table1 VALUES (1)
SELECT @@trancount
COMMIT TRAN
SET IMPLICIT_TRANSACTIONS ON
INSERT table1 values (2)
SELECT @@trancount
COMMIT TRAN
Włączenie opcji IMPLICIT_TRANSACTIONS wymaga pamiętania o zatwierdzaniu lub
wycofywaniu każdej transakcji. Aatwo o tym zapomnieć jeżeli to nastąpi, transakcje pozostaną
otwarte i spowodują powstanie blokad (omówionych pózniej) na dłuższy okres, niż normalnie.
Zaleca się nie włączanie tej opcji; chyba, że użytkownik jest pewny, że będzie pamiętał o
zatwierdzaniu transakcji.
Działanie transakcji
Teraz, po zapoznaniu się z transakcjami jawnymi, niejawnymi i automatycznymi należy
prześledzić krok po kroku opis działań SQL Servera podczas wykonywania transakcji. Działania
te zostaną omówione na podstawie przykładowego zbioru poleceń SQL przedstawionych na
wydruku 12.2.
Wydruk 12.2. Wykonywanie zbioru transakcji krok po kroku
BEGIN TRAN
INSERT table1 VALUES (1,'abcde')
UPDATE table1 SET col1 = 5 WHERE col1 = 1
DELETE FROM table1 WHERE col1 = 5
COMMIT TRAN
Działania wykonywane w powyższym przykładzie:
1. Gdy do bazy danych zostaje wysłane polecenie BEGIN TRAN, analizator składni (parser)
SQL Servera wykrywa żądanie rozpoczęcia jawnej transakcji. Jednak, SQL Server 2000
nie alokuje rekordu dziennika w pamięci dopóki nie jest wykonane jakieś konkretne działanie,
czyli technicznie transakcja jeszcze się nie rozpoczęła.
2. Uruchamiane jest polecenie INSERT. SQL Server tworzy rekord dziennika w pamięci i
alokuje identyfikator transakcji (ID) dla nowej transakcji. W dzienniku transakcji
rejestrowany jest nowy wiersz a następnie modyfikowana jest w pamięci strona danych dla
table1 (zobacz rysunek 12.1). Jeżeli wymagana strona nie znajduje się w pamięci, jest
pobierana z dysku.
3. Kolejne polecenia są uruchamiane podobnie. Polecenie UPDATE jest rejestrowane w dzienniku
transakcji a następnie w pamięci modyfikowana jest strona danych (zobacz rysunek 12.2).
Rysunek 12.1.
Krok 2 procesu
wykonywania
transakcji.
Rysunek 12.2.
Krok 3 procesu
wykonywania
transakcji.
W tym przykładzie, pojedynczy wiersz jest rejestrowany w dzienniku do aktualizacji. W
większości przypadków jest prawdą, że dziennik pokazuje na bieżąco usunięcie, a następnie
wstawienie wiersza a nie pojedynczy modyfikowany rekord. Pobranie pojedynczego
modyfikowanego rekordu do zapisania w dzienniku wymagałoby uaktualnienia w miejsce
poprzedniego . Różne warunki, których spełnienie jest wymagane do uaktualnienia w miejscu
są omówione szczegółowo w książce Microsoft SQL Server 2000 Unleashed opublikowanej
przez Sams Publishing.
4. Kiedy SQL Server otrzyma polecenie COMMIT TRAN, w dzienniku transakcji jest zapisywany
rekord dla odpowiedniej bazy danych (zobacz rysunek 12.3). Jest to gwarancja, że transakcja
może być odtworzona. Ponieważ zmiany w dzienniku są zapisywane na dysku, gwarantuje to,
ze transakcje mogą być odtworzone nawet w przypadku, gdy wystąpi awaria zasilania lub
uszkodzenie bazy danych zanim strona danych zostanie zapisana na dysk.
Rysunek 12.3.
Krok 4 procesu
wykonywania
transakcji.
W przypadku SQL Servera 2000 rekordy dziennika są zapisywane w osobnym pliku (lub
zestawie plików) i nie są dostępne przy pomocy T-SQL. Firma Microsoft nie dostarczyła
narzędzia zapewniającego dostęp do dziennika transakcji (innego niż zaawansowane polecenie
DBCC LOG, które nie jest objęte zakresem tej książki). Jedynie wewnętrzne procesy SQL Servera,
takie jak tworzenie kopii bezpieczeństwa i odtwarzanie potrzebują mieć dostęp do dziennika
transakcji.
Proces CHECKPOINT
Po omówieniu transakcji, powstaje pytanie, kiedy dane są zapisywane na dysk. Wpisy dziennika
zostają zapisane przy uruchomieniu polecenia COMMIT TRAN. Natomiast zapisywanie stron
danych na dysk jest związane z procesem CHECKPOINT, wewnętrznym procesem SQL Servera,
używanym do kopiowania (flush) danych z pamięci na dysk.
Checkpoint (punkt kontrolny) zapewnia, że odtwarzanie zatwierdzonych transakcji nie będzie
zajmowało nadmiernej ilości czasu. Po wystąpieniu punktu kontrolnego, w dzienniku rejestrowany
jest wpis, określający, że wszystkie modyfikowane dane zostały zapisane na dysk. Wpis ten
określa punkt w dzienniku transakcji, do którego proces odtwarzania SQL Servera może
odtwarzać dane, oraz zapewnia, że nie trzeba sprawdzać żadnych wcześniej zatwierdzonych
transakcji aby zapewnić całkowite odtworzenie.
W SQL Serverze używane są dwa rodzaje punktów kontrolnych: automatyczny i ręczny.
Automatyczny proces checkpoint występuje w zależności od wewnętrznych kalkulacji SQL
Servera. Przy pomocy opcji konfiguracji RECOVERY INTERNAL, konfiguruje się jak często
będzie występował proces checkpoint. Opcja ta określa w minutach maksymalną ilość czasu
wymaganą do odtworzenia każdej bazy danych w systemie. Jeżeli SQL Server stwierdzi, że
odtworzenie bazy zajmie przewidzianą ilość czasu lub więcej, ustawia automatyczny punkt
kontrolny (checkpoint). Gdy to się stanie, wszystkie modyfikowane dane (dla tej bazy danych) są
zapisywane z pamięci na dysk, i wszystkie rekordy dziennika tej bazy danych są również
zapisywane na dysk. Automatyczny proces chekpoint przegląda co 60 sekund wszystkie bazy
danych, określając czy dana baza nie potrzebuje ustawienia punktu kontrolnego. Domyślnym
ustawieniem dla opcji RECOVERY INTERNAL jest 0, co oznacza, że SQL Server sam decyduje
kiedy jest potrzebny checkpoint.
Automatyczny punkt kontrolny występuje również w dwóch innych sytuacjach. Po pierwsze,
występuje gdy zostaje uruchomiona procedura składowana sp_dboption do zmiany opcji bazy
danych. Po drugie występuje przy wyłączaniu SQL Servera. SQL Server można wyłączyć
poleceniem T-SQL SHUTDOWN lub poprzez zamknięcie usługi MSSQLServer. W takich
sytuacjach SQL Server automatycznie ustawia punkt kontrolny.
Aby wymusić w dowolnym momencie ręczny punkt kontrolny, należy wpisać polecenie T-SQL
CHECKPOINT. Aby uruchomić to polecenie użytkownik musi należeć do roli bazy danych
db_owner. Gdy ustawiony jest ręczny punkt kontrolny, wszystkie modyfikowane strony z pamięci
są kopiowane na dysk, w ten sam sposób jak w przypadku automatycznego procesu checkpoint.
W przypadku baz danych z ustawioną opcją Truncate Log on checkpoint, automatyczny
proces checkpoint wycina z dziennika transakcji zatwierdzone transakcje w regularnych
odstępach czasu (lub gdy dziennik się przepełni). Ręczny proces checkpoint nie ma wpływu na
dziennik transakcji (poza kopiowaniem wpisów dziennika na dysk), bez względu na ustawienie
w bazie danych opcji Truncate Log on checkpoint.
Transakcje rozproszone
Wszystkie omawiane do tej pory transakcje dotyczyły jednego serwera. SQL Server 2000 może
obsługiwać transakcje związane z więcej niż jednym serwerem. Możliwość ta jest obsługiwana
przy pomocy usługi Microsoft Distributed Transaction Coordinator (MSDTC).
Można korzystać z transakcji rozproszonych na różne sposoby:
" Można programować transakcje rozproszone przy pomocy DB-Lib, open database
connectivity (ODBC) lub interfejs programowy aplikacji (API) typu object linking and
embedding (OLE) DB. ActiveX Data Objects (ADO) również zezwala na korzystanie z
transakcji rozproszonych. Opcje te są poza zakresem niniejszej książki.
" Można korzystać ze składni T-SQL: BEGIN DISTRIBUTED TRANSACTION.
" Można ustawić opcję SET REMOTE_PROC_TRANSACTION aby umożliwić korzystanie z
transakcji rozproszonych w pojedynczej sesji.
Teraz zostaną omówione dwie ostatnie metody. Aby umieścić usługę MSDTC w transakcji i
zapewnić koordynację tej usługi na wielu serwerach, należy uruchomić polecenie BEGIN
DISTRIBUTED TRANSACTION w dokładnie ten sam sposób jak polecenie BEGIN TRAN.
Wcześniejsza sekcja dotycząca transakcji zaznaczała, że zdalne procedury składowane działają
poza bieżącą transakcją, z której zostały wywołane. W przypadku transakcji rozproszonych,
zdalne procedury składowane są włączone do transakcji. Błąd na zdalnym serwerze wpływa
również na wywołujący serwer lub serwery. W następującym przykładzie kodu, jeżeli w
procedurze składowanej wywoływanej z serwera 2 wystąpi błąd, będzie miał on wpływ na
transakcję na bieżącym serwerze:
BEGIN DISTRIBUTED TRANSACTION
UPDATE table1 SET col1= 5 WHERE col1 = 1
DELETE table1 WHERE col1 = 5
EXEC server2.pubs..usp_insertpublisher parm1 parm2 parm3
COMMIT TRAN
SQL Server 2000 może również kierować zapytania do zdalnych serwerów jako cześć polecenia
T-SQL. Dlatego, poniższy kod jest również prawidłową transakcją rozproszoną w SQL Serverze
2000:
BEGIN DISTRIBUTED TRANSACTION
INSERT remoteserver.pubs..remotetable(col1, col2) VALUES (1,2)
UPDATE localtab SET col2= 5 WHERE col1 = 1
COMMIT TRAN
Trzecią metodą jest użycie polecenia SET REMOTE_PROC_TRANSACTIONS. Jeżeli ta opcja jest
ustawiona w sesji, wszystkie zdalne procedury składowane wywoływane podczas sesji są
rozważane jako cześć transakcji. Można również ustawić tę samą opcję konfiguracyjną przy
pomocy procedury sp_configure, opcja jest ustawiona od tego momentu dla wszystkich sesji.
Przed ustawieniem tej opcji dla całego serwera (wszystkich sesji), należy się upewnić czy zostały
przetestowane wszelkie konsekwencje wynikające z używania dowolnej istniejącej zdalnej
procedury składowanej.
SQL Server 2000 pozwala na łączenie się z dwoma typami zdalnych baz danych: serwerami
zdalnymi i serwerami sprzężonymi. Opcja zdalnego serwera jest dostarczana dla zachowania
zgodności wstecz, a opcja serwera sprzężonego została ulepszona w SQL Serverze 2000.
Serwery zdalne
Serwery zdalne umożliwiają uruchamianie procedur składowanych w zdalnych bazach danych
SQL Servera. W zależności od użytego polecenia Transact-SQL: BEGIN DISTRIBUTED
TRANSACTION lub SET REMOTE_PROC_TRANSACTIONS ON, zdalne procedury składowane mogą
być (lub nie) częścią transakcji. Aby zezwolić na uruchamianie zdalnych procedur, należy
skonfigurować zdalny serwer:
1. Należy dodać nazwę zdalnego serwera do tabeli systemowej sysservers na SQL Serverze.
Można to zrobić, uruchamiając polecenie: EXEC sp_addserver remoteserver.
2. Należy dodać własny serwer do tabeli sysservers w zdalnej bazie danych SQL Servera
poprzez polecenie: EXEC sp_addserver yourserver.
3. Na serwerze lokalnym należy uruchomić następujące polecenia T- SQL:
EXEC sp_addremotelogin remoteserver, sa, sa
EXEC sp_remoteoption remoteserver, sa, sa, trusted, true
4. Na zdalnym serwerze, należy uruchomić kod z punktu 3 , ale podając nazwę serwera
lokalnego.
Teraz, logując się jako sa można już uruchamiać zdalne procedury składowane. Aby ustawić inne
konta logowania, potrzeba uruchomić poprzednie procedury składowane. Aby odwołać się do
zdalnych procedur składowanych, należy użyć nazwy z czterech części
servername.dbname.owner.procname z wykonywanym poleceniem. Przykładowo,
odwołując się do procedury składowanej byroyalty w bazie danych pubs na serwerze gizmo,
należy uruchomić następujący kod:
EXEC gizmo.pubs.dbo.byroyalty 10
To podejście jest bardzo pożyteczne, ale nie jest tak elastyczne jak używanie serwerów
sprzężonych.
Serwery sprzężone (linked servers)
Firma Microsoft usprawniła obsługę serwerów sprzężonych w SQL Serverze 2000. Serwery
sprzężone umożliwiają otworzenie OLE DB rowset na zdalnym serwerze. Inaczej niż w
omawianych już zdalnych serwerach, serwery sprzężone umożliwiają pobieranie danych ze zródeł
innych niż SQL Server. Można tym sposobem uzyskać dostęp do dowolnego zródła danych OLE
DB. Czyli, można mieć dostęp do danych z Oracle, Microsoft Access, Microsoft Excel lub
każdego innego zródła danych OLE DB.
Często wykorzystanie serwerów sprzężonych jest korzystniejsze niż serwerów zdalnych. Można
nadal wykonywać procedury składowane, ale można również stosować złączenia zdalnych tabel.
Aby ustawić serwer sprzężony, należy użyć systemowych procedur składowanych
sp_addlinkedserver i sp_addlinkedsrvlogin:
Exec sp_addlinkedserver 'remoteserver', 'SQL Server'
Exec sp_addlinkedsrvlogin 'remoteserver', 'TRUE'
Jeżeli mają być uruchamiane zapytania rozproszone lub zdalne procedury składowane z
opcjami serwera przyłączonego, należy uruchomić skrypt instcat.sql z katalogu instalacyjnego
SQL Servera 2000 dla wszystkich serwerów SQL Server 6.5.
Wcześniejszy kod dodaje do SQL Servera zdalny serwer i określa, że w przypadku próby
połączenia ze zdalnym serwerem, należy logować się ze wszystkimi danymi logowania, które są
używane na serwerze lokalnym.
Dla przykładu, można uruchomić zapytania, które łączą tabelę titles z serwera lokalnego z
tabelą publishers z serwera zdalnego:
Select t1.title, t2.pub_name
From pubs..titles t1 This is the local table
Inner Join remoteserver.pubs..publishers t2 This is the remote table
ON t1.pub_id = t2.pub_id
Jak można się domyśleć, potrzeba jeszcze wiele dodatkowych informacji aby zaimplementować
serwery sprzężone szczególniej jeżeli decyduje się na łączenie tabel SQL Servera z danymi ze
zródła innego niż SQL Server używając możliwości heterogenicznych OLE DB. Tematy te są
poza zakresem tej książki i zaleca się odwołanie do książki Microsoft SQL Server 2000 Unleashed
opublikowanej przez Sams publishing, aby znalezć szczegółowe informacje o serwerach
sprzężonych.
Elementy języka kontroli przepływu
Jeżeli zadanie wsadowe z wieloma poleceniami jest wysyłane do SQL Servera do wykonania,
polecenia są wykonywane w kolejności, w jakiej zostały podane. Język T-SQL dostarcza kilku
poleceń, które pozwalają na zmianę kolejności wykonywania poleceń w pliku wsadowym. Są one
szczególnie przydatne w transakcjach, procedurach wyzwalanych, funkcjach i procedurach
składowanych.
Blok BEGIN...END
Niektóre z poleceń kontroli przepływu jakie zostaną omówione będą wymagały pojedynczego
polecenia jako części swojej składni. Wszędzie gdzie oczekiwane jest pojedyncze polecenie,
można używać tego pojedynczego polecenia. Jeżeli zajdzie potrzeba, aby wiele poleceń było
wykonywanych razem, należy zamknąć je pomiędzy słowami kluczowymi BEGIN i END.
Konstrukcja ta zostanie wykorzystana w kolejnych przykładach.
Polecenie PRINT
Do tego momentu, jedynym sposobem zwrócenia informacji z SQL Servera do programu klienta
było korzystanie z polecenia SELECT. SQL Server obsługuje również polecenie PRINT, ale jest
ono ograniczone:
PRINT {'Any ASCII text' | @local_variable | @@global_variable}
Wszystko, co można drukować to łańcuch ASCII (stała łańcuchowa) lub zmienna typu znakowego
(stała lub o zmiennej długości), jak w tym przykładzie:
PRINT "Hello"
PRINT @@version
Jeżeli potrzeba wydrukować coś bardziej złożonego, należy zbudować łańcuch w zmiennej
znakowej a następnie wydrukować tę zmienną.
Następujący przykład używa @msg do zbudowania jednego dużego łańcucha do wydrukowania.
Nie można używać operacji konkatenacji ani zmiennych liczbowych w poleceniu PRINT, ale
można używać ich w ustawieniu SET i SELECT, a następnie wydrukować wyniki.
USE pubs
DECLARE @msg varchar(50),
@numWA tinyint
SELECT @numWA = COUNT(*) FROM stores
WHERE state = 'WA'
SELECT @msg = 'There are ' + convert(varchar(3), @numWA) +
' stores in Washington'
PRINT @msg
(1 row(s) affected)
(1 row(s) affected)
There are 2 stores in Washington.
Block IF...ELSE
Blok IF..ELSE pozwala aby polecenie było wykonywane warunkowo. Po słowie IF występuje
wyrażenie, które musi być warunkiem typu prawda lub fałsz. Jeżeli wyrażenie jest prawdziwe, to
polecenie jest wykonywane. Opcjonalne słowo kluczowe ELSE wprowadza polecenie
alternatywne, które może być wykonywane gdy wyrażenie występujące po IF jest fałszywe.
IF Boolen_expression
{sql_statement | statement_block}
ELSE {Boolean_expression}
{sql_statement | statement_block}
Przykładowo, poniższe, polecenie wywołuje procedurę uspWeeklyReport jeżeli dniem tygodnia
jest piątek; w innym przypadku wywołuje procedurę upsDailyReport:
IF (datename(dw, getdate()) = 'Friday')
BEGIN
PRINT 'Weekly raport'
EXEC uspWeeklyReport
END
ELSE
BEGIN
PRINT 'Daily Report'
EXEC uspDailyReport
END
Warto zauważyć, że nie ma określonego słowa kluczowego, które oznacza koniec bloku IF..ELSE.
Wyrażenie Boolean, które następuje po słowie IF może zawierać polecenie SELECT. Jeżeli to
polecenie SELECT zwraca pojedynczą wartość, wartość ta może być porównywana z inną
wartością aby było możliwe utworzenie wyrażenia Boolean.
W następującym przykładzie, jeżeli średnia cena książki jest większa niż$15, należy wydrukować
jeden komunikat, jeżeli średnia cena książki jest mniejsza lub równa $15, należy wydrukować inny
komunikat:
IF (SELECT avg(price) FROM titles) > $15
PRINT 'Hold a big sale'
ELSE
PRINT 'Time to raise prices'
Jeżeli polecenie SELECT następujące po słowie IF zwraca więcej niż jedną wartość, można
skorzystać ze specjalnej formy składni IF IF EXISTS:
IF EXISTS (SELECT statement)
{sql_statement | statement_block}
[ELSE
{sql_statement | statement_block}]
IF EXISTS zwraca wartość prawda jeżeli dane polecenie SELECT zwraca jakikolwiek wiersz
lub zwraca wartość fałsz w przypadku, gdy polecenie SELECT nie zwróciło żadnego wiersza.
Następujący przykład zwraca wszystkie informacje o książce publikowanej przez wydawcę o ID
9933:
IF EXISTS
(SELECT * FROM titles WHERE pub_id = '9933')
BEGIN
PRINT 'Here are the books: '
SELECT * FROM titles WHERE pub_id = '9933'
END
ELSE
PRINT 'No books from that publisher'
Poniższe zapytanie daje takie same wyniki ale przez zadanie przeciwnego warunku zapytania:
IF NOT EXISTS
(SELECT * FROM titles WHERE pub_id = '9933')
BEGIN
PRINT 'No books from that publisher'
RETURN
END
ELSE
BEGIN
PRINT 'Here are the books'
SELECT * FROM titles WHERE pub_id = '9933'
END
W niektórych przypadkach, IF EXISTS dostarcza lepszej wydajności niż alternatywna metoda
ponieważ SQL Server może zatrzymać wykonywanie operacji pobierania wierszy, gdy tylko
zostanie znaleziony pierwszy wiersz.
Nie należy używać IF EXISTS z funkcjami agregującymi, ponieważ one zawsze zwracają dane,
nawet jeśli wartością danych jest zero. Przykładowo, następujący kod sprawdzający czy
wydawca 9933 opublikował jakiekolwiek książki, nie wykona swego działania:
IF EXISTS
(SELECT COUNT(*) FROM titles WHERE PUB_ID = '9933')
To polecenie SELECT zawsze zwróci jeden wiersz. Jeżeli baza danych nie zawiera żadnych
książek tego wydawcy, ten jedyny wiersz będzie miał wartość 0, ale IF EXISTS zwróci wartość
prawda .
Wyrażenia CASE
Programiści często wymagają możliwości stosowania wyrażenia warunkowego w innym
poleceniu. Wyrażenie CASE pozwala aby wyrażenia T-SQL były upraszczane dla wartości
warunkowych. Wyrażenie to pozwala zwracać różne wartości w zależności od wartości kontrolnej
lub warunku.
Składnia prostego polecenia CASE wygląda następująco:
CASE expression
WHEN expression1 THEN expression1
[[WHEN expression2 THEN expression2[ [...]]
[ELSE expressionN]
END
Można używać również znalezionych wyrażeń. Składnia w tym przypadku jest następująca:
CASE
WHEN boolean_expression1 THEN expression1
[[WHEN boolean_expression2 THEN expression2[ [...]]
[ELSE expressionN]
END
Proste wyrażenie CASE porównuje wartość początkową z każdą z wartości na liście i zwraca w
wyniku skojarzone z tą wartością wyrażenie. Jeżeli żadne z wyrażeń nie zostanie dopasowane,
zwracane jest wyrażenie występujące po słowie kluczowym ELSE.
Teraz warto przeglądnąć wykorzystanie niektórych wyrażeń CASE w praktyce. W następującym
przykładzie, dla każdego wiersza w tabeli titles, wartość z kolumny type jest porównywana z
każdą wartością z listy CASE. Pierwsze dopasowanie wartości określa, które wyrażenie będzie
zwrócone. Wyrażenie wyszukujące CASE zwraca wartość skojarzoną z pierwszym wyrażeniem
Boolean z listy, które jest prawdziwe. Jeżeli żadne z wyrażeń nie zwróci prawdy, zwrócone
zostanie wyrażenie następujące po słowie ELSE.
SELECT title_id, type = CASE type
WHEN 'business' THEN 'Business Book'
WHEN 'psychology' THEN 'Psychology Book'
WHEN 'mod_cook' THEN 'Modern Cooking Book'
WHEN 'trad_cook' THEN 'Traditional Cooking Book'
WHEN 'popular_comp' THEN 'Popular Computing Book'
WHEN 'undecided' THEN 'No type determined yet'
END
FROM titles
Wyniki powinny wyglądać podobnie jak przedstawione poniżej:
title_id type
-------- ------------------------
BU1032 Business Book
BU1111 Business Book
BU2075 Business Book
BU7832 Business Book
MC2222 Modern Cooking Book
MC3021 Modern Cooking Book
MC3026 No type determined yet
PC1035 Popular Computing Book
PC8888 Popular Computing Book
PC9999 Popular Computing Book
PS1372 Psychology Book
PS2091 Psychology Book
PS2106 Psychology Book
PS3333 Psychology Book
PS7777 Psychology Book
TC3218 Traditional Cooking Book
TC4203 Traditional Cooking Book
TC7777 Traditional Cooking Book
W kolejnym przykładzie, dla każdego z wierszy w tabeli titles, wiele wyrażeń jest wyliczanych.
W tym przypadku, wartość price jest porównywana z innymi wartościami. Pierwsze wyrażenie,
które jest prawdziwe określa, które wyrażenie będzie zwrócone.
SELECT title_id, cost = CASE
WHEN price < 10 THEN 'Cheap'
WHEN price BETWEEN 10 and 20 then 'Midrange'
ELSE 'Expensive'
END
FROM titles
Wyniki powinny wyglądać podobnie jak przedstawione poniżej:
title_id cost
-------- ---------
BU1032 Midrange
BU1111 Midrange
BU2075 Cheap
BU7832 Midrange
MC2222 Midrange
MC3021 Cheap
MC3026 Expensive
PC1035 Expensive
PC8888 Midrange
PC9999 Expensive
PS1372 Expensive
PS2091 Midrange
PS2106 Cheap
TC3218 Expensive
TC4203 Midrange
TC7777 Midrange
Warto zauważyć, ze pierwszy przykład CASE był jedynie specjalną wersją szukanego wyrażenia
CASE. Można przepisać pierwszy przykład następująco i otrzymać te same wyniki:
SELECT title_id, type = case
WHEN type = 'business' THEN 'Business Book'
WHEN type = 'psychology' THEN 'Psychology Book'
WHEN type = 'mod_cook' THEN 'Modern Cooking Book'
WHEN type = 'trad_cook' THEN 'Traditional Cooking Book'
WHEN type = 'popular_comp' THEN 'Popular Computing Book'
ELSE 'No type determined yet'
END
FROM titles
Można używać prostej formy wyrażenia CASE nawet jeśli wszystkie warunki sprawdzają
równość. Można również używać dwóch innych funkcji zamiast CASE: COALESCE i NULLIF.
Funkcja COALESCE
Czasem wyrażenie CASE ma następującą formę:
CASE
WHEN expr1 IS NOT NULL THEN expr1
[[WHEN espr2 IS NOT NULL THEN expr2]
[...]]
[ELSE exprN]
END
W tym przypadku, wartość zwracana przez wyrażenie jest pierwszą wartością z listy, która nie jest
wartością NULL. Alternatywnym sposobem zapisana tego wyrażenia jest wykorzystanie funkcji
COALESCE. W tym przypadku tabela wages (zarobki) posiada trzy kolumny z informacjami o
rocznych zarobkach pracownika, płacy za godzinę, wynagrodzenia miesięcznego i prowizji.
Jednak, pracownik otrzymuje tylko jeden typ zapłaty i tylko jedna z trzech kolumn posiada
wpisaną wartość. Pozostałe dwie kolumny mają wartość NULL. Można uruchomić poniższy kod,
aby stworzyć tabelę z wprowadzonymi przykładowymi danymi:
USE pubs
GO
CREATE TABLE tblWages (
intEmpID int NOT NULL,
curHourly money NULL,
curSalary money NULL,
curCommission money NULL,
bigNumSales bigint NULL)
GO
INSERT tblWages VALUES (111, NULL, 52000, NULL, NULL)
INSERT tblWages VALUES (112, 14, NULL, NULL, NULL)
INSERT tblWages VALUES (113, NULL, NULL, 0.15, 50000)
INSERT tblWages VALUES (114, NULL, 73000, NULL, NULL)
INSERT tblWAGES VALUES (115, 4.90, NULL, NULL, NULL)
INSERT tblWages VALUES (116, NULL, 28500, NULL, NULL)
GO
Po utworzeniu tabeli i zapełnieniu danymi, można określić całkowitą kwotę płaconą wszystkim
pracownikom korzystając z funkcji COALESCE, aby otrzymać wartości inne od NULL
znalezione w kolumnach curHourly, curSalary i curComission:
SELECT intEmpID as "Employee ID", "Total Salary" = Convert(money,
(COALESCE(curHourly * 40 * 52, curSalary, curCommission *
bigNumSales)))
FROM tblWages
Employee ID Total Salary
----------- ------------
111 52000.0000
112 29120.0000
113 7500.0000
114 73000.0000
115 10192.0000
116 28500.0000
(6 row(s) affected)
Funkcja NULLIF
Czasem wyrażenie CASE przyjmuje następującą formę:
CASE
WHEN expr1 = expr2 THEN NULL
[ELSE expr1]
END
W tym przypadku wartość zwrócona przez wyrażenie wynosi NULL jeżeli wyrażenia 1 i 2 są
równe; w innym przypadku zwracana jest wartość pierwszego wyrażenia.
Załóżmy, że istnieje tabela podobna do tej z poprzedniego przykładu tabela tblWages, która
zawiera dane z poprzedniego przykładu, z wyjątkiem tego, że wartości NULL są w tym przypadku
zachowane jako zera. Można uruchomić następujący kod i ustawić wartości na 0 zamiast NULL:
USE pubs
GO
UPDATE tblWages
SET curHourly = ISNULL(curHourly, 0),
curSalary = ISNULL(curSalary, 0),
curCommission = ISNULL(curCommission, 0)
bignumSales = ISNULL(bigNumSales, 0)
Aby używać polecenia NULLIF, potrzeba następującego scenariusza. Jeżeli potrzeba napisać
polecenie SELECT które zwraca jak wielu pracowników otrzymuje płacę godzinną, ilu otrzymuje
pensję miesięczną a ile zarabia na prowizji, następujący kod nie będzie działał poprawnie:
SELECT Hourly = COUNT(curHourly),
Salary = COUNT(curSalary),
Commission = COUNT(curCommission)
FROM tblWages
Funkcja COUNT zlicza ile wartości pojawiło się w kolumnie, przy czym zera są zliczane jako
wartości. Jedyna rzecz nie brana pod uwagę przez funkcję COUNT to wartość NULL. Dlatego,
można użyć funkcji NULLIF do zmiany wartości 0 w NULL i pozostawienie bez zmian
pozostałych (niezerowych) wartości. Aby otrzymać prawidłowe wyniki z poprzedniego przykładu
należy uruchomić kod:
SELECT Hourly = COUNT(NULLIF(curHourly,0)),
Salary = COUNT(NULLIF(curSalary,0)),
Commission = COUNT(NULLIF(curCommission,0))
FROM tblWages
Polecenie WHILE
Język T-SQL posiada konstrukcję WHILE, która pozwala na wykonywanie powtarzane, aż do
momentu, gdy jest spełniony odpowiedni warunek. Używanie tej konstrukcji jest ograniczone w
wielu przypadkach, ponieważ ze swojej natury, SQL Server działa na zbiorach wierszy.
Przykładowo, nie ma potrzeby używania polecenia WHILE, aby przejrzeć wszystkie wiersze w
tabeli; polecenie SELECT pozwala przeglądnąć wszystkie wiersze, sprawdzając dla każdego
wiersza kryteria zawarte w poleceniu WHERE.
W niektórych sytuacjach potrzebna jest możliwość powtórzenia akcji. Można powtarzać działanie
wewnątrz transakcji (nie polecane, ponieważ transakcje powinny być małe i szybkie), w
procedurze składowanej lub w funkcji SQL. Polecenia WHILE mogą być użyteczne przy obsłudze
kursorów.
Konstrukcja WHILE jest powtarzana, do czasu, aż podany warunek będzie prawdziwy. Podobnie
jak w przypadku IF...ELSE, jeżeli warunek zawiera polecenie SELECT, polecenie to w całości
musi być ujęte w nawiasy zwykłe.
Następujący przykład wielokrotnie sprawdza średnią cenę wszystkich książek. Dopóki średnia jest
mniejsza niż $20, cena każdej z książek jest uaktualniana. Powtarzanie zostaje przerwane gdy
średnia cena jest większa lub równa $20.
WHILE (SELECT AVG(price) FROM titles) < $20
BEGIN
UPDATE titles SET price = price * 1.1
PRINT 'all prices have been increased by 10%'
END
Jak w przypadku każdego języka programowania, istnieje zawsze ryzyko, że powtarzanie
będzie trwało w nieskończoność. Sprawą programisty jest dopilnowanie, żeby tak się nie stało.
W tym przykładzie, powtarzanie trwa dopóki cena jest mniejsza od określonej kwoty, dlatego
wewnątrz pętli potrzebna jest akcja powodująca podniesienie ceny; ostatecznie, cena nie
będzie mniejsza niż określona kwota. (Nie będzie to prawdziwe jeżeli wszystkie ceny będą
miały wartość $0.00). Jeżeli polecenie UPDATE obniżało by cenę książek (price = price *
0.90), powstałaby nieskończona pętla.
Blokowanie
Blokowanie występuje na ogół w negatywnym kontekście. Blokowanie pomaga utrzymać
współbieżność pomiędzy bazami. Często słyszy się, że ktoś wspomina o problemach z
blokowaniem, ale rzadko słyszy się o pozytywnych aspektach blokowania; jednak, jest ich wiele.
Bez blokowania, SQL Server nie posiadałby mechanizmu do zabronienia wielu użytkownikom
modyfikacji określonych danych w tym samym czasie.
Zasadniczo, w SQL Serverze używane są cztery typy blokad:
" Można założyć blokadę współdzieloną (shared lock) zwaną też read lock, na dane,
które są czytane. Współdzielona blokada nie pozwala innym użytkownikom na zmianę
danych, gdy dane są przeglądane. Blokady współdzielone są zgodne z innymi blokadami
tego typu.
" Można używać blokady wyłącznej (exclusive lock) gdy dane są modyfikowane. Inni
użytkownicy nie mogą w tym czasie przeglądać ani modyfikować danych, aż do
momentu zwolnienia blokady. Blokady wyłączne nie są zgodne z innymi blokadami.
" Blokada update (update lock)jest używana podobnie jak blokada wyłączna. Blokada
update nie pozwala innym na modyfikację danych, podczas gdy użytkownik wykonuje
proces zmiany danych.
" Blokada intent (intent lock)używana jest na obiektach wyższego poziomu do
określenia, że blokada (opisanego wcześniej typu) została założona na tym obiekcie.
Zagadnienie to zostanie krótko omówione.
Blokady update są potrzebne gdy zapytanie przechodzi przez dwie fazy aby zmodyfikować
dane: fazę wyszukiwania i fazę modyfikacji. Jeżeli SQL Server używa blokady współdzielonej
podczas fazy wyszukiwania, inny użytkownik może również założyć blokadę dzieloną na tym
samym obiekcie. Kiedy transakcja wyszukująca próbuje modyfikować dane, wymaga blokady
wyłącznej. Inna transakcja mogła już próbować założyć blokadę wyłączną i SQL Server nie
przydzieli po raz drugi tej blokady. Dlatego mogą wystąpić sytuacje zablokowania lub
zakleszczenia. Aby uniknąć tych sytuacji, należy stosować blokadę update, która chroni przed
użyciem blokady wyłącznej przez inne transakcje, na obiektach, które zostały zablokowane do
edycji.
Typy blokad
Różne poziomy lub typy obiektów mogą być blokowane:
" RID jest innym określeniem dla blokady z poziomu wiersza. RID oznacz Row Identifier. Gdy
założona jest blokada RID, w danym czasie blokowany jest jedynie jeden wiersz.
" Key jest blokadą na poziomie wiersza która jest zakładana w indeksie. Blokada key blokuje
wartość pojedynczego klucza lub wiele wartości klucza (zwana key range lock). Ten typ
blokady może pomóc szeregować transakcje w indeksie.
" Page jest standardową jednostką 8KB w SQL Serverze. Blokada strony blokuje całą
zawartość pojedynczej strony, która może być jednym lub wieloma wierszami.
" Blokada wyłączna jest wymagana, gdy dla danego obiektu nie ma więcej dostępnych stron a
ma być dodanych więcej danych. Oznacza ona, że potrzeba nowego zestawu ośmiu stron
(extent) dla tego obiektu.
" Można założyć blokadę tabeli automatycznie lub poprzez proces eskalacji (omówiony
pózniej), lub może zażądać blokady bezpośrednio. Wszystkie strony w tabeli są blokowane
jako jednostka.
" Blokada typu intent jest sposobem określenia na poziomie tabeli, że są założone blokady
strony lub wiersza lub na poziomie strony, że ma miejsce blokada wiersza. Przykładowo,
jeżeli w tabeli występuje pojedyncza blokada współdzielona strony, na poziomie tabeli jest
zakładana współdzielona blokada intent.
W SQL Serverze 2000, blokowanie jest całkowicie dynamiczne. W wersjach SQL Servera przed
wersją 7.0, domyślne było blokowanie z poziomu strony. Jednak SQL Server 2000 decyduje, który
typ blokady założyć przy optymalizacji zapytań. Dla zapytań, które dotyczą niewielkiej ilości
danych lub niewielkich dodawań danych, uaktualnień lub usuwania, zakładane są blokady na
poziomie wiersza (i/lub o zasięgu klucza). Dla każdego większego zapytania (takiego jak SELECT
* FROM tblLargeTable) używanie blokady na poziomie strony lub nawet tabeli może być
bardziej efektywne.
Blokowanie na poziomie wiersza uwzględnia blokowanie pojedynczego wiersza w danym
momencie zamiast blokowania strony lub tabeli. Blokowanie na poziomie wiersza może być
użyteczne ponieważ pojedyncza strona może zawierać wiele wierszy. Generalnie mówiąc, im
mniejsza jednostka do blokowania, tym lepsza współbieżność (zdolność aby wielu użytkowników
miało równocześnie dostęp do danych). Kompromisem jest jednak to, że założenie blokad na 5000
wierszy wymaga więcej zasobów, i co za tym idzie więcej czasu, niż założenie blokady na
pojedynczej tabeli. Ponieważ blokowanie zakłada pewne kompromisy i nie jest zawsze oczywiste,
który typ blokady założyć, lepiej jest zostawić decyzje o wyborze blokady SQL Serverowi.
Kontrola blokowania
Normalnie, nie ma potrzeby rozważać kontrolowania blokad. Dla operacji INSERT, UPDATE i
DELETE SQL Server zakłada blokadę wyłączną. Jednak, SQL Server może konfigurować
blokady zapytanie po zapytaniu przy pomocy podpowiedzi blokowania dla poleceń SELECT.
Można je określić po nazwie tabeli w zapytaniu. Okazjonalnie, można zmienić domyśle
zachowanie blokad z powodu problemów z konfliktami transakcji lub blokowaniu ich nawzajem.
Przykładowo, aby wymusić wyłączną blokadę tabeli authors, aby nikt nie mógł modyfikować w
tym czasie danych, należy uruchomić zapytanie:
SELECT *
FROM authors (TABLOCKX)
W miejsce TABLOCKX można używać następujących parametrów:
" NOLOCK żąda, aby nie były stosowane żadne blokady. (Ten typ odczytu jest również
nazywany dirty read). Używanie tej podpowiedzi dla optymalizatora pozwala aby zapytanie
czytało dane, które zostały zablokowane do wyłącznego użytku. Używanie tego wprowadza
możliwość, że dane, które zostały zmienione, ale nie koniecznie zatwierdzone, mogą być
czytane jako część zapytania. Chociaż opcja ta jest pożyteczna w niektórych przypadkach, nie
należy jej używać, dopóki nie zrozumie się w pełni zachodzących powiązań.
" READUNCOMMITTED jest tym samym co NOLOCK.
" READPAST określa, że jeśli niektóre wiersze są zablokowane, a czytane jest kilka wierszy,
włączając w to te, które normalnie nie pozwoliłyby na kontynuację (działanie zostałoby
zablokowane do czasu zwolnienia blokady), wiersze te są opuszczane. Należy ostrożnie
używać tej opcji ponieważ może brakować danych w zbiorze wynikowym.
" REPEATABLEREAD określa, że blokowanie powinna mieć zastosowanie do poziomu izolacji
transakcji REPEATABLE READ. Oznacza to zasadniczo, że podczas czytania danych,
założone blokady nie są zdejmowane do czasu zakończenia transakcji; w związku z tym,
żaden z wierszy danych, które zostały przeczytane nie może być modyfikowany przez inne
transakcje dopóki transakcja nie zakończy się. Dlatego, uaktualnienia i usuwanie danych nie
są możliwe, ale dodawanie danych jest dozwolone.
" HOLDLOCK żąda, aby założona blokada istniała do zakończenia transakcji. Normalnie,
podczas wykonywania polecenia select, blokady współdzielone są zakładane i zwalniane do
czasu gdy następny wymagany wiersz lub strona jest pobierana. Z tą opcją, współdzielone
blokady nie są zwalniane do czasu zatwierdzenia lub wycofania transakcji. Blokady są
również zakładane w ten sposób, że dodawanie danych (insert) również nie jest możliwe.
Zasadniczo zachowanie to jest implementowane z blokadami o zasięgu klucza.
" SERIALIZABLE jest tym samym co HOLDLOCK.
" UPLOCK wymaga raczej blokady update niż blokady współdzielonej. Opcja ta nie jest
normalnie używana.
" ROWLOCK wymusza użycie blokad na poziomie wiersza
" PAGELOCK wymaga blokady współdzielonej strony.
" TABLOCK wymaga blokady współdzielonej na poziomie tabeli raczej niż blokowania
indywidualnych stron lub wierszy
" TABLOCKX wymaga wyłącznej blokady tabeli.
Eskalacja blokad
Eskalacja blokad jest procesem zmiany blokady z niższego poziomu (jak np.: blokada na poziomie
wiersza lub strony) na blokadę wyższego poziomu (blokada na poziomie tabeli). SQL Server
eskaluje blokady gdy jest zmuszony to zrobić. Użytkownik nie ma kontroli nad tym kiedy
występuje ten proces.
Opcja LOCKS
Dla blokad dostępna jest jeszcze jedna opcja konfiguracyjna opcja LOCKS. Jest to opcja
zaawansowana. Domyślna wartość 0 oznacza, że SQL Server dopasowuje liczbę blokad, gdy
zmienią się wymagania systemu. Domyślnie, SQL Server przypisuje 2 procent pamięci do
używania przez blokady. Można również w razie potrzeby ustawić ręcznie liczbę blokad; jednak,
jeżeli zabraknie dostępnych blokad, SQL Server umieści komunikat o błędzie w Windows NT
Application Event Log jak i w dzienniku błędów SQL Servera. Wszelkie zdarzenia, które
wymagają blokad w systemie zostaną wstrzymane do czasu, aż będzie dostępnych więcej blokad.
Każda z blokad wymaga małej ilości pamięci, więc nie należy ustawiać blokad ręcznie ani
określać arbitralnie tego parametru jako dużej wartości dopóki nie zrozumie się dobrze wpływu tej
zmiany na działanie systemu.
Obserwacja blokad
W celu obserwacji blokad należy uruchomić procedury sp_lock lub sp_processinfo.
Następujący przykład pokazuje działanie procedury sp_lock w systemie:
EXEC sp_lock
GO
spid dbid ObjId IndId Type Resource Mode Status
------ ------ ------ ----- ------ -------- ------ -------
1 1 0 0 DB S GRANT
1 1 0 0 DB S GRANT
1 1 0 0 DB S GRANT
1 1 0 0 DB S GRANT
1 1 0 0 DB S GRANT
1 1 0 0 EXT 1:80 X GRANT
1 1 117575457 0 TAB IS GRANT
1 1 0 0 DB S GRANT
(8 row(s) affected)
Jak można zauważyć w wynikach zapytania sp_lock, interpretacja wyników może być trudna.
Potrzeba interpretować wyniki przeglądając dbid (identyfikator ID bazy danych) z tabeli
sysdatabases w bazie danych master oraz ObjId (ID obiektu) z tabeli sysobjects w
odpowiedniej bazie danych. Być może będzie potrzebne przeglądanie IndId (ID indeksu) z tabeli
sysindexes w danej bazie. Kolumna type wyjaśnia jaki rodzaj obiektu został zablokowany
baza danych, tabela, extent, klucz, strona czy wiersz (RID). Kolumna Resource informuje co
zostało zablokowane w zależności od typu blokady:
" EXT (extent) podany jest w formie fileno:Extent. Przykładowo, 1:80 oznacza
blokadę zakresu na pliku numer 1 w bazie danych , w zakresie 80.
" PAG (page) podany jest w formie fileno:pageno. Przykładowo, 1:2401 oznacza
blokadę strony na stronie numer 2401 w pliku numer 1 bazy danych.
" RID (row) podany jest w formie fileno:pageno:rowid. Np.: 1:32:16 oznacza plik
numer 1, strona 32, wiersz 16 jest zablokowany.
" KEY (key range) pokazuje wewnętrzną informacje z SQL Servera, której nie mogą
interpretować użytkownicy.
Kolumna Mode oznacza typ blokady ( S blokada współdzielona, X wyłączna, I intent itd.).
Najbardziej istotna jest informacja z kolumny Status. GRANT oznacza, że blokada jest właśnie
założona. WAIT oznacza, że blokada nie może być założona ponieważ inny użytkownik założył nie
kompatybilną blokadę (zgodność blokad zostanie omówiona w kolejnej sekcji). CNVT pokazuje, że
blokada jest konwertowana na inną blokadę (w większości przypadków, jest eskalowana). Z
perspektywy blokad można traktować CNVT podobnie jak status WAIT.
Używanie okna SQL Server Enterprise Manager Current Activity może być łatwiejsze. Aby
otworzyć te okna, należy rozwinąć nazwę serwera, następnie folder Management i opcję Current
Activity. Przy pomocy tej opcji można zobaczyć, które procesy (użytkownicy) są aktywne, jakie
blokady zostały założone przez procesy i jakie typy blokad są założone przez obiekty. Rysunek
12.4 pokazuje przykładowe okno.
Rysunek 12.4.
Okno
Locks/Process ID
Current Activity.
Po rozwinięciu opcji Locks/Object, można obserwować w miejsce obiektu blokady (zobacz
rysunek 12.5).
Rysunek 12.5.
Okno
Locks/Object
Current Activity.
Zgodność blokad
Blokady mogą być zgodne z innymi blokadami lub nie. Tabela 12.1 pokazuje macierz zgodności
blokad.
Tabela 12.1 Zgodność blokad
IS S U IX SIX X
Intent Shared (IS) YES YES YES YES YES NO
Shared (S) YES YES YES NO NO NO
Update (U) YES YES NO NO NO NO
Intent Exclusive (IX) YES NO NO YES NO NO
Shared With Intent YES NO NO NO NO NO
Exclusive (SIX)
Exclusive (X) NO NO NO NO NO NO
Warto zauważyć, że blokady wyłączne (exclusive) nie są kompatybilne z żadnymi blokadami. Po
przemyśleniu wydaje się to sensowne. Jeżeli dane są zmieniane, nikt inny nie powinien w tym
samym czasie zmieniać tych danych. Z drugiej strony blokady współdzielone są elastyczne z
uzasadnionych przyczyn. Jeżeli użytkownik czyta dane, nie ma problemu, żeby te same dane
czytał w tym samym czasie inny użytkownik.
Zakleszczenia (deadlocks)
Zakleszczenie jest sytuacją, w której dwie transakcje popadają ze sobą w konflikt i jedynym
wyjściem z tej sytuacji jest anulowanie jednej z transakcji. Najlepszym sposobem zrozumienia
tego problemu jest analiza przykładu:
1. Należy utworzyć dwie tabele przy pomocy Query Analyzera i wypełnić je danymi:
USE pubs
GO
CREATE TABLE tblChecking
(intAcctNum INT NOT NULL,
strLastName CHAR(30) NOT NULL,
curBalance MONEY NOT NULL
)
GO
CREATE TABLE tblSavings
(intAcctNum INT NOT NULL,
strLastName CHAR(30) NOT NULL,
curBalance MONEY NOT NULL
)
GO
INSERT tblChecking VALUES (1,'smith', $500.00)
INSERT tblChecking VALUES (2,'Jones', $300.00)
INSERT tblSavings VALUES (1,'smith', $100.00)
INSERT tblSavings VALUES (2,'Jones', $200.00)
GO
2. Należy otworzyć drugie okno Query Analyzera (z menu plik wybrać Connect). W pierwszym
oknie uruchomić kod:
use pubs
GO
BEGIN TRAN
UPDATE tblChecking
SET curBalance = curBalance + $100.00
WHERE intAcctNum = 1
Odpowiedz systemu powinna być następująca:
(1 row(s) affected)
3. W drugim oknie należy uruchomić kod:
USE pubs
GO
BEGIN TRAN
UPDATE tblSavings
SET curBalance = curBalance - $100.00
WHERE intAcctNum = 2
z tym samym wynikiem.
4. Następnie, w pierwszym oknie należy uruchomić:
UPDATE tblSavings
SET curBalance = curBalance - $100.00
WHERE intAcctNum = 1
Warto zauważyć, że transakcja nadal jest wykonywana, podczas gdy inne okno ją blokuje.
Uruchamiając w trzecim oknie procedurę sp_lock można przeglądnąć następujące wyniki:
spid dbid ObjId IndId Type Resource Mode Status
------ ------ ----------- ------ ---- ---------------- -------- ------
51 5 0 0 DB S GRANT
51 5 773577794 0 TAB IX GRANT
51 5 773577794 0 RID 1:141:0 X GRANT
51 5 789577851 0 RID 1:143:1 U WAIT
51 5 789577851 0 RID 1:143:0 X GRANT
51 5 789577851 0 PAG 1:143 IX GRANT
51 5 773577794 0 PAG 1:141 IX GRANT
51 5 789577851 0 TAB IX GRANT
54 5 789577851 0 PAG 1:143 IX GRANT
54 5 789577851 0 TAB IX GRANT
54 5 789577851 0 RID 1:143:1 X GRANT
54 5 0 0 DB S GRANT
55 1 85575343 0 TAB IS GRANT
Warto zauważyć, że jeden z procesów spid w kolumnie status ma słowo WAIT, oznaczające, że
proces czeka aby założyć blokadę i nie może ruszyć, dopóki inny proces (spid) nie zwolni swojej
blokady w wymaganych zasobach.
5. W drugim oknie należy uruchomić kod:
UPDATE tblChecking
SET curBalance = curBalance + $100.00
WHERE intAcctNum = 2
Powinien się pojawić komunikat tego rodzaju:
Server: Msg 1205, Level 13, State 50, Line 1
Your transaction (Proced ID 54) was deadlocked on (lock) resources with
anther process and has been chosen as the deadlock victim. Rerun your
transaction.
6. Teraz wykonane jest oryginalne zapytanie. Należy uruchomić następujący czyszczący kod
w pierwszym oknie:
COMMIT TRAN
GO
DROP TABLE tblChecking
DROP TABLE tblSavings
GO
Unikanie zakleszczeń jest bardzo istotne ponieważ gdy pojawią się zakleszczenia marnowane są
czas i zasoby. Jedynym sposobem uniknięcia zakleszczeń jest dostęp do tabel zawsze w tej samej
kolejności. W poprzednim przykładzie, jeżeli obydwie transakcje rozpoczęłyby od tej samej tabeli,
zakleszczenie by nie wystąpiło. Jedna transakcja czekała na kolejną aby zakończyć przed jej
rozpoczęciem. Jeżeli powstanie zakleszczenie, dobrze jest poczekać sekundę lub dwie a następnie
powtórzyć transakcję.
Jednym z mitów jest to, że zakleszczenia nie powstaną gdy istnieje blokowanie na poziomie
wiersza. Jak można zauważyć z poprzedniego przykładu, zakleszczenia nie mają tak dużo
wspólnego z blokowaniem na poziomie wiersza, jak z tym, że do ich wystąpienia potrzeba
nieprawidłowych blokad w złym czasie. Ważne jest aby doprowadzić poprzez testy do
wyeliminowania z aplikacji tylu zakleszczeń ile jest możliwe.
Poziomy izolacji transakcji
Poziomy izolacji transakcji mają wpływ na domyślne rodzaje i długość trwania blokad założonych
podczas polecenia SELECT. Jak zostało wcześniej powiedziane, typy założonych blokad mogą
być omijane zapytanie po zapytaniu. Poziom izolacji działa podobnie ale może być ominięty na
poziomie sesji (oznacza, że wszystkie zapytania, które są uruchamiane podczas pojedynczej sesji
mają ustawioną tę opcję). Aby to zrobić, należy skorzystać z polecenia SET TRANSACTION
ISOLATION LEVEL:
SET TRANSACTION ISOLATION LEVEL
{READ COMMITTED | READ UNCOMMITTED | REPEATABLE READ | SERIALIZABLE}
" W przypadku opcji READ COMMITTED, która jest wartością domyślną, zapytanie (select)
widzi jedynie dane, na których zapytanie może założyć blokadę współdzieloną (nie wykonuje
dirty reads).
" READ UNCOMMITTED, która jest taka sama jak podpowiedz dla optymalizatora NOLOCK,
pozwala na dirty reads we wszystkich zapytaniach w danej sesji. Należy być ostrożnym z tą
opcją ponieważ można zobaczyć dane, które nie zostały jeszcze zatwierdzone w bazie danych.
" REPEATABLE READ nie zwalnia blokad współdzielonych dopóki transakcja nie została
ukończona i jest odpowiednikiem podpowiedzi HOLDLOCK opisanego wcześniej. Nie
pozwala na modyfikacje i usuwanie danych, które są odczytywane.
" SERIALIZABLE nie tylko nie pozwala na modyfikacje i usuwanie danych, które są
odczytywane, ale również nie zezwala na dodawanie danych z określonego zakresie klucza,
które są czytane.
Obydwie opcja REPEATABLE READ i SERIALIZABLE mają wyrazny wpływ na współbieżność,
dlatego należy dobrze rozumieć konsekwencje używania każdej z opcji zanim się je
zaimplementuje.
Aby sprawdzić jaki poziom izolacji jest wymuszony, należy użyć polecenia DBCC USEROPTIONS.
Bez uruchomienia polecenia SET TRANSACTION ISOLATION LEVEL, pojawią się wyniki:
DBCC USEROPTIONS
GO
Set Option Value
------------------------------ ------------------
textsize 64512
language us_english
dateformat mdy
datefirst 7
ansi_null_dflt_on SET
(5 row(s) affected)
DBCC execution completed. If DBCC printed error messages, contact your
system administrator.
Widać różnicę po włączeniu dirty reads z opcją READ UNCOMMITTED:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
GO
DBCC USEROPTIONS
GO
Set Option Value
---------------------------- ------------------
textsize 64512
language us_english
dateformat mdy
datefirst 7
ansi_null_dflt_on SET
isolation level read uncommitted
(6 row(s) affected)
DBCC execution completed. If DBCC printed error messages, contact your
system administrator.
Wyszukiwarka
Podobne podstrony:
R12 05Wykład 05 Opadanie i fluidyzacjaPrezentacja MG 05 20122011 05 P05 2ei 05 08 s029r12ei 05 s05205 RU 486 pigulka aborcyjnawięcej podobnych podstron