Не використовуйте транзакцію для збереженої процедури


18

У мене збережена процедура, яка виконує кілька команд. Я не хочу, щоб ці команди були зафіксовані в транзакції збереженої процедури. Якщо 4-а команда не вдається, я хочу, щоб 1-я, 2-а та 3-я команди залишилися, а не відкатувались.

Чи можна записати збережену процедуру таким чином, щоб вона не виконувалась як одна велика транзакція?

Відповіді:


16

Усі транзакції не виконуються в одній транзакції. Погляньте на цей приклад:

use TestDB;
go

if exists (select 1 from sys.tables where object_id = object_id('dbo.TestTranTable1'))
    drop table dbo.TestTranTable1;
create table dbo.TestTranTable1
(
    id int identity(1, 1) not null,
    some_int int not null
        default 1
);
go

insert into dbo.TestTranTable1
default values;
go 4

select *
from dbo.TestTranTable1;

if exists (select 1 from sys.sql_modules where object_id = object_id('dbo.ChangeValues'))
begin
    drop proc dbo.ChangeValues;
end
go
create proc dbo.ChangeValues
as
    update dbo.TestTranTable1
    set some_int = 11
    where id = 1;

    update dbo.TestTranTable1
    set some_int = 12
    where id = 2;

    update dbo.TestTranTable1
    set some_int = 13
    where id = 3;

    -- this will error out (arithmetic overflow)
    update dbo.TestTranTable1
    set some_int = 2147483648
    where id = 4;
go

exec dbo.ChangeValues;

select *
from dbo.TestTranTable1;

Ось вихід:

введіть тут опис зображення

Створюючи сеанс розширених подій для моніторингу sql_transactionподії, ось висновок від виконання dbo.ChangeValues:

введіть тут опис зображення

Як ви бачите на цьому скріншоті, для кожного з чотирьох виписок є окремі транзакції. Перші 3 фіксують, а останній відкочується через помилку.


16

Я думаю, що тут може виникнути деяка плутанина щодо партії проти транзакції .

Угода є заява або набір операторів , які будуть або успіху або невдачі , як єдине ціле. Усі висловлювання DDL є самими транзакціями (тобто, якщо ви оновлюєте 100 рядків, але рядок 98 видає помилку, жодна з рядків не оновлюється). Ви можете обернути серію виписок у транзакції, використовуючи, BEGIN TRANSACTIONа потім COMMITабо ROLLBACK.

Партія є послідовність операторів, які виконуються разом. Збережена процедура - приклад партії. У збереженій процедурі, якщо одне твердження виходить з ладу і виникає помилка (зазвичай TRY/CATCHблоки), наступні оператори не виконуються.

Я підозрюю, що ваша проблема полягає в тому, що партія скасовується, коли виникає помилка, тому що або в самому збереженому протоколі, або в зовнішній області (наприклад, додаток або збережений додаток, який викликає цю процедуру) є помилка в ньому. Якщо це так, це вирішити складніше, оскільки вам потрібно відрегулювати, як ви обробляєте помилки, незалежно від того, яка сфера їх захоплює.


Я не знайшов жодної статті, в якій сказано: "Процедура магазину - приклад партії". Я вважаю, що зберігається процедура дуже схожа на пакетну, але це не пакетна. Основна відмінність полягає в тому, що SP гарантовано буде складено заздалегідь і готове до виконання кілька разів на відміну від Пакетів. Схожість: - Вони обидва виконують кожну команду одночасно. - Якщо одна команда не вдалася, всі попередні команди виконуються (якщо вона не виконується в транзакції) - якщо одна команда не вдалася, то всі наступні команди не виконуються.
Аші

6

Все в sql сервері міститься в транзакції.

Коли ви чітко вказуєте begin transactionі end transactionтоді він називається явною транзакцією . Якщо ви цього не зробите, то це - неявна угода .

Щоб переключитися, в якому режимі ви перебуваєте, ви використовуєте

set implicit_transactions on

або

set implicit_transactions off

select @@OPTIONS & 2

якщо вище повертає 2, ви перебуваєте в неявному режимі транзакцій. Якщо він поверне 0, ви перебуваєте в автокомісії.

Угода є ВСЕ або нічого, щоб підтримувати базу даних у послідовному стані .. пам'ятайте властивості ACID.

