как отбросить такое же исключение в SQL Server - PullRequest
76 голосов
/ 20 марта 2010

Я хочу сбросить то же исключение на сервере SQL, которое произошло в моем блоке try. Я могу выдать то же сообщение, но хочу выдать ту же ошибку.

BEGIN TRANSACTION
    BEGIN TRY
            INSERT INTO Tags.tblDomain 
            (DomainName, SubDomainId, DomainCode, Description)
            VALUES(@DomainName, @SubDomainId, @DomainCode, @Description)
            COMMIT TRANSACTION
    END TRY

    BEGIN CATCH
            declare @severity int; 
            declare @state int;

            select @severity=error_severity(), @state=error_state();

            RAISERROR(@@Error,@ErrorSeverity,@state);
            ROLLBACK TRANSACTION
    END CATCH

RAISERROR(@@Error, @ErrorSeverity, @state);

Эта строка покажет ошибку, но я хочу что-то подобное. Это вызывает ошибку с ошибкой 50000, но я хочу, чтобы был выдан номер ошибки, который я передаю @@error,

Я хочу зафиксировать эту ошибку нет на веб-интерфейсе

т.е.

catch (SqlException ex)
{
if ex.number==2627
MessageBox.show("Duplicate value cannot be inserted");
}

Я хочу эту функциональность. что не может быть достигнуто с помощью повышения. Я не хочу давать пользовательское сообщение об ошибке в конце.

RAISEERROR должен возвращать нижеупомянутую ошибку, когда я передаю ErrorNo для выброса в catch

Msg 2627, Level 14, State 1, Procedure spOTest_DomainInsert,

Строка 14 Нарушение ограничения UNIQUE KEY 'UK_DomainCode'. Не могу вставить дубликат ключа в объекте 'Tags.tblDomain. Заявление было прекращено.

EDIT:

Каким может быть недостаток не использования блока try catch, если я хочу, чтобы исключение обрабатывалось во внешнем интерфейсе, учитывая, что хранимая процедура содержит несколько запросов, которые необходимо выполнить

Ответы [ 10 ]

116 голосов
/ 26 марта 2013

SQL 2012 вводит оператор throw:

http://msdn.microsoft.com/en-us/library/ee677615.aspx

Если оператор THROW указан без параметров, он должен появиться внутри блока CATCH Это вызывает возникшее исключение.

BEGIN TRY
    BEGIN TRANSACTION
    ...
    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
    THROW
END CATCH
104 голосов
/ 21 октября 2012

Вот полнофункциональный пример чистого кода для отката ряда операторов в случае возникновения ошибки и сообщения об ошибке.

begin try
    begin transaction;

    ...

    commit transaction;
end try
begin catch
    declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int;
    select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
    rollback transaction;
    raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);
end catch
5 голосов
/ 28 января 2014

Перебрасывание внутри блока CATCH (код перед SQL2012, используйте оператор THROW для SQL2012 и более поздних версий):

DECLARE
    @ErrorMessage nvarchar(4000) = ERROR_MESSAGE(),
    @ErrorNumber int = ERROR_NUMBER(),
    @ErrorSeverity int = ERROR_SEVERITY(),
    @ErrorState int = ERROR_STATE(),
    @ErrorLine int = ERROR_LINE(),
    @ErrorProcedure nvarchar(200) = ISNULL(ERROR_PROCEDURE(), '-');
SELECT @ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + @ErrorMessage;
RAISERROR (@ErrorMessage, @ErrorSeverity, 1, @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine)
4 голосов
/ 20 марта 2010

Я думаю, что ваш выбор:

  • Не поймать ошибку (пусть она всплывет)
  • Поднять пользовательский

В какой-то момент SQL, вероятно, представит команду reraise или возможность перехватывать только определенные ошибки. Но сейчас используйте обходной путь. К сожалению.

1 голос
/ 20 марта 2010

Вы не можете: только движок может выдавать ошибки менее 50000. Все, что вы можете сделать, это выбросить исключение, которое выглядит как ...

Смотри мой ответ здесь

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

0 голосов
/ 02 сентября 2015

