powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Как обработать исключение, возникшее в триггере
16 сообщений из 16, страница 1 из 1
Как обработать исключение, возникшее в триггере
    #32030655
Kosta
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Имеется хранимая процедура, в которой выполняется команда INSERT, запускающая триггер FOR INSERT, который в свою очередь выполняет команду UPDATE. Выглядит это вот так (MS SQL 2000):
----------------------------
CREATE PROCEDURE spAddPlace (@sName varchar(50), @nTopid int) AS
BEGIN
declare @ErrorStatus int
SET NOCOUNT ON
SET LOCK_TIMEOUT 5000

BEGIN TRANSACTION

INSERT INTO tbPlace ( pname, topid ) VALUES ( @sName, @nTopid )
select @ErrorStatus = @@ERROR

if @ErrorStatus = 0 COMMIT TRANSACTION
else ROLLBACK TRANSACTION

RETURN (@ErrorStatus)
END
GO
----------------------------
CREATE TRIGGER tgAddPlace ON [dbo].[tbPlace]
FOR INSERT
AS
set nocount on
UPDATE tbPlace
SET subcount = P.subcount + (select count(*) from inserted where toppid = P.pid)
FROM tbPlace P, inserted I
WHERE I.toppid is not null
AND P.pid = I.toppid
----------------------------
Вобщем, в табличке хранится древовидная структура, и триггер просто добавляет количество "детей" у "родителя".
Теперь берем и выполняем в Query Analyzer'е следующее:

DECLARE @RC int
EXEC @RC = spAddPlace 'test', 123
select @RC

При отсутствии ошибок все проходит, и на выходе получаем 0. Однако, если в момент добавления "родитель" кем-то блокирован, то по истечении 5 секунд получим сообщение об ошибке номер 1222 "Lock request time out period exceeded. The statement has been terminated" и выполнение просто прерывается. Хотя, по идее, код этой ошибки должен сохраняться в переменной @ErrorStatus, дабы быть потом возвращенным из хранимой процедуры пользователю. Однако, этого не происходит. Все строки кода в процедуре после INSERT'а не выполняются, строка "select @@RC" после вызова процедуры тоже не выполняется. Если в триггере после UPDATE написать "print @@ERROR" - тоже не выполняется. Т.е., как будто происходит вылет вверх по всей цепочке вызовов. В чем причина? Помогите, пожалуйста, разобраться. Заранее спасибо.
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030682
Фотография cvasil
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
IMHO дело тут не в триггере, а в том, что внутренний обработчик
ситуации timeout принудительно завершает активный батч.
Даже трюк с 'заворачиванием' проблемного кода в sp_executesql
в данном случае не работает. Батч все равно прерывается, а выставленная
@@ERROR видна только в следующем батче текущего соединения.

BOL предлагает возложить всю обработку timeout-ов на клиентское приложение.
Вот что там написано на эту тему:

When a statement has waited longer than the LOCK_TIMEOUT setting, the blocked statement is canceled automatically,
and error message 1222 "Lock request time-out period exceeded" is returned to the application.
However, any transaction containing the statement is not rolled back or canceled by SQL Server.
Therefore, the application must have an error handler that can trap error message 1222.
If an application does not trap the error, it can proceed unaware that an individual statement
within a transaction has been canceled, and errors can occur because statements later in the
transaction may depend on the statement that was never executed.
Implementing an error handler that traps error message 1222 allows an application
to handle the time-out situation and take remedial action for example, automatically
resubmitting the statement that was blocked, or rolling back the entire transaction.


Вообще говоря отсутствие нормальной структурированной обработки ошибок
это известная проблема для Transact-SQL. Микрософт обещает исправить это
только в следующей версии SQL сервера. Будем надеяться что не обманут
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030797
Kosta
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я тут поэкспериментировал, и вот что заметил. Получается, что дело не в LOCK_TIMEOUT, а в том, как ведет себя MS SQL 2000 (или триггер, или - не знаю кто
). Если в цепочке вызовов не присутствует триггер, то ошибка прекрасно ловится в текущем BATCH'е. Но если в эту цепочку вклинить триггер, в котором содержится команда, заведомо приводящая к ошибке на этапе выполнения (причем, любая ошибка, ну, например, INSERT в несуществующую таблицу заканчивается возвращением @@ERROR=208 ), то получаем все ту же картину: выполнение прерывается и ошибка не ловится, хотя Query Analyzer ругается. И, как сказал cvasil (которому, кстати, спасибо), "выставленная @@ERROR видна только в следующем батче текущего соединения". Т.е., если после неудачного завершения текущего BATCH'а сразу же посмотреть "select @@ERROR", она действительно-таки там присутствует, и можно произвести диагностику. Но ведь это уже в следующем батче. А можно ли поймать такую ошибку в текущем? И если можно, то как?
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030823
Фотография cvasil
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Хм ...
Я тут тоже маленько поэксперементировал, но у меня ошибка по таймауту не ловится даже при отсутствии триггера.
Например делаю так:

