2010-12-10 10 views
6

Encontré un artículo en MSDN Lbrary explicando que try/catch no maneja los errores lanzados cuando no se puede encontrar un objeto. Así que, aunque envuelvo una transacción en un try/catch, la frase retrotracción no ejecuta:manejo de errores de transacción cuando no existen objetos

BEGIN TRY 
BEGIN TRANSACTION 

    SELECT 1 FROM dbo.TableDoesNotExists 
    PRINT ' Should not see this' 
    COMMIT TRANSACTION 
END TRY 
BEGIN CATCH 
    ROLLBACK TRANSACTION 
    SELECT 
      ERROR_MESSAGE() 
END CATCH 

--PRINT 'Error Number before go: ' + CAST(@@Error AS VARCHAR) 

go 
PRINT 'Error Count After go: ' + CAST(@@Error AS VARCHAR) 
PRINT 'Transaction Count ' + CAST(@@TRANCOUNT AS VARCHAR) 

Cuál es la forma recomendada para manejar errores generados cuando no existe un objeto, especialmente cuando hay una transacción involucrados . ¿Debería insertar este código en lugar de las dos últimas instrucciones de impresión?

IF @@ERROR <> 0 AND @@TRANCOUNT > 0 
BEGIN 
    PRINT 'Rolling back txn' 
    ROLLBACK TRANSACTION 
END 

go 

PRINT 'Transaction Count again: ' + CAST(@@TRANCOUNT AS VARCHAR) 

Respuesta

0

¿Por qué intenta recuperar datos de una tabla que no existe?

El componente fundamental de una base de datos es una tabla. No saber lo que está en su esquema es esencialmente tratar de usar SQL como un lenguaje dinámico, que no es.

Me gustaría replantear su diseño; sin saber más sobre las tablas en su base de datos y su uso previsto, es difícil para otros ayudar en este sentido. Agregue algo más de información a su pregunta, por favor.

EDITAR que he tenido una lectura de Bol y de la forma recomendada para controlar los errores cuando un objeto que no existe es como sigue:

Usted puede utilizar TRY ... CATCH controlar los errores que ocurrir durante la compilación 0 recompilación de nivel de extracto por ejecutando el código de generación de error en un lote separado dentro del bloque TRY. Por ejemplo, puede hacer esto colocando el código en un procedimiento almacenado o por ejecutando una instrucción dinámica de Transact-SQL utilizando sp_executesql. Este permite a TRY ... CATCH detectar el error en un nivel de ejecución más alto que el error.

He probado esto a cabo utilizando el siguiente procedimiento

CREATE PROCEDURE [dbo].[BrokenProcedure] 
AS 
BEGIN 
    SET NOCOUNT ON; 
    SELECT * FROM MissingTable 
END 
GO 

Entonces llamaron dentro de un bloque TRY..CATCH:

BEGIN TRY 
    PRINT 'Error Number before: ' + CAST(@@Error AS VARCHAR) 
    EXECUTE [dbo].[BrokenProcedure] 
    PRINT ' Should not see this' 
END TRY 
BEGIN CATCH 
    PRINT 'Error Number in catch: ' + CAST(@@Error AS VARCHAR) 
END CATCH 

resultante en la salida siguiente: Número

Error antes: 0

Número de error en la captura: 208

No es una solución perfecta, ya que tendrá que crear procedimientos (o utilizar SQL dinámico) para la totalidad de su acceso a la tabla, pero es el método recomendado en la EM.

+1

veces en entornos dinámicos, no se sabe el esquema precisa ; otro proceso puede estar cambiando la estructura de la base de datos, por lo que es esencial que el manejo de la excepción try/catch funcione normalmente. –

+0

Punto justo. Sí, el manejo de prueba/captura debería funcionar correctamente pero no defendía la EM; Simplemente no estoy de acuerdo con este uso de una base de datos. – Tony

+0

Tony, este escenario aparece en nuestro entorno de prueba y desarrollo en el que tenemos varios desarrolladores trabajando simultáneamente en los cambios que están realizando. Ahora bien, esta es una situación sobre la que no tengo control, así que no puedo cambiarla. Sin embargo, dado que pueden ocurrir cambios a los objetos en prueba, necesito poder manejar los errores generados por los objetos perdidos, y pensé que el mecanismo de prueba/captura debería ser capaz de manejar eso. Como no lo hago, estoy tratando de encontrar la mejor manera de usar las transacciones para que los cambios de los que soy responsable no terminen en un estado inconsistente. – gr928x

0

Ahora, has encontrado un tema interesante (bueno, para mí de todos modos). No debe iniciar una transacción porque el lote no se compilará. Sin embargo, puede compilar y luego la compilación de nivel de sentencia falla más tarde.

See this question What is wrong with my Try Catch in T-SQL?

Sin embargo, en cualquiera caso se puede usar SET XACT_ABORT ON. Esto agrega predictibilidad porque un efecto es retrotraer automáticamente cualquier transacción. También suprime el error 266. Ver esta SO question demasiado

SET XACT_ABORT ON 
BEGIN TRY 
    BEGIN TRANSACTION 

    SELECT 1 FROM dbo.TableDoesNotExists 
    PRINT ' Should not see this' 
    COMMIT TRANSACTION 
END TRY 
BEGIN CATCH 
    -- not needed, but looks weird without a rollback. 
    -- you could forget SET XACT_ABORT ON 
    -- Use XACT_STATE avoid double rollback errors 
    IF XACT_STATE() <> 0 
     ROLLBACK TRANSACTION 

    SELECT 
      ERROR_MESSAGE() 
END CATCH 

go 
--note, this can't be guaranteed to give anything 
PRINT 'Error Count After go: ' + CAST(@@Error AS VARCHAR) 
PRINT 'Transaction Count ' + CAST(@@TRANCOUNT AS VARCHAR) 
1

Puede comprobar la existencia de un objeto con OBJECT_ID():

IF OBJECT_ID('MyTable') IS NULL RAISERROR('Could not find MyTable.', 18, 0) 
+1

sí, podría pero esto no es escalable. Imagínese si estoy desplegando un script de prueba que toca 50 objetos, ¿pruebo para cada uno? Me parece que try/catch debería manejar el error en primer lugar. – gr928x