sql DELETE trigger won't rollback - sql

The trigger is supposed to rollback if the statement in the SELECT exists. The thing is, it doesn't. When I only run the SELECT from the trigger, it shows that a row exists. But when I try DELETE with the same values as I have hard coded in the trigger, the trigger fires but it does not do a rollback. Anyone got any ideas what might be wrong?
CREATE TRIGGER trg_del ON Projektbefattningar
FOR DELETE
AS
SELECT #befNr = Befattningsnr, #pNr = pNr, #EtappNr = Etappnr FROM deleted
-- Not currently using these. Using hard coded values to illustrate my problem
IF EXISTS (
SELECT *
FROM projektbefattningar
WHERE Befattningsnr = 2 AND pNr = 1 and Etappnr = 1 AND Anställningsnr is not null
)
BEGIN
RAISERROR('Could not delete, Anställningsnr is not null', 16, 1)
--THROW 50001, 'Could not delete, Anställningsnr is not null', 1;
ROLLBACK TRANSACTION;
END
GO

When you use FOR DELETE you consider that this TRIGGER will fired after the delete statement is complete. To Verify a condition before DELETE you need to use INSTEAD OF DELETE. Your Trigger declaration will be:
CREATE TRIGGER trg_del ON Projektbefattningar
INSTEAD OF DELETE
AS
Now To Confirm your delete statement you need to include the delete alghouth it will not be deleted.
The final procedure will be like this:
CREATE TRIGGER trg_del ON Projektbefattningar
INSTEAD OF DELETE
AS
SELECT #befNr = Befattningsnr, #pNr = pNr, #EtappNr = Etappnr FROM deleted
-- Not currently using these. Using hard coded values to illustrate my problem
IF EXISTS (
SELECT *
FROM projektbefattningar
WHERE Befattningsnr = 2 AND pNr = 1 and Etappnr = 1 AND Anställningsnr is not null
)
BEGIN
RAISERROR('Could not delete, Anställningsnr is not null', 16, 1)
--THROW 50001, 'Could not delete, Anställningsnr is not null', 1;
ROLLBACK TRANSACTION;
END
ELSE
BEGIN
DELETE FROM YourTableToDelete Where idOfYourTable in ( Select idOfYourTable from Deleted )
END
GO

Try this
CREATE TRIGGER trg_del ON Projektbefattningar
FOR DELETE
AS
SELECT #befNr = Befattningsnr
,#pNr = pNr
,#EtappNr = Etappnr
FROM deleted
-- Not currently using these. Using hard coded values to illustrate my problem
IF EXISTS (
SELECT 1
FROM deleted
WHERE Befattningsnr = 2
AND pNr = 1
AND Etappnr = 1
AND Anställningsnr IS NOT NULL
)
BEGIN
RAISERROR (
'Could not delete, Anställningsnr is not null'
,16
,1
)
--THROW 50001, 'Could not delete, Anställningsnr is not null', 1;
ROLLBACK TRANSACTION;
END
GO

Related

SQL Trigger prevent delete on single row

I have a table where there is a row of data that I don't want anyone to deleted it. The Table name is ProjectInProcessData. I want to make sure the data with ID 6050 can not delete. How can I do that?
Create Trigger [dbo].[triggerPreventDeleteFormula]
ON [dbo].[AHSC_Project_InprocessData]
INSTEAD OF DELETE
AS
BEGIN
IF EXISTS(
SELECT *
FROM deleted d
where d.AHSC_Project_InprocessData_ID = '6610' or d.AHSC_Project_InprocessData_ID = '6666'
)
BEGIN
ROLLBACK;
RAISERROR('Can not delete this record: this record contains default formula',16,1);
END
ELSE
BEGIN
DELETE [AHSC_Project_InprocessData]
WHERE EXISTS (Select * from deleted d where d.AHSC_Project_InprocessData_ID = [AHSC_Project_InprocessData].AHSC_Project_InprocessData_ID)
END
END
use where condition in times of delete
delete from ProjectInProcessData where id!=6050

Stored procedure for referential integrity between two tables in different database?

