I'm trying to write a INSTEAD OF INSERT trigger that will take a MESSAGE column of type VARCHAR(MAX) and attempt to convert it to XML. If the MESSAGE is not valid XML (malformed), I simply want to pass on the original MESSAGE. If it is valid XML, I want to modify it a bit and then insert the modified version.
My first attempt was to use a TRY/CATCH inside the trigger like this:
BEGIN TRY
SET #Message = (SELECT CONVERT(XML, inserted.[MESSAGE], 1) FROM inserted)
END TRY
BEGIN CATCH
--Cast failed. Insert without XML parsing.
GOTO InsertOriginal
END CATCH
...Perform work on XML and do the insert
Problem is, the failed conversion "dooms" the implied trigger transaction and even though execution runs to completion, the overall transaction fails.
So I then tried this
BEGIN TRANSACTION XmlCastTrans;
BEGIN TRY
SET #Message = (SELECT CONVERT(XML, inserted.[MESSAGE], 1) FROM inserted)
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION XmlCastTrans;
--Cast failed. Insert without XML parsing.
GOTO InsertOriginal
END CATCH
COMMIT TRANSACTION XmlCastTrans;
That doesn't work either, as I get a cannot roll back XmlCastTrans. No transaction or savepoint of that name was found error. How can I get this to work?
This is using SQL Server 2005.
If your XML data is in a predictable format could you consider adding a binding to a schema collection on the XML column to enforce good inserts?
http://technet.microsoft.com/en-us/library/ms176009(v=sql.90).aspx
Related
I'm new to SQL Server and stored procedures and could do with a couple of pointers regarding transaction handling on a bug I've inherited.
I have two stored procedures, one inserts a record passed into it, then it calls another one where the first thing it does is read what was inserted.
But sometimes it completes successfully without processing the data. My suspicion is that the selects are happening before the insert has 'hit' the table and retrieve no records, and the stored procedure doesn't handle that.
I don't have time to re-engineer just yet, but the transaction handling looks suspect. Below is a rough outline of what the stored procedures do.
procedure sp1
(#id, #pbody)
as
begin
begin try
set nocount on;
begin
insert into tbl1 (id, tbody)
values (#id, #pbody)
exec sp2 #id
end
end try
begin catch
execute sperror
end catch
end
go
procedure sp2 (#id)
as
begin
begin try
set nocount on;
declare #vbody varchar(max)
select #vbody = tbody -- I don't believe this step always retrieves the row inserted by sp1
from tbl1 with (nolock)
where id = #id
create table #tmp1 (id, msg)
insert into #tmp1
select id, msg
from openjson........
while exists(select top 1 * from #tmp1) -- this looks similar to above, not sure the insert has finished before the read
begin
** do some stuff **
end
end try
begin catch
execute sperror
end catch
end
go
sp2 is using the WITH (NOLOCK) query hint, which can have unintended side-effects. Missing rows is just one of them.
Using NOLOCK? Here's How You'll Get the Wrong Query Results. - Brent Ozar UnlimitedĀ®
I'd strongly recommend removing that hint unless you really understand what it does and have a very good reason for using it.
Hoping someone can help. I'm trying to stop a stored procedure from returning a recordset that has been selected, if an error is encountered later in the stored procedure. I've included some pseudo code below to show what I'm doing. Basically, the SELECT [Foo] is returning a recordset if the or COMMIT actions fail and [Tran1] is rolled back. The client does not support multiple recordsets and the has to come after the SELECT so I'm looking for a command to place in the CATCH block that effectively cancels the SELECT [Foo] and instead enables me to return the recordset created by SELECT -1 AS [Error_Code]
BEGIN TRANSACTION [Tran1]
BEGIN TRY
SET NOCOUNT ON;
<Do some Update>
SELECT [Foo]
FROM [Bar]
<Do some Insert>
COMMIT TRANSACTION [Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1]
SELECT -1 AS Error_Code
END CATCH
This is my code. How to avoid error happened any query automatically rollback already stored.
insert into muser(UserKey, Email, UserPassword)
values(#Key, #Useremail, 'test')
set #UserId = SCOPE_IDENTITY()
set #Key = NEWID()
insert into mUserProfile(UserProfileKey, UserId, UserEmail)
values(#Key, #UserId, #Useremail)
exec SP_Store #Useremail, #ClientId,
Your question is not very clear but I think you want something along these lines.
begin transaction
begin try
insert into muser(UserKey,Email,UserPassword)
values(#Key,#Useremail,'test')
set #UserId= SCOPE_IDENTITY()
set #Key =NEWID()
insert into mUserProfile(UserProfileKey,UserId,UserEmail)
values(#Key,#UserId,#Useremail)
exec SP_Store #Useremail,#ClientId --You should avoid the SP_ prefix.
commit transaction
end try
begin catch
--Report the error here, do NOT silently rollback you transaction
rollback transaction
end catch
This should work with one caveat. If you have a transaction is SP_Store this is not going to work correctly because you can't nest transactions in sql server.
Also, you really avoid the SP_ prefix, or even better avoid prefixes entirely. http://sqlperformance.com/2012/10/t-sql-queries/sp_prefix
I have a stored procedure which is runs automatically every morning in SQL Server 2008 R2, part of this stored procedure involves executing other stored procedures. The format can be summarised thus:
BEGIN TRY
-- Various SQL Commands
EXECUTE storedprocedure1
EXECUTE storedprocedure2
-- etc
END TRY
BEGIN CATCH
--This logs the error to a table
EXECUTE errortrappingprocedure
END CATCH
storedprocedure1 and storedprocedure2 basically truncate a table and select into it from another table. Something along the lines of:
BEGIN TRY
TRUNCATE Table1
INSERT INTO Table1 (A, B, C)
SELECT A, B, C FROM MainTable
END TRY
BEGIN CATCH
EXECUTE errortrappingprocedure
END CATCH
The error trapping procedure contains this:
INSERT INTO
[Internal].dbo.Error_Trapping
(
[Error_Number],
[Error_Severity],
[Error_State],
[Error_Procedure],
[Error_Line],
[Error_Message],
[Error_DateTime]
)
(
SELECT
ERROR_NUMBER(),
ERROR_SEVERITY(),
ERROR_STATE(),
ERROR_PROCEDURE(),
ERROR_LINE(),
ERROR_MESSAGE(),
GETDATE()
)
99% of the time this works, however occasionally we will find that storedprocedure1 hasn't completed, with Table1 only being part populated. However no errors are logged in our error table. I've tested the error trapping procedure and it does work.
When I later run storedprocedure1 manually it completes fine. No data in the source table will have changed by this point so it's obviously not a problem with the data, something else has happened in that instant which has caused the procedure to fail. Is there a better way for me to log errors here, or somewhere else within the database I can look to try and find out why it is failing?
Try to use SET ARITHABORT (see link). It must ROLLBACK in your case. Also the answer of #Kartic seem reasonable.
I recommned also to read about implicit and explicit transactions - I think that this is your problem. You have several implicit transactions and when error happeneds you are in the middle of the job - so only part is rollbackŠµd and you have some data in that tables.
There are some type of Errors that TRY..CATCH block will not handle them, look here for more information https://technet.microsoft.com/en-us/library/ms179296(v=sql.105).aspx . for such Errors you should handle them in your application.
also I think you might have transaction management problem in your application too.
I am not sure if I understood you completely. Below code is too big for comment. So posting as an answer for your reference. If this is not what you want, I'll delete it.
Can we add transaction handling part as well.
DECLARE #err_msg NVARCHAR(MAX)
BEGIN TRY
BEGIN TRAN
-- Your code goes here
COMMIT TRAN
END TRY
BEGIN CATCH
SET #err_msg = ERROR_MESSAGE()
SET #err_msg = REPLACE(#err_msg, '''', '''''')
ROLLBACK TRAN
-- Do something with #err_msg
END CATCH
First sorry for my English.
I have one server when receives an update in specific table, I want write to a remote server too, but if the remote server is unavailable, I want the trigger to write to the local server in another temp table.
Example code to write to remote server:
-- REMOTO is remote server
CREATE TRIGGER insertin
ON mangas
AFTER INSERT
AS
BEGIN
DECLARE #serie varchar(max), #capitulo int
SELECT #serie = serie ,#capitulo = capitulo
FROM inserted
INSERT INTO [REMOTO].[Gest].[dbo].[MARCA] (Codigo, Descripcion)
VALUES (#capitulo, #serie)
END
I need, for example, something like TRY...CATCH or similar. I don't know how can I do it.
Thanks for answers and sorry for my English again.
If using SQL Server 2005 or later, you can put a TRY...CATCH block round your INSERT statement. See MSDN: http://msdn.microsoft.com/en-US/library/ms175976(v=sql.90).aspx
BEGIN TRY
INSERT INTO [REMOTO].[Gest].[dbo].[MARCA]
(Codigo, Descripcion)
VALUES
( #capitulo, #serie )
END TRY
BEGIN CATCH
INSERT INTO [dbo].[MyTemporaryTable]
(Codigo, Descripcion)
VALUES
( #capitulo, #serie )
END CATCH
i do with this code and works well.
if anyone has better solution , please post, I'm learning t-sql now.
any advice is well come
begin
declare
#serie varchar(max),
#capitulo int,
#maxMarca int
select #serie = serie
from inserted
select #maxMarca =max(Codigo) from [REMOTO].[Gest].[dbo].[MARCA]
set #maxMarca = #maxMarca+1
commit -- save transaction insert which generates this trigger work.
begin TRANSACTION
BEGIN TRY
INSERT INTO [REMOTO].[Gest].[dbo].[MARCA] (Codigo, Descripcion) VALUES ( #maxMarca, #serie)
commit transaction --save transaction and finish, if remote server work
END TRY
BEGIN CATCH
IF ##trancount > 0
begin
rollback transaction --remote transaction is go back
INSERT INTO [mangas].[dbo].[mangasTemp] VALUES (#maxMarca, #serie)
commit transaction -- save transaction in local temporal table.
end
END CATCH
end
go