CREATE TABLE [dbo].[Products](
    [ProductID] [int] NOT NULL,
    [ProductName] [varchar](25) NULL,
    [DatabaseName] [sysname] NOT NULL,
 CONSTRAINT [pk_Product_ID_ServerName] PRIMARY KEY CLUSTERED 
(
    [ProductID] ASC,
    [DatabaseName] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO


-- insert some data 
INSERT INTO [dbo].[Products]([ProductID], [ProductName], [DatabaseName])
SELECT 1, N'repl1_product1', N'repl1' UNION ALL
SELECT 1, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 1, N'repl3_product1_03', N'repl3' UNION ALL
SELECT 2, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 2, N'repl2_product1', N'repl2' UNION ALL
SELECT 2, N'repl3_product1_03', N'repl3' UNION ALL
SELECT 3, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 3, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 3, N'repl3_product1', N'repl3' UNION ALL
SELECT 4, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 4, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 5, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 5, N'repl2_product1_02', N'repl2'

- створіть SP зараз - зауважте, що перші 3 будуть успішними, а 4 - невданими через обрізку рядків ...

IF OBJECT_ID ('usp_UpdateProducts', 'P') IS NOT NULL
    DROP PROCEDURE usp_UpdateProducts;
GO
create procedure usp_UpdateProducts
as 
begin try
update Products 
set ProductName = 'repl1_product1'
where DatabaseName = 'repl1'and ProductID = 1;
update Products
set ProductName = 'repl2_product1'
where DatabaseName = 'repl2' and ProductID = 2;
update Products
set ProductName = 'repl3_product1'
where DatabaseName = 'repl3' and ProductID = 3;
update Products
set ProductName = 'repl3_product1_03&&&&&&&&&&39399338492w9924389234923482' -- this will fail ...
where DatabaseName = 'repl3' and ProductID = 4;
SELECT 1/0;
end try
begin catch
SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() as ErrorState,
        ERROR_PROCEDURE() as ErrorProcedure,
        ERROR_LINE() as ErrorLine,
        ERROR_MESSAGE() as ErrorMessage;
end catch
go

Зверніться до: Чи погана практика завжди створювати транзакцію?


3

Ось як за замовчуванням працюють збережені процедури. Збережена процедура не завершується автоматично в межах транзакції.

Якщо ви хочете, щоб збережена процедура зупинялася, коли вона потрапляє на першу помилку, ви захочете покласти туди вхід TRY / CATCH, щоб повернутись у разі проблеми з командою 2, наприклад.


2

Вам знадобляться індивідуальні транзакції для кожної команди. Ви також можете зробити це за допомогою збережених транзакцій:

Дивіться SAVE TRANSACTION (Transact-SQL)в документації на виріб.

Я хочу кваліфікувати, що окремі транзакції - це поведінка за замовчуванням для збережених процедур, оскільки всі висловлювання загорнуті в неявні транзакції; однак ніхто не повинен покладатися на неявні транзакції, щоб контролювати долю свого коду. Набагато краща практика - чітко контролювати спосіб обробки операцій у виробничому коді.


-2

кожну частину відокремте BEGIN TRAN і перевірте, чи транзакція пройшла успішно. якщо це було зроблено, інакше зробіть відкат, оскільки всі вони виконуються з одного рівня, ви зможете зробити кожен розділ окремо, не потребуючи відкату всіх, якщо один не вдасться.

Для отримання додаткової інформації ви можете подивитися: http://msdn.microsoft.com/en-us/library/ms188929.aspx


1
Чи створить це суб-транзакції в рамках моєї збереженої процедури? В ідеалі я хотів би уникнути цього, якщо можливо
Метью Стіплз,

1
Якщо ІП викликається всередині транзакції, відповідь збережених транзакцій вище. Якщо sp не викликається, то @mrdenny правильний. Сервер Sql не підтримує вкладені транзакції.
StrayCatDBA

@StrayCatDBA просто для уточнення .. В SQL Server є вкладені транзакції, але вони є злими .. SQL Server дозволяє починати транзакції всередині інших транзакцій - так званих вкладених транзакцій. Зверніться до sqlskills.com/blogs/paul/… , msdn.microsoft.com/en-us/library/ms189336(v=sql.105).aspx та sqlblog.com/blogs/kalen_delaney/archive/2007/08/13 /…
Кін Шах

2
Щоб було зрозуміло (і для ледачих, які не хочуть натискати на посилання), ви насправді не починаєте іншу транзакцію. Aka заголовок у публікації Павла: "Міф: Вкладені транзакції справжні". Вони не є реальними операціями. COMMIT у вкладеній транзакції не робить нічого, окрім декременту @@ TRANCOUNT. Це правда, що ви не отримаєте помилку, якщо вкладете BEGIN TRAN / COMMIT, але це відрізняється від реальних вкладених переходів.
StrayCatDBA
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.