Вы также можете создать хранимую процедуру-оболочку для этих сценариев, когда вы хотите, чтобы оператор SQL выполнялся в транзакции и передавал ошибку в ваш код.

CREATE PROCEDURE usp_Execute_SQL_Within_Transaction
(
    @SQL nvarchar(max)
)
AS

SET NOCOUNT ON

BEGIN TRY
    BEGIN TRANSACTION
        EXEC(@SQL)
    COMMIT TRANSACTION
END TRY

BEGIN CATCH
    DECLARE @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int
    SELECT @ErrorMessage = N'Error Number: ' + CONVERT(nvarchar(5), ERROR_NUMBER()) + N'. ' + ERROR_MESSAGE() + ' Line ' + CONVERT(nvarchar(5), ERROR_LINE()), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE()
    ROLLBACK TRANSACTION
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState)
END CATCH

GO

-- Test it
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1; SELECT 2'
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1/0; SELECT 2'
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'EXEC usp_Another_SP'
0 голосов
/ 25 ноября 2013

Учитывая, что вы еще не перешли на 2012 год, одним из способов реализации всплытия исходного кода ошибки является использование части текстового сообщения для исключения, которое вы (повторно) выбрасываете из блока catch. Помните, что он может содержать некоторую структуру, например, XML-текст для вашего кода вызывающей стороны, который нужно проанализировать в его блоке catch.

0 голосов
/ 20 октября 2013

Чтобы остановить выполнение в хранимой процедуре после возникновения ошибки и отправить ошибку обратно вызывающей программе, нужно следовать за каждым оператором, который может выдать ошибку с помощью этого кода:

If @@ERROR > 0
Return

Я был удивлен, узнав, что выполнение в хранимой процедуре может продолжаться после ошибки - незнание этого может привести к некоторым трудным для отслеживания ошибок.

Этот тип параллельной обработки ошибок (до .Net) Visual Basic 6. Ожидание команды Throw в SQL Server 2012.

0 голосов
/ 20 марта 2010

С точки зрения дизайна, какой смысл создавать исключения с исходными номерами ошибок и пользовательскими сообщениями? В какой-то степени это нарушает контракт интерфейса между приложениями и базой данных. Если вы хотите отлавливать оригинальные ошибки и обрабатывать их в более высоком коде, не обрабатывайте их в базе данных. Затем, когда вы поймаете исключение, вы можете изменить сообщение, представленное пользователю, на что угодно. Я бы не стал этого делать, потому что это делает код вашей базы данных хм «не правильным». Как говорили другие, вы должны определить набор своих собственных кодов ошибок (выше 50000) и вместо этого выбросить их. Затем вы можете обрабатывать проблемы целостности («Дублирующиеся значения не допускаются») отдельно от потенциальных бизнес-проблем - «Почтовый индекс недействителен», «Строки не найдены в соответствии с критериями» и т. Д.

0 голосов
/ 20 марта 2010

Хорошо, это обходной путь ...: -)

DECLARE @Error_Number INT
BEGIN TRANSACTION 
    BEGIN TRY
    INSERT INTO Test(Id, Name) VALUES (newID(),'Ashish') 
    /* Column 'Name' has unique constraint on it*/
    END TRY
    BEGIN CATCH

            SELECT ERROR_NUMBER()
            --RAISERROR (@ErrorMessage,@Severity,@State)
            ROLLBACK TRAN
    END CATCH

Если вы заметите блок catch, он не вызывает ошибку, а возвращает фактический номер ошибки (а также откат транзакции). Теперь в вашем .NET-коде вместо того, чтобы ловить Исключение: если вы используете ExecuteScalar (), вы получите нужный номер ошибки и отобразите соответствующий номер.

int errorNumber=(int)command.ExecuteScalar();
if(errorNumber=<SomeNumber>)
{
    MessageBox.Show("Some message");
}

Надеюсь, это поможет,

РЕДАКТИРОВАТЬ: - Просто примечание. Если вы хотите получить количество затронутых записей и пытаетесь использовать ExecuteNonQuery, вышеуказанное решение может не сработать для вас. В противном случае, я думаю, что это подойдет, что вам нужно. Дайте мне знать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...