This is strange but my SQL Server does not delete items:
I have a stored procedure like this:
Create Procedure DeleteItem #cnm nchar(10)
as
Begin Transaction
Delete From T1 where Cnm = #cnm
Delete From T2 where Cnm = #cnm
if ##ERROR <> 0
Begin
Commit
End
Else
Begin
RollBack
End
and when I run this query in SQL Server Management Studio:
Exec DeleteItem '1111111111'
it returns in the Messages pane:
(1 row(s) affected)
(1 row(s) affected)
but when I get data from that tables the deleted record still exist!
Am I doing anything wrong?
##ERROR returns 0 if the previous Transact-SQL statement encountered no errors.
Change your IF condition
if ##ERROR = 0
Commit
else
....
Related
I have an Executed SQL Task with SQL query to delete the data from table. I am using while loop to delete the data in batch and try/catch to handle the failure.
In Execute SQL Task I want to return the deleted record, so I added output parameters which is capturing the deleted record. But the problem I am having is, I am not able to capture/return the deleted record (which is already committed) in case of failure.
I am using below SQL logic
Declare #DeletedRows INT = 0
BEGIN TRY
BEGIN TRANSACTION
While (#deletedrows < #rowstodelete)
BEGIN
Delete records where condition is match
SET #deletedrows = #deletedrows + ##rowcount
END
COMMIT TRANSACTION
SET ? = #deletedrows ---- returning deleted rows to output parameter of execute sql task
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
RAISERROR (CAPTURE ERRORS);
END CATCH
Any idea how can I return already deleted or committed rows to output parameter of Execute SQL Task ?
First, you need to update the spellings of COMMIT in your SQL Script. And then you need to do 'SELECT ? = #DeletedRows' at the end of your SQL Script in Execute SQL Task and change the Parameter Name value to 0 in Parameter Mapping screen. This should give you correct result.
I am using MS SQL Server 2016 where I have implemented a instead of delete trigger. It looks like this:
ALTER TRIGGER MyTrigger ON MyTable INSTEAD OF DELETE AS
BEGIN
IF --some condition
BEGIN
RAISERROR ('Error msg', 16, 1)
ROLLBACK TRAN
RETURN
END
DELETE MyTable FROM MyTable JOIN deleted ON MyTable.id = deleted.id
END
If I execute a DELETE statement on the table 'MyTable' and the condition in the if is not fulfilled the DELETE statement is executed after the if-block. This is absolutely correct. But in the console of SSMS it is written twice that the DELETE statement was executed. So the following is written in the console:
(1 rows affected)
(1 rows affected)
I do not understand why. Why does SSMS indicate twice that a row is affected? I use SSMS version 15.0.18338.0.
This is because there were 2 sets of data effect, the set outside the TRIGGER, and then again inside it, because the initial dataset doesn't perform the DML operation itself. If you don't want to see the latter count, turn NOCOUNT to ON. This, of course, means that if fewer rows are effected in your TRIGGER, you won't know about it in the output from SSMS (but it's just informational anyway).
It is also heavily advised that you don't use ROLLBACK inside a TRIGGER, handle transactions outside the TRIGGER, not inside. RAISERROR isn't recommend either and you should be using THROW for new development work (that's been recommended since 2012!). This results in a TRIGGER like below:
CREATE OR ALTER TRIGGER MyTrigger ON dbo.MyTable INSTEAD OF DELETE AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (SELECT 1 FROM deleted WHERE SomeVal = 'Nonsense')
THROW 95302, N'Error Msg', 16; --Use an error number appropriate for you
ELSE
DELETE MT FROM dbo.MyTable MT JOIN deleted ON MT.id = deleted.id;
END;
GO
Recently, I encountered an SQL error due to the following statement in our legacy code. It tries to drop a temp table, and move on if it hasn't been defined. Clearly, it's a bad way to check the existence of a temp table, but that's not my question here.
BEGIN TRY
DROP TABLE #my_temp_table
END TRY
BEGIN CATCH
END CATCH
The statement runs fine (without any error) as is, but as soon as you put it in a Begin Tran/Commit Tran block like the following, the behavior becomes interesting.
BEGIN TRAN
BEGIN TRY
DROP TABLE #my_temp_table
END TRY
BEGIN CATCH
END CATCH
COMMIT TRAN
My understanding is that Try..Catch block doesn't affect Transactions - once it goes into the Catch block, the transaction will be in an uncommittable state, and the transaction will be rolled back, and that's what I see on SQL Server 2008 R2 (SP1) - 10.50.2550.0. When it's executed inside a Begin Tran/Commit Tran block, we will get an error:
Msg 3930, Level 16, State 1, Line 8
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
Msg 3998, Level 16, State 1, Line 1
Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.
However, it runs without any error on SQL Server 2012 - 11.0.5058.0. The XACT_STATE() returns 1 after the END CATCH line. The transaction will be committed, and if there are other data changes before and after the DROP TABLE statement, the changes will stay.
BEGIN TRAN
BEGIN TRY
DROP TABLE #my_temp_table
END TRY
BEGIN CATCH
END CATCH
PRINT XACT_STATE()
COMMIT TRAN
In all these tests, I've made sure XACT_ABORT is OFF. So my question is what will cause this behavior difference. Is it truly a different between 2008 R2 and 2012, or it's some server/DB settings controlling how Try...Catch block and Transaction work.
Edit 1: I tried to run the following script on both 2008 R2 and 2012 instances. I also tried putting the INSERT dbo.UserOptionsLog line in different places, before the Begin Tran, after the Begin Try, after the Begin Catch, after the the commit tran, but it doesn't change the results.
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'UserOptionsLog')
CREATE TABLE dbo.UserOptionsLog([Set Option] SYSNAME, [Value] VARCHAR(100), ID INT IDENTITY NOT NULL)
BEGIN TRAN
DELETE FROM dbo.UserOptionsLog
INSERT dbo.UserOptionsLog EXEC('DBCC USEROPTIONS')
SELECT * FROM dbo.UserOptionsLog
BEGIN TRY
DROP TABLE #my_temp_table
END TRY
BEGIN CATCH
END CATCH
COMMIT TRAN
Results from 2008 R2 instance.
Set Option Value ID
textsize 2147483647 40
language us_english 41
dateformat mdy 42
datefirst 7 43
lock_timeout -1 44
quoted_identifier SET 45
arithabort SET 46
ansi_null_dflt_on SET 47
ansi_warnings SET 48
ansi_padding SET 49
ansi_nulls SET 50
concat_null_yields_null SET 51
isolation level read committed 52
Messages from 2008 R2 instance
(0 row(s) affected)
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
(13 row(s) affected)
(13 row(s) affected)
Msg 3930, Level 16, State 1, Line 18
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
Msg 3998, Level 16, State 1, Line 1
Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.
Results from 2012 instance.
Set Option Value ID
textsize 2147483647 53
language us_english 54
dateformat mdy 55
datefirst 7 56
lock_timeout -1 57
quoted_identifier SET 58
arithabort SET 59
ansi_null_dflt_on SET 60
ansi_warnings SET 61
ansi_padding SET 62
ansi_nulls SET 63
concat_null_yields_null SET 64
isolation level read committed 65
Messages from 2012 instance.
(13 row(s) affected)
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
(13 row(s) affected)
(13 row(s) affected)
[...]and that's what I see on SQL Server 2008 R2 (SP1) - 10.50.2550.0.
When it's executed inside a Begin Tran/Commit Tran block, we will get
an error:
Msg 3930, Level 16, State 1, Line 8 The current transaction cannot be
committed and cannot support operations that write to the log file.
Roll back the transaction. Msg 3998, Level 16, State 1, Line 1
Uncommittable transaction is detected at the end of the batch. The
transaction is rolled back. However, it runs without any error on SQL
Server 2012 - 11.0.5058.0. [...]
I believe the reason for this diff. behavior is the value of XACT_ABORT setting.
When OFF then XACT_STATE() returns 1, TX is commitable and COMMIT TRAN is executing without errors:
SET XACT_ABORT OFF
BEGIN TRAN
BEGIN TRY
DROP TABLE #my_temp_table
END TRY
BEGIN CATCH
END CATCH
PRINT XACT_STATE()
COMMIT TRAN
but when is ON
SET XACT_ABORT ON
BEGIN TRAN
BEGIN TRY
DROP TABLE #my_temp_table
END TRY
BEGIN CATCH
END CATCH
PRINT XACT_STATE()
COMMIT TRAN
because of error/exception intercepted by CATCH block, TX becomes domed/uncommitable (-1) and COMMIT TRAN raise another error/exception:
-1
Msg 3930, Level 16, State 1, Line 10
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
Update:
I reproduced reported behaviour on SQL2008R2. On 2008R2, it seems that no matter what value has XACT_ABORT (ON/OFF), TX becomes uncommitable. SQL2012 changed this behaviour thus: TX becomes uncommitable only when XACT_ABORT is ON.
I'm experiencing an issue with an access form. I use access 2013 forms as frontend, and sql server 2014 as backend. The form has a button that should delete a record, through use of a stored procedure. Though, when I select the record that should be deleted, and when I press the button, it seems to have worked. The stored procedure has to delete 1 record in 2 different tables, which are linked together with 'articlenr'.
I can debug through the entire process (except for the stored procedure). I'm guessing there's something wrong with my stored procedure:
ALTER PROCEDURE [dbo].[spArticleDelete]
(
#articlenr int
)
AS
BEGIN TRANSACTION
IF (
SELECT COUNT(*)
FROM Article
WHERE articlenr = #articlenr
) <> 0
BEGIN
DELETE FROM Articleprice WHERE articlenr = #articlenr
DELETE FROM Article WHERE articlenr = #articlenr
END
If ##ERROR <> 0
BEGIN
COMMIT TRANSACTION
Raiserror('The article has been deleted!', 16, 1)
END
ELSE
ROLLBACK
Raiserror('The article has not been deleted!', 16,1)
Hope you guys can help me out here..
You may want to change your code to somethis like this:
begin try
BEGIN TRANSACTION
DELETE FROM x2 where i1 = 5
DELETE FROM x2 where i1 = 6
commit
Raiserror('The article has been deleted!', 16, 1)
end try
begin catch
if ##TRANCOUNT > 0
begin
ROLLBACK
end
Raiserror('The article has not been deleted!', 16,1)
end catch
Perhaps I am missing something, but even though the RAISERRORs below have severity of 16 (as per documentation) the transaction is still committed as if XACT_ABORT ON has no effect.
CREATE PROCEDURE [ExploringGroups].[RemoveMember]
#groupId uniqueidentifier,
#adminUsername nvarchar(50),
#targetUsername nvarchar(50)
AS
SET XACT_ABORT ON
BEGIN TRANSACTION
DECLARE
#adminUserId uniqueidentifier = dbo.fn_userId(#adminUsername),
#targetUserId uniqueidentifier = dbo.fn_userId(#targetUsername)
IF #targetUserId IS NULL OR ExploringGroups.IsMember(#groupId, #targetUserId) = 0
RAISERROR('Target user was not located', 16, 1)
IF ExploringGroups.IsInRole(#groupId, #adminUserId, 'adm') = 0
RAISERROR('Specified user is not an administrator of this group', 16, 2)
IF #adminUserId = #targetUserId
RAISERROR('You cannot remove yourself', 16, 3)
-- statements below still execute and commit even though there was an error raised above
DELETE FROM ExploringGroups.MemberRole WHERE GroupId = #groupId AND UserId = #targetUserId
DELETE FROM ExploringGroups.Membership WHERE GroupId = #groupId AND UserId = #targetUserId
COMMIT
RETURN 0
Calling
exec exploringgroups.removemember '356048C5-BAB3-45C9-BE3C-A7227225DFDD', 'Crypton', 'Crypton'
Produces
Msg 50000, Level 16, State 2, Procedure RemoveMember, Line 20
Specified user is not an administrator of this group
Msg 50000, Level 16, State 3, Procedure RemoveMember, Line 24
You cannot remove yourself
I thought XACT_ABORT was supposed to roll back the whole transaction if it's set to ON?
Actually, it behaves exactly as it was supposed to. XACT_ABORT really caused the transaction to roll back, so were there any data modifications up to the point of the error they will be rolled back. However, it didn't affect the flow of execution, and it didn't stop running the stored procedure, so the following two DELETEs were executed as implicite transactions. Explicit RAISERRORs don't abort the batch.
See this simplified version:
create table #t(i int);
insert #t values(1);
go
alter procedure sp
as
set xact_abort on
begin tran
raiserror ('x', 16, 1);
print 'deleting';
delete #t;
commit;
go
exec sp
go
select * from #t
go
The only funny thing was that the error about COMMIT not having a corresponding BEGIN TRAN was swallowed.
With SEH, it would jump into CATCH block.
You should be using a "THROW" statement versus the RAISERROR approach.
Use a BEGIN TRY and BEGIN CATCH and commit the transaction normally or rollback in the CATCH block.
BEGIN TRY
-- Do your inserts or throw error
-- Commit transaction
END TRY
BEGIN CATCH
-- Rollback
END CATCH;