--В соединении 1 создаю табличку, вставляю туда строку и запускаю долгую транзакцию с update этой строки
create table t1(id int, data int)
insert t1 (id,data) values (1,1)
go
begin tran
update t1 set data=0 where id=1
waitfor delay '000:05:00'
rollback tran
go
drop table t1
go

-- В то же самое время из соединения 2 делаю update той же строки:
set LOCK_TIMEOUT 1000
update t1 set data=0 where id=1
if @@error<>0
print 'timeout'

-- или вот так:
declare @rc int
set LOCK_TIMEOUT 1000
exec @rc=sp_executesql N'update t1 set data=0 where id=1'
if @rc<>0
print 'timeout'

И в том и в другом случае до print 'timeout' дело не доходит.
Ошибка несуществующей таблицы ловится во втором варианте, но не в первом.

В случае же когда ошибка (любая) происходит в триггере она похоже действительно не ловится.
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030836
Kosta
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вот сижу и не пойму: это что - очередной сюрприз Микрософта? Смотрю в BOL, раздел "Rollbacks in Stored Procedures and Triggers", а там написано:
--------------------------
You must use the SAVE TRANSACTION statement to do a partial rollback in a trigger, even if it is always called in autocommit mode. This is illustrated by the following trigger:

CREATE TRIGGER TestTrig ON TestTab FOR UPDATE AS
SAVE TRANSACTION MyName
INSERT INTO TestAudit SELECT * FROM inserted
IF (@@error <> 0)
BEGIN
ROLLBACK TRANSACTION MyName
END
--------------------------
Ну, случится этот самый ERROR в INSERT'е, и - большой привет, проверка никогда не выполнится. Хотя, с другой стороны, если люди пишут такую документацию, значит считают этот пример абсолютно рабочим? Значит, после генерации исключения в триггере его выполнение не должно прерываться? Почему ж тогда наблюдается противоположная картина? Может, есть все-таки методы? Может, установки какие-то или трюки?
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030863
Andreyka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не надо чуть-что ругать мелкософт...большинство ошибок из-за нашего не допонимания или не знания, хотя кое-где и мелкософт виновен.
А теперь по теме:
вставь перед Begin Tran строку - SET XACT_ABORT ON. И отлавливай ошибку выполнения своей SP.
Удачи.
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030868
Deem
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я так думаю, что надо поставить все-таки SET XACT_ABORT OFF, чтобы сервер НЕ прерывал транзакцию автоматически, если возникает ошибка run-time, тогда операторы после INSERT будут выполняться.
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030869
Andreyka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Deem:
Но в этом случае триггер не отработает и SP завершится без ошибки
Kosta:
Попробуйте-
CREATE TRIGGER tgAddPlace ON [dbo].[tbPlace]
FOR INSERT
AS
set nocount on
UPDATE tbPlace
SET subcount = P.subcount + (select count(*) from inserted where toppid = P.pid)
FROM tbPlace P, inserted I
WHERE I.toppid is not null
AND P.pid = I.toppid
if @@ERROR >0
begin
raiserror 50010 'Неверная попытка update!'
rollback transaction
end
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030877
Kosta
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Andreyka, Deem:
Ставил SET XACT_ABORT и ON и OFF - не помогает.
Andreyka:
Если бы все было так просто... Все дело как раз в том, что проверка "if @@ERROR >0" не отработает, если в "UPDATE" сгенерируется исключение. Прерывается выполнение всего пакета вплоть до верхнего уровня.

Я уже свел свои "изыскания" до минимума. Вот попробуйте сделать у себя такой примерчик, и посмотрите, что получается. Может, я действительно чего-то не вижу? (Напомню: это MS SQL 2000):
------------------------------------------------------------------------------
1. Создаем простенькую табличку Test с двумя полями - CODE int, NAME char(10).

2. Создаем триггер на вставку для этой таблички:
CREATE TRIGGER tgTest ON [dbo].[Test]
FOR INSERT
AS
set nocount on
insert into AbsentTable values (1,2,3) -- попытка вставки в несуществующую таблицу, стопроцентная ошибка
print 'Trigger: '+ltrim(str(@@ERROR))

