How to delete an hierarchy tree in SQL - sql

I have built an hierarchy and I'm able to insert, move, but I having problems with deleting.
I created this stored procedure to delete an Hierarchy Tree.
I call the procedure using exec [tag].[usp_TagDeleteHierarchyTree] 45 but the records are being displayed instead of being deleted.
Sample data
In the messages I get:
CREATE PROCEDURE [tag].[usp_TagDeleteHierarchyTree]
#TagId float
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ParentNode hierarchyid
Begin Try
Begin Transaction
-- Parent node
Select #ParentNode = [Node] From [tag].[Process] Where [Tag_Id] = #TagId
-- Delete records
Delete From [tag].[Process] Where [Node].IsDescendantOf(#ParentNode) = 1
Commit Transaction
End Try
Begin Catch
Rollback Transaction
Return ERROR_MESSAGE()
End Catch
END
I can't figure out what is wrong, any idea?
Regards
Elio Fernandes

Related

Using multiple relating statements inside a stored procedure?

I am using TSQL. I found this stored procedure template in the internet.
I want to first create a table A and then create from table a the table C. Would this work?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE <Procedure_Name, sysname, ProcedureName>
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRAN
SELECT Name into table_A From table1
COMMIT TRAN
BEGIN TRAN
SELECT Name into table_C From table_A
COMMIT TRAN
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRAN
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT #ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR(#ErrorMessage, #ErrorSeverity, #ErrorState );
END CATCH
END
GO
FIRST - there is no need to write BEGIN TRANSACTION and COMMIT arround all SQL Statement that manipulate data or structures inside the database. SQL Server run in autocommit mode, which mean that every SQL statement (INSERT, DELETE, UPDATE, CREATE, ALTER...) has it own transacion scope. If you want to have a transaction involving many statement use only one transaction...
SECOND - The syntax of RAISERROR does not accept parameters for then 2nd and 3rd argument as shown in the doc (just press F1 on the command when in SSMS) :
RAISERROR ( { msg_id | msg_str | #local_variable }
{ ,severity ,state }
[ ,argument [ ,...n ] ] )
[ WITH option [ ,...n
As you see, only the message can be argued as a local variable (it is why you get the error...)
Severity must be 16 which is the reserved class of developper error raising.
1 is the default state, but this is no more used...
THIRD - because the transaction is not systematically began whe entering in the CATCH part, you need to test if the transaction is alive or not with the XACT_STATE() function
FOURTH - it is preferable to use THROW instead of RAISERROR as the doc says...
FITH - Instead of writing separates command DECLARE and a SELECT for assignement, you can declare and set simultaneously
SIXTH - terminate all your TSQL sentences with a semicolon
Well now you have the code :
CREATE PROCEDURE <Procedure_Name, sysname, ProcedureName>
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
SELECT Name into table_A From table1;
SELECT Name into table_C From table_A;
COMMIT TRAN;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK TRAN;
DECLARE #ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
THROW 55555, #ErrorMessage, 1;
END CATCH
END
GO
It will definitely work. Stored Procedure allows you to have multiple statement (create table, insert, update, index) and many more.
The best way to handle exceptions in SQL is, oddly enough, to not handle them at all.
If you catch the exception, and there are multiple messages, you lose that info. You also prevent the exception from bubbling up properly to the client. It only looks like a strange SELECT.
Furthermore, you can't catch all exceptions anyway. There are a number of fatal exceptions which are un-catchable.
So the best thing to do is to just use XACT_ABORT ON to ensure that any transactions are always rolled back and not left hanging. Note that not every query needs a transaction, only if you want to do multiple things atomically.
You should also use NOCOUNT ON for performance reasons
CREATE OR ALTER Procedure_Name
AS
SET XACT_ABORT, NOCOUNT ON;
SELECT 'Something';
GO
CREATE OR ALTER Procedure_Name
AS
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRAN;
UPDATE SomeTable....;
INSERT SomeTable....;
COMMIT;
GO

Create SQL Server procedure in a transaction

I need to create two procedures in a SQL Server transaction. If failure, I need to rollback the create(s) and any other executed queries in this transaction. I know the create statement must be the first statement in query batch, but I need to know how handle the transaction with multiple batches.
BEGIN TRANSACTION
CREATE PROCEDURE [dbo].[SP_SP-1]
#id BIGINT
AS
BEGIN
SET NOCOUNT ON;
-- SQL statements
END
GO
CREATE PROCEDURE [dbo].[SP_SP-2]
#id BIGINT
AS
BEGIN
SET NOCOUNT ON;
-- SP-2 statements
END
GO
UPDATE Table
SET Value = '1.0.0.5'
COMMIT TRANSACTION / ROLLBACK TRANSACTION
Below is one method to execute multiple batches in a transaction. This uses a temp table to indicate if any batch erred and perform a final COMMIT or ROLLLBACK accordingly.
Another method is to encapsulate statements that must be in single-statement batch (CREATE PROCEDURE, CREATE VIEW, etc.) but that can get rather ugly when quotes within the literal text must be escaped.
CREATE TABLE #errors (error varchar(5));
GO
BEGIN TRANSACTION
GO
CREATE PROCEDURE [dbo].[USP_SP-1]
#id bigint
AS
BEGIN
SET NOCOUNT ON;
-- SP Statments
END;
GO
IF ##ERROR <> 0 INSERT INTO #errors VALUES('error');
GO
CREATE PROCEDURE [dbo].[USP_SP-2]
#id BIGINT
AS
BEGIN
SET NOCOUNT ON;
-- SP-2 Statments
END;
GO
IF ##ERROR <> 0 INSERT INTO #errors VALUES('error');
GO
UPDATE Table SET Value='1.0.0.5'
GO
IF ##ERROR <> 0 INSERT INTO #errors VALUES('error');
GO
IF EXISTS(SELECT 1 FROM #errors)
BEGIN
IF ##TRANCOUNT > 0 ROLLBACK;
END
ELSE
BEGIN
IF ##TRANCOUNT > 0 COMMIT;
END;
GO
IF OBJECT_ID(N'tempdb..#errors', 'U') IS NOT NULL
DROP TABLE #errors;
GO
I suggest you to study more about this subject in Handling Transactions in Nested SQL Server Stored Procedures.
From the beginning, your syntax is wrong. You cannot begin a transaction and then create a procedure, you need to do just the opposite:
CREATE PROCEDURE [dbo].[SP_SP-1]
#id bigint
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
SET NOCOUNT ON;
-- SP-2 Statments
Update Table set Value='1.0.0.5'
END TRY
BEGIN CATCH
--handle error and perform rollback
ROLLBACK
SELECT ERROR_NUMBER() AS ErrorNumber
SELECT ERROR_MESSAGE() AS ErrorMessage
END CATCH
END
It is best practice to use TRY and CATCH when attempting to perform update inside transaction scope.
Please read more and investigate using the link I provided to get a bigger picture.
To use Transaction, you need to know what is the meaning of transaction. It's meaning of 'Unit of work either in commit state or rollback state'.
So when you use transaction, you must know that where you declare and where you close. So you must start and end transaction in the parent procedure only than it will work as a unit of work i.e. whatever no of query execute of DML statement, it uses the same transaction.
I do not understand why your update statement outside of procedure and transaction portion too.
It should be (See my comments, you can use TRY Catch same as c sharp) :
Create PROCEDURE [dbo].[SP_SP-1]
#id bigint
AS
BEGIN
Begin Transaction
SET NOCOUNT ON;
-- SP Statments
Exec SP_SP-2 #id --here you can pass the parameter to another procedure, but do not use transaction in another procedure, other wise it will create another transaction
If ##Error > 0 than
Rollback
Else
Commit
End
END
GO
--Do not use transaction in another procedure, otherwise, it will create another transaction which has own rollback and commit and do not participate in the parent transaction
Create PROCEDURE [dbo].[SP_SP-2]
#id BIGINT
AS
BEGIN
SET NOCOUNT ON;
-- SP-2 Statments
END
GO
i find this solution to execute the procedure as string execution , it`s a workaround to execute what i want
Begin Try
Begin Transaction
EXEC ('
Create PROCEDURE [dbo].[SP_1]
#id bigint
AS
BEGIN
SET NOCOUNT ON;
SP-1
END
GO
Create PROCEDURE [dbo].[SP_Inc_Discovery_RunDoc]
#id bigint
AS
BEGIN
SET NOCOUNT ON;
Sp-2
END')
Update Table set Value='1.0.0.5'
Commit
End Try
Begin Catch
Rollback
Declare #Msg nvarchar(max)
Select #Msg=Error_Message();
RaisError('Error Occured: %s', 20, 101,#Msg) With Log;
End Catch

stored procedure sql server 2008

I am working on a project and am using stored procedure. I'm getting this error:
Line: 939
Error: Sys.WebForms.PageRequestManagerServerErrorException: Invalid object name 'IT_Assets'.
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
Please find below my stored procedure code:
alter PROCEDURE [ITAssets_sp_IT_Assets]
-- Add the parameters for the stored procedure here
(#Mode varchar(12)='ADD',
#ID integer , #AssetCode nvarchar(20)=null, #Description nvarchar(70)=null,
#Site nvarchar(10)=null)
AS
Begin
IF #Mode='ADD'
Begin
Begin Tran
INSERT INTO [IT_Assets]
([ID]
,[AssetCode]
,[Description]
,[Site])
values
(#ID, #AssetCode, #Description, #Site
)
If ##ERROR <> 0
ROLLBACK TRAN
Else
COMMIT TRAN
Select #ID
End
ELSE
Begin
Begin Tran
UPDATE [IT_Assets]
SET
AssetCode = #AssetCode, Description = #Description, Site = #Site
WHERE ID = #ID
If ##ERROR <> 0
ROLLBACK TRAN
Else
COMMIT TRAN
Select #ID
End
End
I didn't understand the error and I don't know exactly where is the problem? Would someone please help me in sloving this problem?
From the error Invalid object name 'IT_Assets' I believe that the table/view 'IT_Assets' is present in a diferent database than the stored procedure (assuming that the object exist and you are using the correct name).
Then you need to fully quaify it wih Db name like
UPDATE [DB_NAME].[dbo].[IT_Assets] (assuming `dbo` is the owner)
Try using your database name at top of procedure using use statement like
use [DB_NAME]
GO
alter PROCEDURE [ITAssets_sp_IT_Assets]
-- Add the parameters for the stored procedure here
(#Mode varchar(12)='ADD',
...
Also change you transaction handling using TRY .. CATCH consruct like below
Begin Tran
BEGIN TRY
INSERT INTO [IT_Assets]
([ID]
,[AssetCode]
,[Description]
,[Site])
values
(#ID, #AssetCode, #Description, #Site);
COMMIT TRAN;
END TRY
BEGIN CATCH
ROLLBACK TRAN ;
END CATCH

Begin Transaction ... Commit Transaction Issue

i have a question regarding using Transaction. Consider this code:
declare #trans_name varchar(max) = 'Append'
begin tran #trans_name
insert into SIDB_Module
(module_name, module_description, modulelevel, parentid, issystem, iscurrent)
values
(#module_name, #module_description, #modulelevel, #parentid, #issystem, 1)
set #moduleid = SCOPE_IDENTITY()
declare #id int = OBJECT_ID('SIDB_Module')
exec usp_M_SIDB_TransactionInformation_App_Append
#moduleid, id, 'append' ,#createdby_userid
if ##ERROR <> 0
rollback tran #trans_name
commit tran #trans_name
does transaction still apply on this.. even the next insert query is on the other stored procedure??
Yes, the call to usp_M_SIDB_TransactionInformation_App_Append is part of the transaction
Note: your error handling is "old style" (using ##ERROR) from SQL Server 2000 and will generate errors (error 266) if the inner proc rolls back or commits.
For more, see Nested stored procedures containing TRY CATCH ROLLBACK pattern?

MSSQL Prevent rollback when trigger fails

I have an after insert/update/delete trigger, which inserts a new record in an AuditTable every time an insert/update/delete is made to a specific table. If the insertion in the AuditTable fails I'd like the first record to be inserted anyway and the error logged in a further table "AuditErrors".
This is what I have so far and I tried many different things but I can't get this to work if the trigger insert into the AuditTable fails (I test this by misspelling the name of a column in the AuditTable insert). NB: #sql is the insert into the AuditTable.
DECLARE #TranCounter INT
SET #TranCounter = ##TRANCOUNT
IF #TranCounter > 0
SAVE TRANSACTION AuditInsert;
ELSE
BEGIN TRANSACTION;
BEGIN TRY
EXEC (#sql)
IF #TranCounter = 0
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- roll back
IF #TranCounter = 0
ROLLBACK TRANSACTION;
ELSE
IF XACT_STATE() <> -1
ROLLBACK TRANSACTION AuditInsert;
-- insert error into database
IF #TranCounter > 0
SAVE TRANSACTION AuditInsert;
ELSE
BEGIN TRANSACTION;
BEGIN TRY
INSERT INTO [dbo].[AuditErrors] ([AuditErrorCode], [AuditErrorMsg]) VALUES (ERROR_NUMBER(), ERROR_MESSAGE())
IF #TranCounter = 0
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- roll back
IF #TranCounter = 0
ROLLBACK TRANSACTION;
ELSE
IF XACT_STATE() <> -1
ROLLBACK TRANSACTION AuditInsert;
END CATCH
END CATCH
This is the only way I know of separating the original transaction from the trigger action. In this example the original insert completes even though the audit insert fails. Tested on 2008R2.
It's not pretty but it won't rollback the transaction!
It worked just fine with trusted authentication:
create table TestTable(
ID int identity(1,1) not null
,Info varchar(50) not null
)
GO
create table AuditTable(
AuditID int identity(1,1) not null
,TestTableID int not null
,Info varchar(10) -- The failure is the mismatch in length
)
GO
create procedure insertAudit #id int, #Info varchar(50)
as
set nocount on;
begin try
insert into AuditTable(TestTableID,Info)
values(#id,#Info);
end try
begin catch
select 0
end catch;
GO
create trigger trg_TestTable on TestTable
AFTER INSERT
as
begin
set nocount on;
declare #id int,
#info varchar(50),
#cmd varchar(500),
#rc int;
select #id=ID,#info=Info from inserted;
select #cmd = 'osql -S '+##SERVERNAME+' -E -d '+DB_NAME()+' -Q "exec insertAudit #id='+cast(#id as varchar(20))+',#Info='''+#info+'''"';
begin try
exec #rc=sys.xp_cmdshell #cmd
select #rc;
end try
begin catch
select 0;
end catch;
end
GO
Drop the Audit table and it still completes the original transaction.
Cheers!
Instead of using sqlcmd, you may consider playing with BEGIN TRAN/ROLLBACK a little bit.
Note that, even tho a rollback command will undo every change made since the start of the statement which caused the trigger to fire, any changes made by subsequent commands will not.
All you have to do is to repeat the execution of the code in #sql if the transaction in which data is inserted in the audit table gets rolled back:
TRIGGER BEGINS
<INSERT INSERTED AND DELETED TABLES INTO TABLE VARIABLES, U'LL NEED THEM>
BEGIN TRY
BEGIN TRAN
INSERT INTO AUDITTABLE SELECT * FROM #INSERTED
COMMIT
END TRY
BEGIN CATCH
ROLLBACK
REDO ORIGINAL INSERT/UPDATE/DELETE USING TRIGGER TABLE VARIABLES (#INSERTED AND #DELETED)
INSERT INTO AUDITERROS...
END CATCH
BEGIN TRAN -- THIS IS TO FOOL SQL INTO THINKING THERE'S STILL A TRANSACTION OPEN
TRIGGER ENDS