¿Cuál es la mejor práctica de manejo de errores de SQL Server T-SQL? [cerrado]


Tenemos una gran aplicación escrita principalmente en SQL Server 7.0, donde todas las llamadas a bases de datos son a procedimientos almacenados. Ahora estamos ejecutando SQL Server 2005, que ofrece más características de T-SQL.

Después de casi cada SELECCIÓN, INSERCIÓN, ACTUALIZACIÓN y ELIMINACIÓN, el @@ROWCOUNT y el @@ERROR se capturan en variables locales y se evalúan para detectar problemas. Si hay un problema, se hace lo siguiente:

  • el parámetro de salida del mensaje de error se establece
  • la reversión (si es necesario) es hecho
  • la información se escribe (INSERTAR) en la tabla de registro
  • devuelve con un número de error, único para este procedimiento (positivo si es fatal, negativo es advertencia)

Todos no comprueban las filas (solo cuando se conoce) y algunas difieren con más o menos información de registro/depuración. Además, la lógica de filas es algo separado de la lógica de error (en actualizaciones donde un campo de concurrencia está marcado en la cláusula WHERE, rows=0 significa que alguien más ha actualizado los datos). Sin embargo, aquí es un bastante genérico ejemplo:

SELECT, INSERT, UPDATE, or DELETE

SELECT @Error=@@ERROR, @Rows=@@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

Estoy buscando reemplazar cómo hacemos esto con el TRY-CATCH T-SQL. He leído sobre el INTENTO...CAPTURA la sintaxis (Transact-SQL), así que no solo publiques un resumen de eso. Estoy buscando buenas ideas y cómo mejor para hacer o mejorar nuestros métodos de manejo de errores. No tiene que ser Try-Catch, solo cualquier uso bueno o de las mejores prácticas del manejo de errores de T-SQL.

Author: SteveC, 2009-04-07

4 answers

Deberías leer esto:

Http://www.sommarskog.se/error-handling-I.html

No puedo recomendar ese enlace lo suficiente. Es un poco largo, pero en el buen sentido.

Hay un descargo de responsabilidad en el frente que fue escrito originalmente para SQL Server 2000, pero también cubre las nuevas habilidades de manejo de errores try/catch en SQL Server 2005+.

 31
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-08 17:56:01

Actualmente usamos esta plantilla para cualquier consulta que ejecutemos (puede omitir el material de la Transacción, si no lo necesita, por ejemplo, en una declaración 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

Por supuesto, puede insertar fácilmente la excepción capturada en su tabla de registro de errores.

Funciona muy bien para nosotros. Probablemente incluso podría automatizar parte de la conversión de sus antiguos procs almacenados a un nuevo formato utilizando la generación de código (por ejemplo, CodeSmith) o algún código C# personalizado.

 19
Author: marc_s,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-07 09:20:59

No hay un conjunto de buenas prácticas para el manejo de errores. Todo se reduce a cuáles son sus necesidades y a ser consistentes.

Aquí hay una muestra de una tabla y un procedimiento almacenado que almacena números de teléfono.

 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
 6
Author: DBAndrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-07 15:25:16

Parece que ya tienes un buen manejo de esto. Sospecho que usted está haciendo más del 95% de los programadores SQL por ahí.

Aquí encontrará información interesante:

Una sugerencia [no relacionada]: comience a usar ' ' en lugar de '!='.

[*SQL Junkies ha desaparecido, por lo que el segundo artículo no está disponible. Voy a tratar de conseguir que se vuelva a publicar en algún lugar y actualizar el enlace.]

 3
Author: Rob Garrison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-30 17:53:42