Był masywny upgrade do obsługi błędu wewnątrz SQL Server 2005. Te artykuły są dość rozległe: Error Handling in SQL 2005 and Later by Erland Sommarskog i Error Handling in SQL 2000 – a Background by Erland Sommarskog
Najlepszym sposobem jest coś takiego:
Utwórz procedurę przechowywaną jak:
CREATE PROCEDURE YourProcedure
AS
BEGIN TRY
BEGIN TRANSACTION --SqlTransaction
DECLARE @ReturnValue int
SET @ReturnValue=NULL
IF (DAY(GETDATE())=1 --logical error
BEGIN
SET @ReturnValue=5
RAISERROR('Error, first day of the month!',16,1) --send control to the BEGIN CATCH block
END
SELECT 1/0 --actual hard error
COMMIT TRANSACTION --SqlTransaction
RETURN 0
END TRY
BEGIN CATCH
IF XACT_STATE()!=0
BEGIN
ROLLBACK TRANSACTION --only rollback if a transaction is in progress
END
--will echo back the complete original error message to the caller
--comment out if not needed
DECLARE @ErrorMessage nvarchar(400), @ErrorNumber int, @ErrorSeverity int, @ErrorState int, @ErrorLine int
SELECT @ErrorMessage = N'Error %d, Line %d, Message: '+ERROR_MESSAGE(),@ErrorNumber = ERROR_NUMBER(),@ErrorSeverity = ERROR_SEVERITY(),@ErrorState = ERROR_STATE(),@ErrorLine = ERROR_LINE()
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber,@ErrorLine)
RETURN ISNULL(@ReturnValue,1)
END CATCH
GO
jest to jednak tylko dla SQL Server 2005 i nowszych.Bez użycia bloków TRY-CATCH w SQL Server 2005, bardzo trudno jest usunąć wszystkie wiadomości odesłane przez program SQL Server. extra messages
odnieść się do spowodowanych przez naturę jak cofanie są obsługiwane za pomocą @@ TRANCOUNT:
z http://www.sommarskog.se/error-handling-I.html#trancount
@@ trancount jest zmienna globalna który odzwierciedla poziom zagnieżdżonych transakcji. Każdy rozpocząć transakcji wzrosty @@ TRANCOUNT o 1, a każdy dokonać transakcji maleje @@ trancount o 1. Nic nie jest faktycznie popełnił aż @@ trancount osiągnie 0. cofnąć transakcji wycofuje wszystko do zewnętrznej BEGIN transakcji (chyba że użyłeś dość egzotycznego ZAPISZ TRANSAKCJĘ) i wymusza @@ trankount na 0, poznaj poprzedniej wartości.
Po wyjściu z procedury przechowywanej, jeśli @@ trancount nie mają taką samą wartość jak to było, gdy procedura rozpoczęta realizacja, SQL Server podnosi błąd 266. Ten błąd nie jest podniesiony, chociaż, jeśli procedura nosi nazwę z wyzwalacza, bezpośrednio lub pośrednio. Nie jest ona podniesiona jeśli używasz z zestawem niejawna TRANSAKCJI NA
Jeśli nie chcesz dostać ostrzeżenie o transakcji liczyć nie pasuje, trzeba mieć tylko jedną transakcję otwarty w jednym czasie . Można to zrobić, tworząc całą procedurę, taką jak ta:
CREATE PROC YourProcedure
AS
DECLARE @SelfTransaction char(1)
SET @SelfTransaction='N'
IF @@trancount=0
BEGIN
SET @SelfTransaction='Y'
BEGIN TRANSACTION --SqlTransaction
END
SELECT 1/0
IF @@ERROR<> 0
BEGIN
IF @SelfTransaction='Y'
BEGIN
ROLLBACK TRANSACTION --SqlTransaction
END
RETURN -1
END
ELSE
BEGIN
IF @SelfTransaction='Y'
BEGIN
COMMIT TRANSACTION --SqlTransaction
END
RETURN 0
END
GO
W ten sposób wydaje się tylko polecenia transakcji, jeśli jeszcze nie jesteś w transakcji. Jeśli w ten sposób zakodujesz wszystkie swoje procedury, tylko procedura lub kod C#, który wyda BEGIN TRANSACTION, wyda polecenie COMMIT/ROLLBACK, a liczba transakcji zawsze będzie zgodna (nie otrzymasz błędu).
w C# od TransactionScope Class Documentation:
static public int CreateTransactionScope(
string connectString1, string connectString2,
string commandText1, string commandText2)
{
// Initialize the return value to zero and create a StringWriter to display results.
int returnValue = 0;
System.IO.StringWriter writer = new System.IO.StringWriter();
try
{
// Create the TransactionScope to execute the commands, guaranteeing
// that both commands can commit or roll back as a single unit of work.
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// Create the SqlCommand object and execute the first command.
SqlCommand command1 = new SqlCommand(commandText1, connection1);
returnValue = command1.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command1: {0}", returnValue);
// If you get here, this means that command1 succeeded. By nesting
// the using block for connection2 inside that of connection1, you
// conserve server and network resources as connection2 is opened
// only when there is a chance that the transaction can commit.
using (SqlConnection connection2 = new SqlConnection(connectString2))
{
// The transaction is escalated to a full distributed
// transaction when connection2 is opened.
connection2.Open();
// Execute the second command in the second database.
returnValue = 0;
SqlCommand command2 = new SqlCommand(commandText2, connection2);
returnValue = command2.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
}
}
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message);
}
catch (ApplicationException ex)
{
writer.WriteLine("ApplicationException Message: {0}", ex.Message);
}
// Display messages.
Console.WriteLine(writer.ToString());
return returnValue;
}
Tylko myśl, ale może być w stanie korzystać z TransactionAbortedException
zaczep, aby uzyskać rzeczywisty błąd i ignoruje ostrzeżenia niezgodności licznika transakcji.
@KM, Masz 'IF @@ trankount <0' wcześnie w przykładowym proc. Czy transkonto @@ może być zawsze ujemne? Nie powinno to być 'IF @@ trankount = 0' ?? –
@Charles Bretana, masz rację, to jest typ-o. Naprawię to ... –
@KM, Thx! to działało w moim teście sproc ze zmianą, ale to jest tutaj niezakłócone od kwietnia/maja ... Tak więc moim naturalnym założeniem jest to, że czegoś mi brakuje ... Nie byłem do końca pewien w ten czy inny sposób ... Happy Wakacje ! –