For two tables A and B, I'd like to implement referential integrity such that in tables A a foreign key's value must present in table B, while in table B a primary key can only be deleted or modified if that value does not present in table A. My requirement is that I'd like to have table A, and B as variable, and apply the procedure to any arbitrary instances of tables. That is,
sp_referential_integrity_across_databases(A, B)
I have figured out how to do the referential integrity as triggers for a pair of particular tables. I wonder if it's feasible to write such stored procedure to save future effort?
My environment is Microsoft SQL Server 2017. The more portable the solution, the better.
Here are my crafted procedures:
The triggers on table "A" for insert and update:
USE DWPractice
IF OBJECT_ID ('dbo.trgCheckCustomer_Cat_Id_Customer_D', 'TR') IS NOT NULL
DROP Trigger trgCheckCustomer_Cat_Id_Customer_D;
GO
CREATE TRIGGER trgCheckCustomer_Cat_Id_Customer_D
ON Customer_D
AFTER INSERT, UPDATE
AS
IF NOT EXISTS
(
SELECT Customer_Cat_Id
FROM inserted
WHERE Customer_Cat_Id IN (SELECT Customer_Cat_Id FROM [OtherDW].[dbo].[Customer_Cat_D])
)
BEGIN
RAISERROR('Lookup Value Not Found -- Inerst Failed', 16, 1);
ROLLBACK TRANSACTION;
END;
The trigger on table "B" for delete and update:
USE OtherDW
IF OBJECT_ID ('dbo.trgCheckCustomer_Cat_Id_Customer_Cat_D', 'TR') IS NOT NULL
DROP Trigger trgCheckCustomer_Cat_Id_Customer_Cat_D;
GO
CREATE TRIGGER trgCheckCustomer_Cat_Id_Customer_Cat_D
ON Customer_Cat_D
AFTER DELETE, UPDATE
AS
Begin
IF EXISTS
(
SELECT Customer_Cat_Id
FROM deleted
WHERE Customer_Cat_Id IN (SELECT Customer_Cat_Id FROM [DWPractice].[dbo].[Customer_D])
)
BEGIN
RAISERROR('Lookup Value Found -- Delete Failed', 16, 1);
ROLLBACK TRANSACTION;
END;
-- It seems that the following for the case of update is not needed
-- The above clauses would get executed even for the case of update.
-- IF EXISTS
-- (
-- SELECT Customer_Cat_Id
-- FROM inserted
-- WHERE Customer_Cat_Id IN (SELECT Customer_Cat_Id FROM [DWPractice].[dbo].[Customer_D])
-- )
-- BEGIN
-- RAISERROR('Lookup Value Found -- Update Failed', 16, 1);
-- ROLLBACK TRANSACTION;
-- END;
End;
If stored procedure is not the best practice, then what is the best practice? It seems to me, there's much boiler-plate code, with only database name and table name are variables.
The logic in your (first) trigger is not correct. If you have multiple rows in inserted, then only one has to match. Instead, you want:
CREATE TRIGGER trgCheckCustomer_Cat_Id_Customer_D ON Customer_D AFTER INSERT, UPDATE
AS BEGIN
IF EXISTS (SELECT 1
FROM inserted i LEFT JOIN
[OtherDW].[dbo].[Customer_Cat_D] d
ON i.Customer_Cat_Id = d.Customer_Cat_Id
WHERE d.Customer_Cat_Id IS NULL
)
BEGIN
RAISERROR('Lookup Value Not Found -- Insert Failed', 16, 1);
ROLLBACK TRANSACTION;
END;
END; -- trigger

Sql Server Triggers. How does the trigger checks for Update Command?

