Какова наилучшая практика использования ошибок SQL Server T-SQL?
У нас есть большое приложение, в основном написанное на SQL Server 7.0, где все вызовы базы данных хранятся в хранимых процедурах. Теперь мы запускаем SQL Server 2005, который предлагает больше возможностей T-SQL.
После почти всех SELECT, INSERT, UPDATE и DELETE, @@ROWCOUNT и @@ERROR попадают в локальные переменные и оцениваются по проблемам. Если возникла проблема, выполните следующие действия:
- установлен параметр вывода сообщения об ошибке
- откат (если необходимо)
- информация записывается (INSERT) для записи таблицы
- return с номером ошибки, уникальным для этой процедуры (положительный, если фатальный, отрицательный - предупреждение)
Все они не проверяют строки (только когда это известно), а некоторые отличаются более или менее информацией об ошибках/отладочной информации. Кроме того, логика строк когда-то расщепляется от логики ошибок (при обновлениях, где поле concurrency отмечено в предложении WHERE, rows = 0 означает, что кто-то еще обновил данные). Однако здесь довольно общий пример:
SELECT, INSERT, UPDATE, or DELETE
SELECT @[email protected]@ERROR, @[email protected]@ROWCOUNT
IF @Rows!=1 OR @Error!=0
BEGIN
SET @ErrorMsg='ERROR 20, ' + ISNULL(OBJECT_NAME(@@PROCID), 'unknown')
+ ' - unable to ???????? the ????.'
IF @@TRANCOUNT >0
BEGIN
ROLLBACK
END
SET @LogInfo=ISNULL(@LogInfo,'')+'; '+ISNULL(@ErrorMsg,'')+
+ ' @YYYYY=' +dbo.FormatString(@YYYYY)
+', @XXXXX=' +dbo.FormatString(@XXXXX)
+', Error=' +dbo.FormatString(@Error)
+', Rows=' +dbo.FormatString(@Rows)
INSERT INTO MyLogTable (...,Message) VALUES (....,@LogInfo)
RETURN 20
END
Я изучаю замену того, как мы это делаем с TRY-CATCH T-SQL. Я читал о синтаксисе TRY... CATCH (Transact-SQL), поэтому не просто публикуйте некоторые сводки об этом. Я ищу любые хорошие идеи и как лучший делать или улучшать наши методы обработки ошибок. Это не должно быть Try-Catch, просто любая хорошая или лучшая практика использования обработки ошибок T-SQL.
Ответы
Ответ 1
Вы должны прочитать следующее:
http://www.sommarskog.se/error-handling-I.html
Я не могу рекомендовать эту ссылку достаточно высоко. Это немного длиннее, но в хорошем смысле.
Там есть оговорка о том, что она была первоначально написана для SQL Server 2000, но она также охватывает новые возможности обработки ошибок try/catch в SQL Server 2005+.
Ответ 2
В настоящее время мы используем этот шаблон для любых выполняемых нами запросов (вы можете оставить материал Transaction, если он вам не нужен, например, в заявлении DDL):
BEGIN TRANSACTION
BEGIN TRY
// do your SQL statements here
COMMIT TRANSACTION
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
ROLLBACK TRANSACTION
END CATCH
Конечно, вы можете легко вставить исключенное исключение в свою таблицу журнала ошибок.
Это хорошо работает для нас. Возможно, вы, возможно, даже автоматизировали некоторые преобразования из старых хранимых procs в новый формат с использованием Code Generation (например, CodeSmith) или какой-то пользовательский код С#.
Ответ 3
В обработке ошибок не существует набора наилучших методов управления камнем. Все сводится к тому, каковы ваши потребности и быть последовательными.
Ниже приведен пример таблицы и хранимой процедуры, в которой хранятся номера телефонов.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Phone](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Phone_Type_ID] [int] NOT NULL,
[Area_Code] [char](3) NOT NULL,
[Exchange] [char](3) NOT NULL,
[Number] [char](4) NOT NULL,
[Extension] [varchar](6) NULL,
CONSTRAINT [PK_Phone] PRIMARY KEY CLUSTERED
(
[ID] 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
SET ANSI_PADDING OFF
GO
/**/
CREATE PROCEDURE [dbo].[usp_Phone_INS]
@Customer_ID INT
,@Phone_Type_ID INT
,@Area_Code CHAR(3)
,@Exchange CHAR(3)
,@Number CHAR(4)
,@Extension VARCHAR(6)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Err INT, @Phone_ID INT
BEGIN TRY
INSERT INTO Phone
(Phone_Type_ID, Area_Code, Exchange, Number, Extension)
VALUES
(@Phone_Type_ID, @Area_Code, @Exchange, @Number, @Extension)
SET @Err = @@ERROR
SET @Phone_ID = SCOPE_IDENTITY()
/*
Custom error handling expected by the application.
If Err = 0 then its good or no error, if its -1 or something else then something bad happened.
*/
SELECT ISNULL(@Err,-1) AS Err, @Phone_ID
END TRY
BEGIN CATCH
IF (XACT_STATE() <> 0)
BEGIN
ROLLBACK TRANSACTION
END
/*
Add your own custom error handling here to return the passed in paramters.
I have removed my custom error halding code that deals with returning the passed in parameter values.
*/
SELECT ERROR_NUMBER() AS Err, ISNULL(@Phone_ID,-1) AS ID
END CATCH
END
Ответ 4
Похоже, вы уже очень хорошо справляетесь с этим. Я подозреваю, что вы делаете более 95% программистов SQL.
Здесь вы найдете интересную информацию:
Одно [несвязанное] предложение: начните использовать '< > ' вместо '! ='.
[* SQL Junkies ушел, поэтому вторая статья недоступна. Я попытаюсь переиздать его и обновить ссылку.]