I have a big problem with Sql Server (2008 R2).
In one database i have 3 table for example Tmp_table and T_Dest1 and T_Dest2
and I have 3 procedure that work with this 3 table
procedure Main()
Begin
Declare #Res_Two int, #Res_Tree Int;
Begin Transaction T_One;
do heavy work;
exec #Res_Two = two;--Call Procedure Two
if #Res_Two = -1
Begin
GoTo RB;
End;
do heavy work;
exec #Res_Three = Three;--Call Procedure Tree
if #Res_Three = -1
Begin
GoTo RB;
End;
Delete From Tmp_table;
if ##ERROR > 0
Begin
RB:
Rollback Tran T_One;
Return (-1);
End Else Begin
Commit Tran T_One;
Return (1);
End;
End;
End;
Procedure Two;
Begin
insert into T_Dest1 Select * from tmp_table ;
if ##ERROR > 0 Goto Rb;
if ##ERROR > 0
Begin
RB:
Return (-1);
End Else Begin
Return (1);
End
End;
Procedure Three;
Begin
insert into T_Dest2 Select * from tmp_table ;
if ##ERROR > 0 Goto Rb;
if ##ERROR > 0
Begin
RB:
Return (-1);
End Else Begin
Return (1);
End
End;
all things is OK, but suddenly when user call Main Proc data from tmp_table copy in T_Dest1 but dont copy in T_Dest2 and Main proc return 1 that means every thing is OK; (this state maybe occur one or two in day).
how i can what is incorrect in my db and what is my mistake?
this way that i use from transaction is correct?
can i find when data Lose from Tmp_Table and Main Proc don't work correctly?
i thinks this fault when my server is very busy and many users work with database together.
Here is some pseudocode using try/catch instead of GOTOs.
procedure Main()
Begin
Begin Transaction;
begin try
Declare #Res_Two int, #Res_Tree Int;
do heavy work;
exec two;--Call Procedure Two
do heavy work;
exec Three;--Call Procedure Tree
Delete From Tmp_table;
Commit Transaction;
end try
begin catch
rollback transaction
--Do some other to indicate the error like logging
--Do something to inform caller that an error happened (return statement or raiserror)
end catch
End;
Procedure Two;
Begin
insert into T_Dest1 Select * from tmp_table ;
End;
Procedure Three;
Begin
insert into T_Dest2 Select * from tmp_table ;
End;
Related
This is my stored procedure
ALTER Proc [dbo].[DeleteQualityAssemblyProduction]
#id int,
#Quantity int,
#idPartShip int,
#FK_idNextProcess int
AS
DELETE FROM [dbo].DailyQualityAssemblyProduction
WHERE id=#id
if #FK_idNextProcess=11
Begin
UPDATE [dbo].[ProjectShipping]
SET
QualityAssemblyQty = QualityAssemblyQty- #Quantity
WHERE id=#idPartShip
End
I want when both DELETE and UPDATE run successfully COMMIT the changes otherwise ROLLBACK .
I was wondering if adding COMMIT in the end of stored procedure do the job or I need other method
Here is one way you could tackle this. This is adding a transaction which you will need to handle multiple DML statements in one autonomous block. Then added a try/catch so that if either statement fails the transaction will deal with both statements as one unit of work.
ALTER Proc [dbo].[DeleteQualityAssemblyProduction]
(
#id int,
#Quantity int,
#idPartShip int,
#FK_idNextProcess int
) AS
set nocount on;
begin transaction
begin try
DELETE FROM [dbo].DailyQualityAssemblyProduction
WHERE id = #id
if #FK_idNextProcess = 11
begin
UPDATE [dbo].[ProjectShipping]
SET QualityAssemblyQty = QualityAssemblyQty - #Quantity
WHERE id = #idPartShip
end
commit transaction
end try
begin catch
rollback transaction
declare #error int
, #message varchar(4000);
select #error = ERROR_NUMBER()
, #message = ERROR_MESSAGE()
raiserror ('DeleteQualityAssemblyProduction: %d: %s', 16, 1, #error, #message) ;
end catch
I am new to databases and i try to create a stored procedure that inserts data in tables that are in a many to many relation.If any part of the operation fails then it must try to recover as much as possible from the entire operation. For example, if one wants to create a record regarding publishers and books and succeeds creating the publisher but fails with the book, then it should roll back
the creation of the book, but not of the publisher.
My code looks like this:
BEGIN TRY
BEGIN TRANSACTION
DECLARE #serviciuKey int
DECLARE #specializareKey int
IF NOT EXISTS (SELECT denumire, moneda, pret FROM Serviciu where denumire=#denumire and moneda=#moneda and pret=#pret)
BEGIN
INSERT INTO Serviciu ( denumire, moneda, pret)
VALUES (#denumire, #moneda, #pret)
END
SET #serviciuKey=##IDENTITY
SAVE TRANSACTION savepoint
IF NOT EXISTS (SELECT denumire, descriere FROM Specializare where denumire=#denumire_spec AND descriere=#descriere)
BEGIN
INSERT INTO Specializare( denumire, descriere)
VALUES (#denumire_spec, #descriere)
END
SET #specializareKey=##IDENTITY
SAVE TRANSACTION savepoint
IF NOT EXISTS (SELECT * FROM Specializare_Serviciu where cod_specializare=#specializareKey and cod_serviciu=#serviciuKey)
BEGIN
INSERT INTO Specializare_Serviciu( cod_specializare, cod_serviciu)
VALUES (#specializareKey, #serviciuKey)
END
SAVE TRANSACTION savepoint
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##trancount > 0 ROLLBACK TRANSACTION savepoint
DECLARE #msg nvarchar(2048) = error_message()
RAISERROR (#msg, 16, 1)
RETURN 55555
END CATCH
When i execute the procedure, i have this error:
Msg 3931, Level 16, State 1, Procedure AddData0, Line 76
The current transaction cannot be committed and cannot be rolled back to a savepoint. Roll back the entire transaction.
Also when i try to insert some data that already exists, it is inserted with another ID, which means that IF NOT EXIST statement is not working.
Any help, please?
Try this version:
DECLARE #serviciuKey int
DECLARE #specializareKey int
BEGIN TRY
BEGIN TRAN
IF NOT EXISTS (SELECT denumire, moneda, pret FROM Serviciu where denumire=#denumire and moneda=#moneda and pret=#pret)
BEGIN
INSERT INTO Serviciu ( denumire, moneda, pret)
VALUES (#denumire, #moneda, #pret)
END
SET #serviciuKey=scope_identity()
IF NOT EXISTS (SELECT denumire, descriere FROM Specializare where denumire=#denumire_spec AND descriere=#descriere)
BEGIN
INSERT INTO Specializare( denumire, descriere)
VALUES (#denumire_spec, #descriere)
END
SET #specializareKey=scope_identity()
IF NOT EXISTS (SELECT * FROM Specializare_Serviciu where cod_specializare=#specializareKey and cod_serviciu=#serviciuKey)
BEGIN
INSERT INTO Specializare_Serviciu( cod_specializare, cod_serviciu)
VALUES (#specializareKey, #serviciuKey)
END
COMMIT TRAN
END TRY
BEGIN CATCH
IF ##trancount > 0
ROLLBACK TRAN
DECLARE #msg nvarchar(2048) = error_message()
RAISERROR (#msg, 16, 1)
RETURN 55555
END CATCH
In Sql Server 2008 R2, I am creating a procedure with multiple transactions in it. Try..Catch Block is used for each transaction. I use output parameter to catch the error code and message since it will be caught by the main program. But When there is errors the output parameter are not correct set to the error message and code, what is problem here?
create procedure xxx (#P_Return_Status VARCHAR(1) OUTPUT, #P_Error_Code INT OUTPUT,)
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION TR1
.....
COMMIT TRANSACTIOn TR1
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION TR1
END
Set #P_Error_Code = Error_Number();
Set #P_Error_Messages = LEFT(ERROR_MESSAGE (), 2000)
END CATCH
BEGIN TRY
BEGIN TRANSACTION TR2
.....
COMMIT TRANSACTIOn TR2
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION TR2
END
Set #P_Error_Code = Error_Number();
Set #P_Error_Messages = LEFT(ERROR_MESSAGE (), 2000)
END CATCH
END
GO
Any help would be really appreciated!
I just don't see the point of putting two transaction inside this one procedure, just put all of the statements in one transaction and commit it or rollback it.
if it does need to be in separate transactions put these both try...catch in two separate procedures and call one sp from another sp's try block.
create procedure xxx
#P_Return_Status INT OUTPUT
,#P_Error_Code INT OUTPUT
,#P_Error_Messages VARCHAR(2000) OUTPUT
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION TR1
/* put statements from both transaction here
or
statements for TR1
AND
call the procedure containing code for TR2
*/
COMMIT TRANSACTIOn TR1
SET #P_Return_Status = 1;
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION TR1
END
Set #P_Error_Code = Error_Number();
Set #P_Error_Messages = LEFT(ERROR_MESSAGE (), 2000)
SET #P_Return_Status = 0;
END CATCH
END
GO
I have this procedure to delete a row from table
CREATE PROCEDURE [dbo].[spConfiguration_test]
#ID int
AS
Declare #ERR int
set #ERR = 0
DECLARE #Rows int
SET #Rows =0
begin tran
delete from de where ClaimVersion=5
set #ERR = ##Error
SELECT #Rows= #Rows + ##ROWCOUNT;
if #ERR = 0 commit tran
else rollback tran
I want it return a value like 1 or 2 or 3 depending on the number of rows deleted
but it gives me (1 row(s) affected)
what changes do I have to make?
Note that the sp prefix on stored procedure names should be left for Microsoft's use.
If you want to have access to the flag that indicates whether or not rows were deleted then you probably want to add it as an output parameter.
Using a transaction to wrap a single DELETE statement isn't going to do much. I've left the transaction code in on the assumption that this is part of a larger procedure.
If you do have multiple steps within the transaction you may want to use RaIsError to indicate to the caller where a problem occurred and provide some application specific context, e.g. the arguments passed to the procedure.
You can also use try/catch in a stored procedure. Sometimes handy, sometimes clumsy depending on what sorts of operations you need to perform and how much you need to know about exceptions.
create procedure [dbo].[Configuration_test]
#Id Int, -- Unused.
#Deleted Int Output -- Flag: 1 if any rows are deleted, 0 otherwise.
as
set nocount on
declare #Err as Int = 0;
set #Deleted = 0;
begin transaction;
delete
from de
where ClaimVersion = 5;
select #Deleted = case when ##RowCount > 0 then 1 else 0 end,
#Err = ##Error;
if #Err != 0
begin
rollback transaction;
return;
end;
-- Next operation...
set #Err = ##Error;
if #Err != 0
begin
rollback transaction;
RaIsError( 'Boo boo in step 42 processing Id %d.', 13, 42, #Id );
end;
-- Repeat operation/error check...
commit transaction;
Move the reference to ##RowCount immediately after the delete. Or, combine the two assignments in one statement:
delete from de where ClaimVersion = 5 ;
select #Rows = #Rows + ##ROWCOUNT, #ERR = ##Error;
The issue you are facing is that ##RowCount returns the number of rows affected by the previous statement.
In your case the previous statement is: set #ERR = ##Error
Try this instead
DELETE
FROM de
WHERE ClaimVersion = 5
;
SELECT #ERR = ##Error
, #Rows = ##RowCount
;
PRINT #Rows;
P.S. don't prefix your procedure names with things like "sp". It's as bad as prefixing all your tables with "tbl".
You can also SET NOCOUNT OFF.
Remarks
When SET NOCOUNT is ON, the count (indicating the number of rows affected by a Transact-SQL statement) is not returned. When SET NOCOUNT is OFF, the count is returned.
CREATE PROCEDURE [dbo].[spConfiguration_test]
#ID int
AS
Declare #ERR int
set #ERR = 0
SET NOCOUNT OFF
begin tran
delete from de where ClaimVersion=5
set #ERR = ##Error
if #ERR = 0 commit tran
else rollback tran
After SP Creation execute the SP As follows :
EXEC [dbo].[spConfiguration_test]
#ID = 0
Hello I got some stored procedures to create products and other stuff on my site. Now I have to run some of them in a transaction. Is that possible or do I have to make a stored procedure only for the transaction?
Can I say something like
BEGIN TRAN
"1. stored procedure"
"2. stored procedure"
COMMIT
To add to the other answers above, you may want to add some error handling:
BEGIN TRAN
BEGIN TRY
EXEC P1
EXEC P2
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
END CATCH
Update with C# code (I personally find it a lot easier to keep trans code out of the sprocs and in the data layer - makes composing stored procedures easier at a later stage):
using (var conn = new SqlConnection(...))
trans = conn.BeginTransaction();
try
{
...call P1 using transaction
...call P2 using transaction
trans.Commit();
}
catch
{
trans.RollBack();
throw;
}
}
Yes, a stored procedure can be run inside a transaction. Please find below a sample query.
create table temp1
(
id int,
name varchar(20)
)
create table temp2
(
id int,
name varchar(20)
)
go
create proc p1 as
insert temp1 values (1, 'test1')
create proc p2 as
insert temp2 values (1, 'test2')
go
begin tran tx
exec p1
exec p2
commit
From SQL Server (not sure about other RDBMS), You can call multiple stored procedures inside a transaction.
BEGIN TRAN
EXEC StoredProc1
EXEC StoredProc2
COMMIT TRAN
You may want to add a return code to the stored proc to check if you should run stored proc 2 if stored proc 1 failed
EDIT:
To check a return code you can do something like the following. This will run the first stored proc. If it returns 0 then it runs the 2nd. If the 2nd returns 0 then it commits the transaction. If either returns non-0 then it will rollback the transaction
DECLARE #ReturnValue INT
BEGIN TRAN
EXEC #ReturnValue = StoredProc1
IF #ReturnValue = 0
BEGIN
EXEC #ReturnValue = StoredProc2
IF #ReturnValue = 0
BEGIN
COMMIT
END
ELSE
BEGIN
ROLLBACK
END
END
ELSE
BEGIN
ROLLBACK
END
Begin TRAN
BEGIN TRY
-- your Action
Commit TRAN
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRAN
END
END CATCH