September 11, 2006 at 11:07 am
Hi ,
I do not really like doing this. I know it goes against the grain.... but
I am told that they want to commit the insert regardless of whether the sp runs ok...
Some more info...
The insert into the table is an isolated insert (not part of a larger
transaction). Also, only 1 row is added at a time (no bulk inserts).
The question I have, and I want to be sure on this is....
Does the "commit transaction" in the code below apply soley to committing the Insert into the "Orders" table. Can it cause other issues ?
CREATE TRIGGER testtrigger ON dbo.Orders
FOR INSERT AS
DECLARE @OrderID VARCHAR(20)
SELECT @OrderID = OrderNumber
FROM Inserted
commit transaction
BEGIN
EXEC prProcessOrder @OrderID
END
Thanks,
Rob
September 11, 2006 at 2:18 pm
Rob - the "commit transaction" is not required - the trigger gets fired only after the row is already inserted into the table....& since "they want to commit....regardless of whether the sp runs ok"...all's well...
ps:Hopefully I've understood the question right...
**ASCII stupid question, get a stupid ANSI !!!**
September 11, 2006 at 3:07 pm
As I understand it..., without the explicit "Commit Transaction" within the trigger, the row would not get inserted if prProcessOrder fails because if any action within the trigger fails, then the original action (insert into the table) fails as well. This is by design.
Thanks,
Rob
September 11, 2006 at 6:54 pm
The only way to keep from rolling back the record inserted by the trigger, is to do it with another connection.
SP_OA could be used to create such a connection but it's slow and, according to rumor, does have some memory leaks associated with it. Also, if you forget to close the connection, you will quickly eat up all connections available and the server dies.
One method that I've used is to make the call to insert the record using xp_CmdShell to insert the record. It effectively makes a new connection to the server and automatically closes the connection when the cmd is done. Of course, the xp_CmdShell call should be to OSQL using a trusted connection.
There sre some warnings about using xp_CmdShell so far as possible attacks on your database and you might want to Google for those things just to make sure your code doesn't fit into any of the "attackable" categories.
Also, like SP_OA, xp_CmdShell requires "SA" privs, another possible detriment.
Other than that, I can't think of a way of doing this.
--Jeff Moden
Change is inevitable... Change for the better is not.
September 11, 2006 at 7:00 pm
Jeff,
When I tested this it did appear to "work"... The sp that the trigger call fails, yet the original insert sticks... because of the Commit Transaction within the trigger appears too make it stick...
If you insert an OrderNumber < 6 characters and a valid date, the
transactions sticks, even if the sp fails... sample below...
insert into Orders select '1111','1/1/2006'
Thanks,
Rob
CREATE TABLE [dbo].[Orders] (
[OrderNumber] [char] (6) COLLATE Latin1_General_BIN NOT NULL ,
[Shipped] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[OrdersLog] (
[OrderNumber] [char] (5) COLLATE Latin1_General_BIN NULL ,
[Shipped] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[OrdersRaw] (
[Ord] [char] (6) COLLATE Latin1_General_BIN NULL ,
[DatShp] [varchar] (50) COLLATE Latin1_General_BIN NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[OrdersSuccessFail] (
[OrderId] [char] (6) COLLATE Latin1_General_BIN NULL ,
[SuccessFail] [varchar] (50) COLLATE Latin1_General_BIN NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Orders] ADD
CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED
(
[OrderNumber]
  ON [PRIMARY]
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER testtrigger ON dbo.Orders
FOR INSERT
AS
DECLARE @OrderID VARCHAR(20)
SELECT @OrderID = OrderNumber
FROM Inserted
COMMIT TRANSACTION
BEGIN TRANSACTION
insert into OrdersSuccessFail select @OrderID, 'Fail'
COMMIT TRANSACTION
BEGIN
EXEC prProcessOrder @OrderID
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
CREATE PROCEDURE prProcessOrder @OrderId as char(6) AS
declare @errorcode int
Select @errorcode = @@Error
-- The following code causes failure due to incorrect datatype
If @errorcode = 0
Begin
Insert into OrdersLog select @OrderId, 'mmm'
Select @errorcode = @@Error
End
If @errorcode = 0
begin
update OrdersSuccessFail set SuccessFail = 'Success' where OrderId =
@OrderId
end
GO
September 11, 2006 at 7:24 pm
okay Jeff - use your infinite reserves of patience and explain to me where Rob says that the row needs to be "rolled back"...I've read this post several times over to see where this is spelled out and I still "don't get it"...
As I understand it, the row needs to be inserted regardless of the success/failure of the proc - the proc is called from the trigger - all this will happen ONLY IF the trigger is fired and the trigger will be fired ONLY IF the t-sql responsible for the insertion succeeds...what am I missing ?!?!
**ASCII stupid question, get a stupid ANSI !!!**
September 11, 2006 at 8:56 pm
Ok... first things first...
Rob... I'm confused... I thought you were saying that some sproc OUTSIDE of the trigger was being rolled back and that you need what was happening inside the trigger to "stick" even if the OUTSIDE sproc failed... is that true? Please explain one more time
Sushila... as of this very moment, I'm as confused as you are... I'm not sure what Rob means, anymore
--Jeff Moden
Change is inevitable... Change for the better is not.
September 12, 2006 at 5:42 am
Sorry for the confusion folks....
In the example above, I want the original insert statement to work...
insert into Orders select '1111','1/1/2006'
Even if the procedure called by the trigger fails....
EXEC prProcessOrder @OrderID
That is why I placed the "COMMIT TRANSACTION" after obtaining the "Inserted" value within the trigger.
The reason I placed the "COMMIT TRANSACTION" within the trigger is to Commit the implicit transaction that the trigger itself begins.
From what I understand, if the code attached to a trigger fails (in this case prProcessOrder), then the underlying action (the original Insert in this case) fails as well. Well, they do not want the original insert to fail.
Assume that the original insert is simply some ADO insert statement.
Thanks !
September 12, 2006 at 5:57 am
And that's why I said that the trigger is fired only after the insert is successful - at the point that it executes you already have a row inserted in your "Orders" table...
"The trigger executes after the statement that triggered it completes. If the statement fails with an error, such as a constraint violation or syntax error, the trigger is not executed." (from BOL)
**ASCII stupid question, get a stupid ANSI !!!**
September 12, 2006 at 6:45 am
Rob - a million apologies - I was so focusing on the fact that the trigger does not ever get fired if the insert fails that I didn't pay attention to the second part - of transactions within the trigger failing...a "commit" then should work though I've never had cause to use one since usually most business rules go for an "all or nothing" basis..I'll test it as soon as I get a break..or maybe Jeff will be back to confirm/negate..
**ASCII stupid question, get a stupid ANSI !!!**
September 12, 2006 at 6:54 am
In the example I presented, if you comment out the first COMMIT TRANSACTION (in the trigger), then the stored procedure that is called by the trigger (EXEC prProcessOrder @OrderID) will fail (I placed a statement in the stored proc to make it fail on purpose). then you will notice that the original insert (insert into Orders select '1111','1/1/2006') fails as well.
I believe this is because SQL treats the whole process as 1 transaction that gets rolled back if there are any errors at all within the trigger.
Thanks
September 12, 2006 at 7:12 am
Rob - a couple of things - I was going to tell you to take the "exec proc" out of the trigger and make it a separate command when I came acoss this other post with an identical requirement....erland sommarskog (no less) has this to say:
"If I understand this correctly, you have a mission-critical application, and now you want to tap data from a table, but without any risk of disturbing the normal flow. In such case a trigger is probably not the way to go, because it is part of trigger fundamentals that a trigger is whole together with the statement that it is attached to. If the trigger fails, then the statement has failed, and there is no way around that, nor should there be. You should probably look into replication. In this case, the committed data is read from the transaction log at some later point in time it reaches its destination."
On the other hand I did run some quick tests - and you're right...adding a "commit" at that point does seem to make the insert "stick" despite a failing sproc...you may have just found a really simple way to avoid going through some convoluted hoops...
**ASCII stupid question, get a stupid ANSI !!!**
September 12, 2006 at 7:27 am
I'll say... as for me, I can't find the examples I had made for something else (might not even be the same problem but dunno, anymore) and I can't make what I said work (shame on me for not testing prior to opening my mouth ). Either it was some really bad coffee or Alzheimers is finally settling in I'm going to my room to suck my thumb and twiddle my hair, now...
--Jeff Moden
Change is inevitable... Change for the better is not.
September 12, 2006 at 7:28 am
Yes, I posted this in the public forums as well... and yes erland sommarskog has responded to me as well...
He does not like the "quick fix" (and rightly so for the reasons he states), and quite frankly I never liked it either, but I need some ammo to prevent it being employed as a temporary solution. I also need to find a new solution... You know how it goes..., if you do not want to do that, then what "DO" you want to do .
Problem is that there are also problems with any of the other suggestions I can think of... these are 2 processes that must be very closely coupled (scheduled jobs won't cut it). Also, the program that is inserting the row is another application altogether, so it cannot be modified.
Thanks again for your help and taking the time to look at it...
September 12, 2006 at 7:43 am
You can always start a job in the trigger (in another process with an alert or raiserror... don't remember which). That way there would be no delay besides the execution of the code...
BTW you can also schedule a job to run every 0.001 second if you want to... But I would suggest every second if that's acceptable to you .
Viewing 15 posts - 1 through 15 (of 18 total)
You must be logged in to reply to this topic. Login to reply