Egymást követő több SQL hiba kezelése

Felmerült egy érdekes kérdés Twitteren és a Stackoverflow-n is, nevezetesen: hogyan lehet több hibát "elkapni" SQL kódban. A szomorú hír az, hogy T-SQL-ben a TRY/CATCH nem tudja ezt, pedig volt rá igény, lásd Connect bejegyzés.

Lássuk rá egy konkrét példát:
SELECT 1/0
RAISERROR ('második hiba',16,1)
GO
Ennél az esetnél két hiba is lesz:
Msg 8134, Level 16, State 1, Line 18
Divide by zero error encountered.
Msg 50000, Level 16, State 1, Line 19
második hiba
Lássuk mindezt TRY/CATCH használatával:
BEGIN TRY
SELECT 1/0
RAISERROR ('második hiba',16,1)
END TRY
BEGIN CATCH
	SELECT ERROR_NUMBER(), ERROR_MESSAGE()
END CATCH

GO
Ebben az esetben csak a nullával való osztásra fogjuk megkapni a 8134-es hibát. Látható, hogy a CATCH csak 1 hibát ad vissza. Ennek ellenére és az XACT_ABORT beállítástól függően, több hibánk is lehet. Lássunk egy másik példát is: egy mentést szeretnék csinálni, azonban nincs joga az SQL Server-nek írni az adott mappába. Ilyenkor két hibát is kapok:
Msg 3201, Level 16, State 1, Line 5
Cannot open backup device 'C:\temp\backup\aw.bak'. Operating system error 5(Access is denied.).
Msg 3013, Level 16, State 1, Line 5
BACKUP DATABASE is terminating abnormally.

Ezt az SSMS simán visszaadja, de egy TRY/CARCH esetén már csak a 3013-as hiba jön vissza. 
Akkor ezt most hogyan is lehet szépen megfogni? Sajnos csak olyan alkalmazásból lehet elkapni, ami az SQLException .NET objektummal tud dolgozni. Ez akár lehetne gy CLR sproc is, de az már nem a leghatékonyabb :) Én egy parancssori alkalamzás segítségével mutatnám ezt be:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Data;

namespace multipleSqlExceptions
{
    class Program
    {
        static void Main(string[] args)
        {
            using (SqlConnection conn = new SqlConnection(@"Data Source=.;Initial Catalog=master;Integrated Security=True"))
            {
                conn.Open();

                using (SqlCommand comm = conn.CreateCommand())
                {
                    comm.CommandText = @"BACKUP DATABASE AdventureWorks TO DISK = N'C:\temp\backup\aw.bak';";
                    comm.CommandType = CommandType.Text;

                    try 
	                {	        
		                comm.ExecuteNonQuery();
	                }
	                catch (SqlException e)
	                {
		                foreach (SqlError err in e.Errors)
	                    {   
                            Console.WriteLine("ERROR:");
                            Console.WriteLine("Code: " + err.Number);
		                    Console.WriteLine("Message: " + err.Message);
                            Console.WriteLine("-----------------------------------------------");
	                    }
	                }

                }
            }

            Console.ReadKey();
        }
    }
}
Az SSMS persze mindezt azért tudja, mert egy .NET alkalmazás és pontosan ezzel a módszerrel dolgozik.

Add comment