3. В Query Analyzer'е пишем:
insert into Test values (1, 'Try insert')
select @@ERROR
------------------------------------------------------------------------------
Вот в такой ситуации я никогда не получу сообщения 'Trigger: 208' и не увижу результатов от "select @@ERROR". Хотя на закладке "Messages" появится ожидаемое сообщение об ошибке (и правильно, так и было задумано).
И все, батч прервался... Т.е., если даже и вставить проверку на наличие ошибки после INSERT (как в триггере, так и в Analyzer'е), она не отработает. А вот теперь, после того, как наш батч прервался, повторим в Query Analyzer'е "select @@ERROR" и увидим результат, с которым завершился предыдущий батч (т.е., 208 в данном случае). Но, опять же: это уже СЛЕДУЮЩИЙ батч, а поймать ошибку нужно было в текущем. Вот в чем вся загвоздка
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030932
Deem
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
По-моему, тут дело в том, что триггер всегда откатывает всю вложенную транзакцию, поэтому мне кажется, что в нашем случае мы никогда не доберемся до переменной @ErrorStatus. Вот если бы ошибку времени выполнения давала бы вторая вложенная процедура, то в ней можно было бы откатывать только ее транзакцию с помощью точек сохранения, и тогда первая ХП продолжала бы работу при SET XACT_ABORT OFF (ее транзакцию сервер бы не откатил). А тут, наверное, номер ошибки (если он нужен) придется отлавливать на клиенте.
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030945
Kosta
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Deem:
На клиенте в таком случае возникает EOleException, перехватив которое получаем:
1. В ErrorCode - сумасшедший отрицательный код (около 10 знаков), который абсолютно не является значением @@ERROR с SQL-сервера.
2. В Message - текстовое сообщение в оригинальном виде (естественно, на английском языке);

При всем при этом в @RETURN_VALUE хранимой процедуры возвращается 0, даже если перед вызовом проинициализировать его другим значением.
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030982
Deem
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
А на чем написано клиентское приложение?
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32030990
Kosta
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Deem:
Еще пока не написано. Только начинаю. Borland C++Builder 6. Вобщем-то, есть и 5-й, но если вышел 6-й - грех не попробовать
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32031041
Andreyka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Kosta:
Вообще я с такой ситуацией столкнулся у себя (MSSQL2000&MSACCESS2000.adp), т.е. когда триггер на таблице давал ошибку, но хранимая процедура ее не ловила и не делала откат транзакции. Вылечил это так:
на клиенте: 1) открываю транзакцию; 2)вызываю SP; 3)закрываю транзакцию или откатываю;
на сервере: 1) в теле SP перед открытием транзакции включаю ...ABORT ON(чтобы SP при ошибке в триггере откатила транзакцию).
Получаю вот что: при ошибке в триггере SP действительно не ловит @@Error, но транзакция откатывается и прерывается выполнение SP, транзакция на клиенте прерывается и возникает ошибка (она действительно не суразная, но что стого, она всегда в этом случае одна - именно ее и лови). В принципе можно и без транзакции на клиенте. Ведь ты говоришь, что ошибка ловиться только не нравиться ее код, ну и хрен с ним (если он в данном случае всегда такой именно его и обработай по своему - дай свое сообщение и т.д.).
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32031052
Deem
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
andreyka,Kosta:

В общем-то правильно, хотелось бы только, чтобы Kosta сообщил, если у него все получится.
...
Рейтинг: 0 / 0
Как обработать исключение, возникшее в триггере
    #32031078
Kosta
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
andreyka, Deem:
Похоже, докопался, наконец-то до того кода ошибки, который возвращается с сервера. Делаю вот что:
1. На сервере оставляю все, как и было.
2. На клиенте (BCB5,TADOConnection,TADOStoredProc) заключаю вызов хранимой процедуры в блок try...catch,
3. В catch'е смотрю в TADOConnection->Errors->Item[index]->NativeError. Вот там-то он и лежит - свеженький и нетронутый

(Item[index] - потому что в коллекции может присутствовать несколько ошибок, но в данном случае - всего одна)

А тот сумасшедший отрицательный код, который находится в ErrorCode объекта EOleException, лежит в TADOConnection->Errors->Item[index]->Number и, как пишет MSDN, "Indicates the number that uniquely identifies an Error object". В принципе, можно и им пользоваться, и, скорее всего, тоже не ошибешься, т.к., в том же MSDN говорится "Based on the error documentation from the Source, Number, and Description properties of Error objects, you can write code that will handle the error appropriately". Но, мне, например, гораздо больше по душе видеть первоисточник


Большое спасибо cvasil, andreyka, Deem! Всем - удачи!
...
Рейтинг: 0 / 0
16 сообщений из 16, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Как обработать исключение, возникшее в триггере
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]