tsql
TRANSCRIPT
eleks.com eleks.com
Розширення SQLTransact SQL
• Програмні конструкції T-SQL• Процедури та функції• Обробка помилок• Курсори, тимчасові таблички• Динамічний SQL• Транзакції• Тригери
Програмні конструкції T-SQL•Блок-оператор BEGIN {множина операторів} END•Умовний оператор IF {логічний вираз} {1 оператор}[ELSE {1
оператор}]
•Оператор циклу WHILE {логічний вираз} {1 оператор [BREAK][CONTINUE]}
•Оператор зупинки RETURN [ціле число] – повертає стан виконання блоку (0 - success)
•Оператор переходу GOTO мітка – в якому сама мітка має вигляд point:
•Конструкція виконання BEGIN TRY {1 оператор} END TRY
BEGIN CATCH {1 оператор} END CATCH
•Оператор затримки WAITFOR{DELAY[проміжок часу]|TIME[значення типу datetime][,TIMEOUT затримка]}
•Оператор виконання EXECUTE {}• Оголошення (команда DECLARE)
• Присвоєння значень оператор SELECT (можна кільком змінним відразу та використанням джерела даних) або оператор SET
• GO [кількість повторень] – інформує SQL сервер про закінчення пакету інструкцій
Програмні конструкції T-SQL
Процедура (Stored Procedure)Процедури – об’єкт БД, який дозволяє виконувати набір інструкцій
•Керуються операторами CREATE, ALTER та DROP•Допускають наявність параметрі•Можуть повертати значення різними способами:
-OUTPUT параметри-RETURN - коди (лише int значення)-Result Set кожного SELECT’у чи іншої процедури,
викликаної всередині-Через глобальний курсор, оголошений за межами
процедури
Приклад процедуриCREATE PROCEDURE RecalculateBoxOffice
@MovieId intASBEGIN
UPDATE MovieSET BoxOffice = (select sum(cs.profit) from CinemaShow cswhere ca.MovieId = MovieId)WHERE MovieId = @MovieId
ENDGO
Дану процедуру можна викликати наступним чином :exec RecalculateBoxOffice 1
Виклик процедури з іншого пакету:begindeclare @mId intset @mId = 2exec RecalculateBoxOffice @MovieId = @mIdselect BoxOffice from Moviewhere MovieId = @mIdendGO
Оголошення параметру із значенням по замовчуванню:ALTER PROCEDURE RecalculateBoxOffice @MovieId int = 0AS…
Приклад процедури
Перевірка параметрів:ALTER PROCEDURE RecalculateBoxOffice@MovieId int = 0,@UpdatedBoxOffice money OUTPUTASdeclare @err_code intBEGINdeclare @mId intset @mId = isnull( @MovieId, 0)if @mId = 0 -- check input parameterbeginraiserror('Parameter MovieId is empty!', 10, 1)set @err_code = 50001end
Обробка помилок
Перевірка наявності значення:-- check exists needed rowsif not exists(select 1 from Movie
where MovieId = @mId)beginraiserror('Parameter MovieId %d not found!', 10, 1, @mId)set @err_code = 50002goto SP_ENDend-- update tableUPDATE MovieSET BoxOffice = (select sum(cs.Profit) from CinemaShow cswhere cs.MovieId = MovieId)WHERE MovieId = @mId
Обробка помилок
Перевірка виконання оператора:select @row_code = @@ROWCOUNT, @err_code = @@ERRORif @err_code <> 0 -- check errorbeginraiserror(‘BoxOffice was not calculated for %d!',10,1,@mId)goto SP_ENDendif @row_code = 0 -- check update resultsbeginraiserror(‘Nor rows was updated for %d!',10,1,@ mId)set @err_code = 50003goto SP_ENDend
Обробка помилок
Використання конструкції TRY…CATCH :BEGIN TRY -- update table
UPDATE MovieSET BoxOffice = (select sum(cs.Profit)
from CinemaShow cswhere cs.MovieId = MovieId)
WHERE MovieId = @MovieId END TRYBEGIN CATCH -- check error
SELECT ERROR_NUMBER() ErrorNumber, ERROR_SEVERITY() ErrorSeverity, ERROR_STATE() ErrorState, ERROR_PROCEDURE() ErrorProcedure, ERROR_LINE() ErrorLine, ERROR_MESSAGE() ErrorMessage;END CATCH
Обробка помилок
Обробка помилокПовернення результату із процедури :
-- return new valueselect @UpdatedBoxOffice = BoxOffice from Moviewhere MovieId = @mIdreturn 0SP_END:return @err_codeENDGO
Виклик процедури:declare @ResultValue money = 0declare @ResultCode int = 0exec @ResultCode = RecalculateBoxOffice @MovieId = 1, @UpdatedBoxOffice = @ResultValue OUTPUT
select @Res, @Amm
Використання курсору
Обробка множини значень:declare @Amount intdeclare @Price moneyset @Amount = 0declare docDet cursor -- init cursor
FORWARD_ONLY FAST_FORWARD READ_ONLY LOCALfor select Amount, Price
from DocumentDetail where DocumentId = @DocumentIdopen documentDetail -- open cursor for usingfetch next from docDet into @Amount, @Price -- use cursorwhile @@FETCH_STATUS = 0begin
set @Amount = @Amount + @Amount * @Pricefetch next from docDet into @Amount, @Price
endclose docDet -- close cursordeallocate docDet-- free cursor
Використання курсоруТип доступу до курсору LOCAL | GLOBAL Варіанти руху по курсору FORWARD_ONLY | SCROLLСпосіб вичитування до даних
STATIC | KEYSET | DYNAMIC | FAST_FORWARD Варіанти блокування даних під час використання курсору
READ_ONLY | SCROLL_LOCKS | OPTIMISTIC Видавати попередження про неузгодженість типів у курсорі
TYPE_WARNINGЗміна значень в курсорі (або в його окремих колонках)
FOR UPDATE [ OF назви колонок]
Використання курсоруОператор отримання строки з курсору - FETCHFETCH FIRST – отримання першого рядкаFETCH LAST – отримання останнього рядкаFETCH ABSOLUTE – отримання рядка за його порядковим номеромFETCH RELATIVE – використовується для переміщення курсору вперед чи назад на задану кількість рядківFETCH PRIOR – отримання попереднього рядкаFETCH NEXT – отримання наступного рядка (за замовчуванням)
Для варіанту руху FORWARD_ONLY доступний лише FETCH NEXT
Використання тимчасових таблиць
Змінні табличного типу:declare @TempTab table (
Amount int,Price money)
-- insert datainsert into @TempTab (Amount, Price)
select Amount, Pricefrom DocumentDetail where DocumentId = @DocumentId
-- use tableselect @Amount = sum(Amount * Price)
from @TempTab
Використання тимчасових таблиць
Локальні тимчасові таблиці:create table #TempTab (Amountint,Price money)insert into #TempTab (Amount, Price)select Amount, Pricefrom DocumentDetailwhere DocumentId = @DocumentIdselect @Amount = sum(Amount * Price)from #TempTabdrop table #TempTab
Глобальні тимчасові таблиці (##) доступні всім сесіям та процесам
Динамічний SQL (Dynamic SQL) Безпосереднє виконання запитів із стрічки:
exec ('select Name from Client')declare @query varchar(50)set @query = 'select Name from Client ' +
' where ClientId = ' + cast(1 as varchar(2))exec (@query)
Використання процедури sp_executesql:DECLARE @intParameter INTDECLARE @query NVARCHAR(500) -- Build the SQL stringDECLARE @parameterDef NVARCHAR(100) -- Specify the parameter formatSET @query = N'select Name from Client where ClientId = @ClientId'SET @parameterDef = N'@ClientId int'SET @intParameter = 1; -- Execute with the first parameter valueEXECUTE sp_executesql @query, @parameterDef, @ClientId = @intParameterSET @IntParameter = 2; -- Execute the same with the second valueEXECUTE sp_executesql @query, @ParameterDeg, @ClientId = @intParameter
SQL-ін’єкції
ФункціїСкалярні функції, визначені користувачем:
CREATE FUNCTION GetCinemaProfitSum(@MovieId int = 0)RETURNS moneyASBEGINdeclare @result moneyselect @result = sum(Profit) from CinemaShow where MovieId = @MovieIdreturn @resultENDGO
Приклад використання:select dbo. GetCinemaProfitSum(1)
ФункціїПерелік дозволених операторів (виразів) у функціях:• Оператор присвоєння (SELECT, SET)• Всі оператори керування послідовністю дій (if, while, goto, …), окрім
TRY...CATCH виразу.• DECLARE - вирази для оголошення локальних змінних та курсорів• SELECT - вирази повинні повертати данні у змінні • Всі операції з локальними курсорами (OPEN, FETCH, CLOSE,
DEALLOCATE) повинні знаходитися всередині функції, також FETCH - вираз може бути використаний лише для присвоєння значення локальним змінним
• INSERT, UPDATE та DELETE повинні модифікувати лише локальні змінні типу table.
• Оператор EXECUTE викликає лише загальні процедури
ФункціїТабличні функції, визначені користувачем:
CREATE FUNCTION fnClientList(@c_Name varchar(20))RETURNS @resList table (cfName varchar(50))
ASBEGIN
declare @nInt intdeclare @cName varchar(50)set @nInt = 1while @nInt > 0 begin
select top 1 @cName = cName from tClients where cName like (@c_Name + '%')and cName not in (select cfName from @resList)
set @nInt = @@rowcountif @nInt > 0
insert into @resList(cfName) values (@cName)endreturn
ENDGO
Приклад використання:select * from dbo.fnClientList('CL')
Задані користувачем типи•User-defined Data Types•User-defined Table Types
Створення Table типу:create type EmailTable as table(
EmailId int null,EmailAddress nvarchar(256) not null,EmailTypeEnum int not null,IsPrimary bit not null
)
Використання параметром в процедурі:create procedure CreateContact
@FirstName nvarchar(64),@LastName nvarchar(64),@Emails EmailTable readonly,@Phones PhoneTable readonly
as begin--procedure body
end
ТранзакціїОкрема операція або набір операцій, які розглядаються як єдиний ланцюжок дійТранзакція має чотири спеціальні характеристики (властивості ACID):1. Неподільність (ATOMIC) – зв’язані операції можуть
виконуються або всі, або не виконується жодна2. Відповідність (CONSISTENCY) – по закінчені транзакції всі
данні повинні відповідати всім правилам, які накладаються на них в БД, без винятків
3. Ізоляція (ISOLATION) – одна транзакція не може накладатися на іншу (перетинатися з іншою)
4. Довговічність (DURABILITY) – після завершення транзакції всі внесені зміни повинні стати постійними
Транзакції
Чотири режими транзакцій:1. Autocommit (автоматична фіксація транзакцій) – використовується по
замовчуванню, ініціюються SQL-сервером для кожної команди зокрема2. Explicit (явно задані транзакції) – ініціюються та керуються користувачем за
допомогою наступних команд:• BEGIN {TRAN | TRANSACTION} [назва транзакції]• COMMIT {TRAN | TRANSACTION} [назва транзакції]• ROLLBACK {TRAN | TRANSACTION} [назва транзакції]• SAVE {TRAN | TRANSACTION} [назва точки збереження]3. Implicit (неявні транзакції) – режим автоматичної ініціалізації транзакції,
проте явного примусового завершення (регулюється перемикачем SET IMPLICIT_TRANSACTIONS { ON | OFF })
4. Batch-scored (транзакції для виконання пакетів) – специфічний режим для однієї сесії Multiple Active Result Sets (MARS)
Рівні ізольованості транзакцій:Проблеми паралельного виконання транзакцій:
•Втрачене обновлення (lost update)•«Брудне» читання (dirty read)•Неповторюване читання (non-repeatable read)•Фантомне читання (phantom reads)
Встановлення рівня ізоляції:SET TRANSACTION ISOLATION LEVEL [LEVELNAME]
Рівні ізольованості транзакцій:1.Read uncommitted2.Read committed (рівень за замовчуванням в MSSQL)3.Repeatable read4.Serializable
Поведінка при різних рівнях ізольованості:
Рівень ізоляції Втрачене оновлення
«Брудне» читання
Неповторюване читання
Фантомне читання
Read uncommitted + - - -
Read committed + + - -
Repeatable read + + + -
Serializable + + + +
ТранзакціїПриклад транзакції в процедурі:
CREATE PROCEDURE InsertDocumentDetails@DocId int, @ProductId int, @Amount int, @Price money, @DocDetailId int OUTPUTAS…--BEGIN TRYBEGIN TRAN T1INSERT INTO DocumentDetail(DocumentId,ProductId,Amount,Price)VALUES (@Docid,@ProductId,@Amount,@Price)set @DocDetailId = @@IDENTITYUPDATE DocumentSET Amount = Amount + @Amount * @Price
WHERE DocumentId = @DocIdCOMMIT TRAN T1END TRYBEGIN CATCHROLLBACK TRAN T1set @DocDetailId = 0END CATCH--
ТранзакціїОсобливості використання вкладених транзакційСистемна функція @@TRANSCOUNT (BEGIN TRANSACTION збільшує значення цієї змінної на 1, а оператор COMMIT TRANSACTION – зменшує 1)Оператор SAVE TRANSACTION (фіксує певні змін і дає можливість відкочувати стан даних саме до цієї точки)
Відображення впливу всіх операторів на @@TRANSCOUNT:Оператор Вплив
BEGIN TRANSACTION +1COMMIT TRANSACTION -1ROLLBACK TRANSACTION Встановить 0SAVE TRANSACTION “мітка” Без змінROLLBACK TRANSACTION “мітка” Без змін
ТранзакціїПриклад вкладених транзакцій #1:
CREATE PROCEDURE InsertDocumetDetailsTR@DocumentId int, @ProductId int, @Amount int, @Price money,@DocDetailId int OUTPUTASBEGINdeclare @Sum moneydeclare @Res intBEGIN TRYBEGIN TRAN T1 -- begin main tranINSERT INTO DocumentDetail (DocumentId, ProductId, Amount, Price)VALUES (@DocumentId,@ProductId,@Amount,@Price)set @DocumentDetailId = @@IDENTITYset @Sum = @Amount * @Price-- run other procedure
exec @Res = UpdateDocumentTR @DocumentId, @Sum
Приклад вкладених транзакцій #2:CREATE PROCEDURE UpdateDocumentTr
@DocumentId int, @Sum moneyASBEGIN
declare @tranCount int -- choose value before begin our transactionsselect @tranCount = @@TRANCOUNTBEGIN TRYif @tranCount = 0begin tran tr2 -- begin tran for separate modeelsesave tran tr2 -- start save for call modeupdate Documentset Amount = Amount + @Sumwhere DocumentId = @Docif @tranCount = 0 –– commit tran only for separate modecommit tran tr2return 0END TRYBEGIN CATCHrollback tran tr2 –– rollback new tran only return 50010 –– return error codeEND CATCH
END
Транзакції
ТригерТип процедур, який виконується автоматично як частина оператора по зміні даних. Trigger DML створюється для таблиці і зв’язується з однією або кількома подіями модифікації даних (INSERT, UPDATE, DELETE).Основними властивостями тригера можна назвати:
•Можливість порівняння попередньої та наступної версії даних (системні таблиці deleted та inserted)
•Здійснення при потребі відкоту недопустимих змін•Зчитування та зміна даних в інших таблицях•Виконання процедур та функцій
•Функція update() дозволяє перевірити зміну колонки таблички (задається як параметр)
ТригерПриклад тригера:
CREATE TRIGGER DocumentDetailSumON DocumentDetail AFTER INSERT,UPDATE,DELETEASdeclare @oldDocumentId int, @newDocumentId int, @DocumentId intdeclare @oldSum money, @newSum money, @Sum moneyBEGINif update(Price) or update(Amount)beginselect @oldDocumentId=DocumentId, @oldSum=Price*Amountfrom deletedselect @newDocumentId=DocumentId, @newSum=Price*Amountfrom insertedselect @DocumentId = isnull(@oldDocumentId,@newDocumentId),@Sum = isnull(@newSum,0) - isnull(@oldSum,0)exec UpdateDocument @DocumentId, @SumendEND
Any questions?