Home All Groups Group Topic Archive Search About

How can I force some data maintenance statements whether the transactions success or failure?

Author
24 Nov 2005 6:19 AM
ABC
How can I force some data maintenance statements whether the transactions
success or failure?

The case is as:

When the insert statement to do, the insert trigger will execute, when the
triggers has errors, then the triggers should write the custom messages to
the another tables and then rollback the insert statements.  The client
sides will access the another tables gather the messages.  How to write this
case?

Author
24 Nov 2005 9:15 AM
Madhivanan
Author
24 Nov 2005 2:44 PM
Razvan Socol
Hi, ABC

If you want something logged in a table even when an error appears
(because of a constraint violation, for example), you cannot do this.
If you only want something logged according to a certain condition
which is verified by the trigger, you have two solutions:
a) if it's OK to just ignore the incorrect value, use an "INSTEAD OF"
trigger which updates only the correct values and returns (maybe with a
RAISERROR, but with no ROLLBACK)
b) use a normal trigger, but after RAISERROR and ROLLBACK, insert the
message in the error log table, with something like this:

USE tempdb
SET NOCOUNT ON
GO
CREATE TABLE Employees (
    EmployeeID int IDENTITY PRIMARY KEY,
    EmployeeName nvarchar(50) NOT NULL UNIQUE,
    Salary money NOT NULL CHECK (Salary>0)
)

INSERT INTO Employees VALUES ('Adam', 50000)
INSERT INTO Employees VALUES ('Bob', 70000)

GO
CREATE TABLE ErrorLog (
    MessageID int IDENTITY PRIMARY KEY,
    EventDate datetime NOT NULL DEFAULT GETDATE(),
    UserName varchar(30) NOT NULL DEFAULT SYSTEM_USER,
    HostName varchar(30) NOT NULL DEFAULT HOST_NAME(),
    Message nvarchar(1000) NOT NULL
)

GO
CREATE TRIGGER Employees_U_SmallRaisesOnly ON Employees
FOR UPDATE
AS
IF @@ROWCOUNT>0 AND UPDATE(Salary) BEGIN
    SET NOCOUNT ON

    IF EXISTS (
        SELECT * FROM inserted i
        INNER JOIN deleted d ON i.EmployeeID=d.EmployeeID
        WHERE i.Salary>d.Salary*2
    ) BEGIN

        DECLARE @Messages TABLE (Message nvarchar(1000))
        INSERT INTO @Messages (Message)
        SELECT 'Someone tried to change the salary for '+i.EmployeeName
            +' from '+CONVERT(varchar(23),d.Salary,1)
            +' to '+CONVERT(varchar(23),i.Salary,1)+' !'
        FROM inserted i
        INNER JOIN deleted d ON i.EmployeeID=d.EmployeeID
        WHERE i.Salary>d.Salary*2

        RAISERROR ('You cannot raise a salary to more than double !',16,1)
        ROLLBACK

        INSERT INTO ErrorLog (Message)
        SELECT Message FROM @Messages
    END
END

GO
UPDATE Employees SET Salary=60000 WHERE EmployeeName='Adam'
GO
SELECT * FROM Employees
SELECT * FROM ErrorLog
GO
UPDATE Employees SET Salary=200000 WHERE EmployeeName='Bob'
GO
SELECT * FROM Employees
SELECT * FROM ErrorLog
GO
DROP TABLE Employees, ErrorLog

Razvan

PS. I don't know if this behaviour is documented and/or supported.
Author
24 Nov 2005 10:02 PM
Hugo Kornelis
On Thu, 24 Nov 2005 14:19:17 +0800, ABC wrote:

>How can I force some data maintenance statements whether the transactions
>success or failure?
>
>The case is as:
>
>When the insert statement to do, the insert trigger will execute, when the
>triggers has errors, then the triggers should write the custom messages to
>the another tables and then rollback the insert statements.  The client
>sides will access the another tables gather the messages.  How to write this
>case?
>

Hi ABC,

I know of only two ways to persist something over a ROLLBACK.

If you want to log a single error, best is to use variables:

SET @ErrorMsg = 'Whatever the error code should be'
SET @ErrorSrc = 'Something to identify where the error was made'
SET @ErrorWhen = CURRENT_TIMESTAMP
ROLLBACK TRANSACTION
INSERT INTO Logging (ErrorMsg, ErrorSrc, ErrorWhen)
VALUES (@ErrorMsg, @ErrorSrc, @ErrorWhen)

In this example, the variables are not actually needed since I use
hard-coded strings. But if the error message or error source has to be
taken from inserted or updated rows that will be rolled back, then use
this method.


If you have to log an unknown number of error messages (e.g. a multi-row
update fails and you want to log all rows that vioalte the business
constraint), then use a table variable:

DECLARE @Errors TABLE(....)
INSERT INTO @Errors (..., ..., ...)
SELECT ..., ..., ...
FROM   inserted
WHERE  business rule is violated
ROLLBACK TRANSACTION
INSERT INTO Logging (..., ..., ...)
SELECT ..., ..., ...
FROM   @Errors

Best, Hugo
--

(Remove _NO_ and _SPAM_ to get my e-mail address)

AddThis Social Bookmark Button