I have a trigger for auditing purchase and sales table. The trigger is on INSERT and DELETE. To check if its "Insert", the condition is
IF (SELECT COUNT(*) FROM INSERTED) > 0
BEGIN
END
How do I check if its an Update command inside the Trigger?
Arun
The "tables" that are in play in an update trigger are still called inserted and deleted, and the old values of the rows are in the deleted table, and the new values are in the inserted table.
You can use logic along these lines to detect whether you are in a insert, update or a delete:
CREATE TRIGGER MyTrigger
ON MyTable
AFTER INSERT,DELETE,UPDATE -- you need to add the "update" here,
-- in order to catch updates as well
AS
BEGIN
Declare #insertedCount int
Declare #deletedCount int
select #insertedCount = COUNT(*) from inserted
select #deletedCount = COUNT(*) from deleted
if (#insertedCount != 0) -- we have some new values
if (#deletedCount = 0) -- do we have old values?
print 'Inserting'
else
print 'Updating'
else
print 'Deleting'
END
An INSERT,DELETE trigger will not fire for updates.
.........................
CREATE an INSERT, UPDATE, DELETE all in one table
IF ##ROWCOUNT = 0
BEGIN
RETURN;
END;
IF EXISTS(SELECT * FROM inserted)
BEGIN
IF EXISTS(SELECT * FROM deleted)
BEGIN
-- for UPDATE
END
ELSE
BEGIN
-- for INSERT
END
END
ELSE
BEGIN
-- for DELETE
END;
Once you've updated your trigger (as Mitch says) to also apply to UPDATEs, you need to check for updates first:
IF EXISTS(select * from inserted) and EXISTS(select * from deleted)
BEGIN
--Update
END
Note that I've switch to EXISTS rather than COUNT(*). Exists more properly describes what we want to establish - that rows exist in the table. We don't care how many rows are in there.
I would like to thank "SWeko" for the clue.
Now, this what I did and the Trigger worked.
CREATE TRIGGER dbo.Sample
ON TableName
FOR INSERT, UPDATE, DELETE
AS
IF (SELECT COUNT(*) FROM INSERTED) > 0
BEGIN
IF (SELECT COUNT(*) FROM DELETED) > 0
BEGIN
-- UPDATE (TABLE).
END
ELSE
BEGIN
-- INSERT (TABLE).
END
END
ELSE
-- DELETE (TABLE).
BEGIN
END

Trigger performing insert

I have to create a trigger that does not allow more than one null in a certain column (hourfinish). Here is the trigger I wrote (I am using Sybase):
ALTER TRIGGER "InsertIntoCardDetail" instead of insert on
DBA.CardDetail
referencing new as new_name
for each row
begin
declare #nullCount integer;
if(new_name.hourfinish is null) then
select COUNT(*) into #nullCount
from CardDetail
where hourfinish is null;
if(#nullCount > 0) then
raiserror 99999 'Cannot have Multiple Nulls'
else
insert into CardDetail( card,hourstart,hourfinish,"work",project,N)
values( new_name.card,new_name.hourstart,new_name.hourfinish,new_name."work",new_name.project,new_name.N)
end if
else
insert into CardDetail( card,hourstart,hourfinish,"work",project,N)
values( new_name.card,new_name.hourstart,new_name.hourfinish,new_name."work",new_name.project,new_name.N)
end if
end
The trigger works fine. What I am asking is if there is a command executing the insert - a command that can replace those long insert statements.
Try to change your trigger to:
ALTER TRIGGER "InsertIntoCardDetail" instead of insert on
DBA.CardDetail
referencing new as new_name
for each row
begin
if(new_name.hourfinish is null) then
if exists
(select 1
from CardDetail
where hourfinish is null) then
raiserror 99999 'Cannot have Multiple Nulls'
return 1
end if --exists
end if--(new_name.hourfinish is null)
insert into CardDetail( card,hourstart,hourfinish,"work",project,N)
values(new_name.card,new_name.hourstart,new_name.hourfinish,new_name."work",new_name.project,new_name.N)
end
I achieved the same result with a before insert trigger. This way i don't need to write any insert statement in the trigger.
ALTER TRIGGER "InsertIntoCardDetail" before insert on
DBA.CardDetail
referencing new as new_name
for each row
begin
if(new_name.hourfinish is null)
and exists(select 1
from CardDetail
where hourfinish is null) then
raiserror 99999 'Cannot have Multiple Nulls'
end if;
end

Create Database trigger for custom ChangeTable

I'm trying to create a database trigger in SQL Server 2008 for a tblCustomer table. I need it to add a new row into the tblChanges table every time there is an insert, update, or delete within the tblCustomer table.
Specifically, I need it to insert the CustomerId (PK) that was changed, the dateTime that it the changed occurred, and the type of change (insert, update, delete).
I've got something along these lines so far but can't figure out the rest:
CREATE TRIGGER change_trigger
AFTER INSERT OR UPDATE OR DELETE
ON tblCustomer
DECLARE log_action varchar(30)
BEGIN
IF INSERTING THEN
log_action := 'I';
ELSEIF UPDATING THEN
log_action := 'U';
ELSEIF DELETEING THEN
log_action := 'D';
ELSE
DBMS_OUTPUT.PUT_LINE('undefined');
END IF;
INSERT INTO tblChanges(ChanedPK, ChangedTime, ChangedType)
VALUES ...
I'm unsure if any of the above SQL is correct as I haven't tried running it yet and my knowledge of SQL is limited. Any help completing the code and correcting errors would be appreciated.
Try something along these lines:
CREATE TRIGGER change_trigger AFTER INSERT, UPDATE, DELETE
ON tblCustomer
AS
BEGIN
DECLARE log_action varchar(30)
IF EXISTS (SELECT * FROM INSERTED) AND NOT EXISTS(SELECT * FROM DELETED)
SET log_action = 'INSERT'
IF EXISTS NOT (SELECT * FROM INSERTED) AND EXISTS(SELECT * FROM DELETED)
SET log_action = 'DELETE'
IF EXISTS (SELECT * FROM INSERTED) AND EXISTS(SELECT * FROM DELETED)
SET log_action = 'UPDATE'
-- Your other code goes here
END
Maybe you should read up un T-SQL and DML triggers in SQL Server first. MSDN and many other sites provide excellent examples on how to go about things.