2009-04-07 14 views
27

Tenemos una gran aplicación escrita principalmente en SQL Server 7.0, donde todas las llamadas a bases de datos se realizan en procedimientos almacenados. Ahora estamos ejecutando SQL Server 2005, que ofrece más características de T-SQL.¿Cuál es el mejor uso de la práctica del manejo de errores de SQL Server T-SQL?

Después de seleccionar SELECT, INSERT, UPDATE y DELETE, @@ ROWCOUNT y @@ ERROR se capturan en variables locales y se evalúan para detectar problemas. Si hay un problema se hace lo siguiente: parámetro de salida de mensajes

  • de error se establece
  • rollback (si es necesario) se hace
  • información que está escrito (INSERT) para registrar la tabla
  • de retorno con una número de error, exclusivo de este procedimiento (positivo si es fatal, negativo es advertencia)

No todos 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 a veces se divide de la lógica de error (en actualizaciones donde se comprueba un campo de concurrencia en la cláusula WHERE, rows = 0 significa que otra persona ha actualizado los datos). Sin embargo, aquí es un ejemplo bastante genérica:

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 

Estoy buscando en la sustitución de la forma en que hacemos esto con el try-catch T-SQL. He leído sobre la sintaxis TRY...CATCH (Transact-SQL), así que no solo publique 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, simplemente cualquier uso bueno o mejor práctica del manejo de errores T-SQL.

Respuesta

29

Usted debe leer esto:

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

no puede recomendar que enlazan 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 capacidades de manejo de errores try/catch en SQL Server 2005+.

+0

Creo que se salta las cosas de SQL Server 2005, pero de todos modos es excelente. Y el resto de su sitio también. – gbn

+0

Para SQL Server 2005+ comience aquí http://www.sommarskog.se/error_handling/Part1.html –

3

Parece que ya tiene un buen manejo de esto. Sospecho que estás haciendo más del 95% de los programadores de SQL que hay.

Usted debe encontrar algo de información interesante aquí:

Uno sugerencia [no relacionada]: empiece a usar '<>' en lugar de '! ='.

[* SQL Junkies se ha ido, por lo que el segundo artículo no está disponible. Intentaré volver a publicarlo en algún lugar y actualizar el enlace.]

+0

¿por qué usar '<>' en lugar de '! ='? –

+1

especificación ANSI especifica <>. Muchos db también admiten! =, Pero no es estándar. –

+0

http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt Consulte la sección 5.2 –

6

No hay un conjunto de mejores prácticas para el manejo de errores. Todo se reduce a cuáles son tus necesidades y a ser coherente.

Aquí hay un ejemplo 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 
17

la actualidad utilizamos esta plantilla para cualquier consulta que llevamos a cabo (se puede dejar de lado el material de transacción, si usted no lo necesita, por ejemplo en una sentencia 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 atrapada en su tabla de registro de errores.

Funciona muy bien para nosotros. Probablemente puedas incluso automatizar parte de la conversión de tus viejos procs almacenados a un nuevo formato usando Code Generation (por ejemplo, CodeSmith) o algún código C# personalizado.

+10

¿Por qué comenzar la transacción fuera del bloque TRY? ¿Existe alguna ventaja? Todos los ejemplos en MSDN muestran BEGIN TRAN como la primera instrucción dentro de TRY http://technet.microsoft.com/en-us/library/ms179296%28v=sql.105%29.aspx – Davos

+2

XACT_STATE también se debe considerar si se utilizan transacciones: https://msdn.microsoft.com/en-us/library/ms189797.aspx –

Cuestiones relacionadas