Trigger to delete prior duplicate records in the table - sql

I need to write a trigger based on the following condition
Before inserting a record in the table, I need to compare the value of one column to the existing records, and if records found then I need to delete those records having same column value in the already existing records, and then need to insert that new record.
Please let me know how to achieve this.
Thanks

CREATE TRIGGER [dbo].[CustomInsert_Trigger] ON [dbo].[Realtimebookingcount]
INSTEAD OF INSERT AS
BEGIN
DECLARE #Flag INT
SELECT #Flag = Booking_NUM FROM inserted
IF (SELECT COUNT(1) FROM Realtimebookingcount
WHERE Booking_NUM = #Flag) > 0
BEGIN
DELETE FROM Realtimebookingcount
WHERE Realtimebookingcount.Booking_NUM = #Flag
END
INSERT INTO Realtimebookingcount
SELECT * FROM inserted
END

Related

Prevent duplicate data in using After Insert Trigger

I can detect duplicate records, but when I'm inserting new data it will detect it as a duplicate record even if doesn't already exist.
Here is my code:
ALTER TRIGGER [dbo].[SDPRawInventory_Dup_Trigger]
ON [dbo].[SDPRawInventory]
AFTER INSERT
AS
DECLARE #Year float,
#Month float,
#SDPGroup nvarchar(255);
SELECT
#Year = i.InvYear, #Month = i.InvMonth, #SDPGroup = i.SDPGroup
FROM inserted i;
IF (SELECT COUNT(*) FROM SDPRawInventory A
WHERE A.InvYear = #Year
AND A.InvMonth = #Month
AND A.SDPGroup = #SDPGroup) >= 1
BEGIN
RAISERROR ('Duplicate data', 16, 1)
ROLLBACK;
END
ELSE
BEGIN
INSERT INTO SDPRawInventory
SELECT * FROM inserted;
END
This is the table
And to clarify there is no primary key nor unique identifier.
If you are unable to put a constraint in place, then you need to handle the fact that Inserted may have multiple records. And because its an after insert trigger, you don't need to do anything if no duplicates are found because the records are already inserted.
ALTER TRIGGER [dbo].[SDPRawInventory_Dup_Trigger]
ON [dbo].[SDPRawInventory]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (
SELECT 1
FROM dbo.SDPRawInventory S
INNER JOIN Inserted I ON
-- Test for a duplicate
S.InvYear = I.InvYear
AND S.InvMonth = I.InvMonth
AND S.SDPGroup = I.SDPGroup
-- But ensure the duplicate is a *different* record - assumes a unique ID
AND S.ID <> I.ID
)
BEGIN
THROW 51000, 'Duplicate data.', 1;
END;
END;
Note the simplified and modern error handling.
EDIT: And if you have no unique key, and no permission to add one, then you need an instead of trigger to only insert non-duplicates e.g.
ALTER TRIGGER [dbo].[SDPRawInventory_Dup_Trigger]
ON [dbo].[SDPRawInventory]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
-- Reject the entire insert if a single duplicate exists
-- Note if multiple records are inserted, some of which are duplicates and some of which aren't, they all get rejected
IF EXISTS (
SELECT 1
FROM dbo.SDPRawInventory S
INNER JOIN Inserted I ON
-- Test for a duplicate
A.InvYear = I.InvYear
AND A.InvMonth = I.InvMonth
AND A.SDPGroup = I.#SDPGroup
)
-- Test that Inserted itself doesn't contain duplicates
OR EXISTS (SELECT 1 FROM Inserted GROUP BY InvYear, InvMonth, SDPGroup HAVING COUNT(*) > 1)
BEGIN
THROW 51000, 'Duplicate data.', 1;
END;
INSERT INTO dbo.SDPRawInventory (SDP_SKU_DESC, WholeQty, InvYear, InvMonth, SDPGroup, invUOM, LooseQty)
SELECT SDP_SKU_DESC, WholeQty, InvYear, InvMonth, SDPGroup, invUOM, LooseQty
FROM Inserted I
WHERE NOT EXISTS (
SELECT 1
FROM dbo.SDPRawInventory S
-- Test for a duplicate
WHERE S.InvYear = I.InvYear
AND S.InvMonth = I.InvMonth
AND S.SDPGroup = I.SDPGroup
);
END;
Note: This doesn't do anything to handle existing duplicates.
This trigger is executed after the new records were inserted, so it will at least find the original records in the SELECT COUNT statement. Changing >= 1 into >= 2 can only partially fix this when inserting is guaranteed to occur one record as a time. Moreover, it will still fail when there were already multiple duplicated of the newly inserted record in the database before the insert.
You need to exclude the latest inserted records from the COUNT. But a better idea would probably be to add a UNIQUE constraint for preventing duplicates, so no trigger would be necessary.
If adding a constraint is not possible yet, you should initiate a clean-up process to eliminate the existing duplicates beforehand. Everything else is looks pretty flawed to me, since it is unlikely the current approach will ever bring the table into a state with no duplicates.
You are creating the infinite loop. You just have to remove the insertion part from your trigger.
ELSE
BEGIN
INSERT INTO SDPRawInventory
SELECT * FROM inserted;
END
This should not be in the trigger as trigger is called as part of insertion. you should not write actual insertion in to table in trigger.

Sql Trigger to add new row when table update

I am new to sql triggers and learning as developing triggers for asp.net application.i am having case where I need to first save table and then edit the same table - this edit create new row in different table but the the problem is every time I edit the table it create new row in different table I want to create row in different table for only first edit.
Dividing my problem for readability.
I have two tables:
Table A and table B
I have written trigger on table A that add row in table B.
Problem:
Every time I edit row in table A a new row get added to table B. (So every edit create new row)
Required result:
I want my trigger to add ONLY one row in table B for the first edit in table A but not for subsequent edits.
I am using update triggers.
Any example with code would be great
Thanks you much in advance .
Create TRIGGER [dbo].[triggerName] ON [dbo].[databaseName]
For Update
As
Begin
DECLARE #i int
DECLARE #d int
DECLARE #action char(6)
DECLARE #Car VARCHAR(20)
IF (##ROWCOUNT = 0) RETURN
SELECT #i = Count(*) From Inserted
SELECT #d = Count(*) From Deleted
SELECT #action = CASE
WHEN (#i <> 0) and (#d <> 0) THEN 'UPDATE'
WHEN (#i = 0) and (#d <> 0) THEN 'DELETE'
WHEN (#i <> 0) and (#d = 0) THEN 'INSERT'
End
SELECT #Car = A From inserted
IF #action = 'UPDATE' AND #Car in ('BMW')
Begin
INSERT INTO Tableb (c,d,f)
Select c,d,f from inserted
End
Your trigger has some flaws in it.
First, You don't need to test if it was fired because of update, insert or delete. The trigger is specified for update, so inserts and deletes will not fire it anyway.
Second, SELECT #Car = A From inserted will raise an error whenever you update more then one row in the table.
Third, As you said, this will insert a record in tableB for every update, while you want it to insert a record only for the first update done (I assume one for the first update on any row, so if you update row 1 then insert, update row 2 then another insert, and update row 1 again don't insert).
I would write it like this:
Create TRIGGER [dbo].[triggerName] ON [dbo].[tableName]
For Update
As
Begin
INSERT INTO Tableb (c,d,f)
Select c,d,f
from inserted i
left join Tableb t ON(i.c = t.c and i.d = t.d and i.f = t.f)
where t.id is null -- or some other non-nullable column
and i.a = 'BMW'
End
You can modify your SQL trigger to execute only after INSERT
CREATE TRIGGER dbo.myTable_Insert
ON dbo.myTable
AFTER INSERT
AS
It is possible to create SQL trigger to run after insert, update or delete as seen in the referred tutorial

how to copy row from one table to another before update on specific column in sybase trigger?

I want to copy a row from one table to other table before an update happens on specific column. this column named StartDateTime_ in my table.
can anyone correct this trigger if it is not correct or need optimisation.
CREATE TRIGGER PeriodicHistory_TR_U ON order_db..Periodic
FOR UPDATE
AS IF update(StartDateTime_)
begin
declare #Identity_;
declare #Version_;
DECLARE #Revision_;
declare #Identifier_;
declare #CreationTime_;
declare #CreationUserId_;
declare #StartDateTime_;
SELECT #Identity_= i.Identity_ from inserted i;
SELECT #Version_= i.Version_ from inserted i;
SELECT #Identifier_= i.Identifier_ from inserted i;
SELECT #CreationTime_= i.CreationTime_ from inserted i;
SELECT #CreationUserId_= i.CreationUserId_ from inserted i;
SELECT #StartDateTime_= i.StartDateTime_ from inserted i;
set #Revision_ = #Version_ +1;
insert into order_db..PeriodicHistory(Identity_,Version_,Revision_,Identifier_,CreationTime_,CreationUserId_,StartDateTime_)
values(#Identity_,#Version_,#Identifier_,#CreationTime_,#CreationUserId_,#StartDateTime_);
end
How about this:
CREATE TRIGGER PeriodicHistory_TR_U ON Periodic
FOR UPDATE
AS IF update(StartDateTime_)
BEGIN
INSERT INTO PeriodicHistory(Identity_,Version_,Revision_,Identifier_,CreationTime_,CreationUserId_,StartDateTime_)
SELECT d.Identity_,d.Version_,d.Revision_ + 1,d.Identifier_,d.CreationTime_,d.CreationUserId_,d.StartDateTime_
FROM deleted d
JOIN inserted i
ON d.Identity_ = i.Identity_
END
This eliminates the need for temporary variables.
Think about what happens if the update on Periodic affects more than one row. The original trigger won't be able to store multiple values...
Also, my revised trigger updates the version in the history table in the same way as your original trigger. You probably want to update the version in the Periodic table and keep the old value in the history. As it is after ten updates you could end up with: Periodic.Version_ = 1
PeriodicHistory.Version_ = 2

After insert not working

all id columns has auto_increment
In my trigger:
ALTER trigger [dbo].[mytrig]
on [dbo].[requests]
after INSERT, UPDATE
as
begin
declare #MyId1 int
set #MyId1 = (select Id from inserted)
declare #MyId2 int
declare #MyId3 int
if (select column1 from inserted) = 1
begin
insert into [dbo].[contracts] select column1,column2,column3 .... from inserted
set #MyId2 = SCOPE_IDENTITY()
insert into [dbo].[History] select column1,column2,column3 .... from inserted
set #MyId3 = SCOPE_IDENTITY()
insert into [dbo].[contracts_depts](Id_Contract ,column5) select #MyId2,column6 from request_depts where Id_request=#MyId1
insert into [dbo].[History_depts] (Id_InHistory,column5) select #MyId3,column6 from request_depts where Id_request=#MyId1
end
end
#MyId1 returns value only after update but not after insert. Do I have to use scope_identity() or something ?
Your main issue is: you're assuming the triggers is called once per row - that is NOT the case!
The trigger is called once per statement, and if your statement affects multiple rows, the Inserted pseudo table will contain multiple rows - so your statement here
set #MyId1 = (select Id from inserted)
really isn't going to work - it will select one arbitrary row (out of however many there are).
You'll need to rewrite your trigger to take this fact into account! Assume that Inserted contains 100 rows - how do you want to deal with that? What are you trying to achieve? Triggers don't return values - they will record into an audit table, or update other rows, or something like that ....

Retrieve initial table in AFTER INSERT trigger SQL

I use this code to check if an element of the new entry is equal to an element of previously inserted data.
CREATE TRIGGER trig1 ON Table1
AFTER INSERT
AS
DECLARE trigcursor CURSOR FOR
SELECT Name FROM INSERTED
DECLARE #Name1 varchar(80)
OPEN trigcursor;
FETCH NEXT FROM trigcursor INTO #Name1
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT * FROM Table1 WHERE Name= #Name1)
BEGIN
...
END
FETCH NEXT FROM trigcursor INTO #Name1
END
The problem is that for some reason the new entry exists also in the table Table1, not only in INSERTED. So the condition is always true. Can you help me why this happens? Is there a way to retrieve only the initial table without the new entry in it? Thanks!
Your trigger is AFTER INSERT on table Table1. It should be BEFORE INSERT if you expect not to find the record in the table.
Alternative: use INSTEAD OF INSERT trigger.
OR
Add another column that accepts null. Make it a number column so that it will be fast. Do not insert any value in it on the insert. Then, in the AFTER INSERT TRIGGER, the rows that have that column empty are the new ones. The ones that have the column filled with something are the old ones.
Then update empty columns with value.
eg: add column mark
After insert, look for the name:
SELECT * FROM Table1 WHERE Name= #Name1 and mark is not null
Once you found out whether or not it existed before, update everything with something:
update table1 set mark = 1 where mark is null