I have a view and a trigger that overwrites UPDATE. I try to understand the code, however I am having some troubles with line from deleted and from inserted - Are these automatically created by original update? Does it mean that if I call this trigger, first the update gets called and then the trigger?
create trigger updateView on View
instead of UPDATE as
begin
declare #nameK VARCHAR(100), #addresK VARCHAR(100), #nameZ VARCHAR(100), #number INT, #date DATE;
declare #nameKo VARCHAR(100), #addresKo VARCHAR(100), #nameZo VARCHAR(100), #numbero INT, #dateo DATE; -- 'o'ld
declare insCur cursor for select name, addres, bandName, number, date from inserted;
declare delCur cursor for select name, addres, bandName, number, date from deleted;
open insCur;
open delCur;
...
This trigger will fire instead of performing the update. The inserted and deleted tables are made available so that you can determine:
The new values for each updated row in inserted
The previous (in this case current, since the normal update operation has not been performed) values from each updated row in deleted
The convention of inserted and deleted is used regardless of the type of trigger. So, for example, in an after update trigger, which simply follows the execution of a normal update, the deleted table would be the primary way to know the previous values.
The deleted contains old values, the inserted - new values.
There are options: AFTER UPDATE or BEFORE UPDATE which you can set to see behavior which you want.
About Deleted and Inserted:
UPDATE dbo.Client SET Name = 'John' WHERE Name = 'Steve'
you will have: deleted value 'Steve', inserted value 'John'
Also I would recommend to avoid using cursors inside triggers, i.e. it can hit performance a lot.
Related
I'm going to create a trigger for update. Purpose of this trigger is that If muadurum column is changed , take the old value of mua_tarih in table fkayitlar and insert to another table mua_tarihleri.
My code block like;
ALTER TRIGGER [dbo].[trgr_fkayit_update]
ON [dbo].[fkayitlar]
AFTER UPDATE
AS
DECLARE #mua_durum_once NVARCHAR(10)
DECLARE #mua_durum_sonra NVARCHAR(10)
DECLARE #mua_tarih_once DATE
DECLARE #mua_yapan_once NVARCHAR(25)
DECLARE #kisi_id INT
Take the old value;
SELECT
#kisi_id=kayitid,
#mua_durum_once=muayenedurum,
#mua_tarih_once=muayenetarih,
#mua_yapan_once=mua_yapan
FROM deleted
Take the new value;
SELECT #mua_durum_sonra=muayenedurum FROM inserted
Check if value is changed ; if changed, Insert #mua_tarih to table mua_tarihleri with #kisi_id and #mua_yapan_once
IF #mua_durum_once='OLDU'
AND #mua_durum_sonra='OLMADI'
AND #mua_tarih_once IS NOT NULL
BEGIN
INSERT INTO mua_tarihleri(kayitid,mua_tarihi,mua_yapan)
VALUES(#kisi_id,#mua_tarih_once,#mua_yapan_once)
END
My problem is When I update more than one row in table fkayitlar,Trigger is working, but I see only one inserted row in table mua_tarihleri (only working once). I need to see more than one.(should be working more than once) Are not Triggers working on more than one process? or How can I solve this my problem?
The trigger only occurs once when the table is updated, no matter how many rows are updated. Therefore, you have to write your trigger body to operate on a set of rows, not a single row as you have done.
Should be something like:
ALTER TRIGGER [dbo].[trgr_fkayit_update]
ON [dbo].[fkayitlar]
AFTER UPDATE
AS
INSERT INTO mua_tarihleri(kayitid,mua_tarihi,mua_yapan)
SELECT deleted.kayitid, deleted.muayenedurum, deleted.muayenetarih, deleted.mua_yapan
FROM deleted
JOIN inserted ON deleted.kayitid = inserted.kayitid
WHERE deleted.muayenedurum='OLDU'
AND inserted.muayenedurum='OLMADI'
AND muayenetarih IS NOT NULL
I have an SQL Trigger FOR INSERT, UPDATE I created which basically does the following:
Gets a LineID (PrimaryID for the table) and RegionID From the Inserted table and stores this in INT variables.
It then does a check on joining tables to find what the RegionID should be and if the RegionID is not equal what it should be from the Inserted table, then it should update that record.
CREATE TRIGGER [dbo].[TestTrigger]
ON [dbo].[PurchaseOrderLine]
FOR INSERT, UPDATE
AS
-- Find RegionID and PurchaseOrderLineID
DECLARE #RegionID AS INT
DECLARE #PurchaseOrderLineID AS INT
SELECT #RegionID = RegionID, #PurchaseOrderLineID = PurchaseOrderLineID FROM Inserted
-- Find PurchaserRegionID (if any) for the Inserted Line
DECLARE #PurchaserRegionID AS INT
SELECT #PurchaserRegionID = PurchaserRegionID
FROM
(...
) UpdateRegionTable
WHERE UpdateRegionTable.PurchaseOrderLineID = #PurchaseOrderLineID
-- Check to see if the PurchaserRegionID has a value
IF #PurchaserRegionID IS NOT NULL
BEGIN
-- If PurchaserRegionID has a value, compare it with the current RegionID of the Inserted PurchaseOrderLine, and if not equal then update it
IF #PurchaserRegionID <> #RegionID
BEGIN
UPDATE PurchaseOrderLine
SET RegionID = #PurchaserRegionID
WHERE PurchaseOrderLineID = #PurchaseOrderLineID
END
END
The problem I have is that it is not updating the record and I'm guessing, it is because the record hasn't been inserted yet into the PurchaseOrderLine table and I'm doing an update on that. But can you update the row which will be inserted from the Inserted table?
The major problem with your trigger is that it's written in assumption that you always get only one row in INSERTED virtual table.
SQL Server triggers are statement-triggers not row-triggers. You have to take that fact into consideration.
Now if I understand correctly the logic behind this trigger then you need just one update statement in it
CREATE TRIGGER TestTrigger ON PurchaseOrderLine
FOR INSERT, UPDATE
AS
UPDATE l
SET RegionID = u.PurchaserRegionID
FROM PurchaseOrderLine l JOIN INSERTED i
ON l.PurchaseOrderLineID = i.PurchaseOrderLineID JOIN
(
SELECT PurchaseOrderLineID, PurchaserRegionID
FROM UpdateRegionTable -- !!! change this for your proper subquery
) u ON l.PurchaseOrderLineID = u.PurchaseOrderLineID
For this example I've created a fake table UpdateRegionTable. You have to change it to the proper query that returns PurchaseOrderLineID, PurchaserRegionID (in your code you replaced it with ...). Make sure that it returns all necessary rows, not one.
Here is SQLFiddle demo
I think the problem could be that you are making the update to PurchaceOrderLine inside the trigger that is monitoring updates to the same table as well. Try to alter the trigger to just monitor the inserts, than if this works, you can make some changes or break your trigger on two: one for inserts, another for updates.
This has been resolved. I resolved the problem by adding the trigger to another table as the IF #PurchaserRegionID IS NOT NULL was always false.
Today I saw a nice post in which the author has proven that if you are updating a column of a table without any indexes defined, an "in place update" will occur and not traditional delete\insert.
Considering this I run a small test where I have created an Update trigger on table and try to access the INSERTED magic table and here is the catch.
I am able to access the INSERTED magic table, can someone explain me if in place update is not using traditional Delete\Insert? How come one can access the magic tables?
These are my SQL statement to prove this thing.
Main table :
CREATE TABLE TestingUpdate1 (
ID INT IDENTITY,
SomeString CHAR(50)
)
INSERT INTO TestingUpdate1 (SomeString)
VALUES
('One'),('Two'),('Three'),('Four'),('Five'),('Six'),('Seven'),('Eight'),('Nine')
CHECKPOINT -- truncate the log, DB is in simple recovery.
UPDATE TestingUpdate1
SET SomeString = 'NotFour'
WHERE ID = 4 -- one row
SELECT Operation, Context, AllocUnitName, [Transaction Name], Description FROM fn_dblog(NULL, NULL) AS TranLog
Second table :
CREATE TABLE TestingUpdate4 (
ID INT IDENTITY,
SomeString CHAR(50)
)
INSERT INTO TestingUpdate4 (SomeString)
VALUES
('One'),('Two'),('Three'),('Four'),('Five'),('Six'),('Seven'),('Eight'),('Nine')
Trigger :
CREATE TRIGGER ViewCustomerTrigger ON TestingUpdate1
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE TestingUpdate4
SET SomeString = i.SomeString
FROM INSERTED i
END
GO
select * from TestingUpdate4
select * from TestingUpdate1
Thanks in advance
The INSERTED and DELETED tables in an update trigger always show the logical before and after versions of the updated rows.
Whether that is physically implemented as an in place update or as an insert/delete is execution plan dependant and irrelevant to the contents of these tables.
I want a SQL trigger to fire when a table is updated. I have a table called SimActivation and a stored procedure called spUpdateMnpDate that takes two parameters, msisdn(varchar) and date(datetime).
When the SimActivation table is updated I want to call the procedure with the new values.
Here is what I got at the moment, after changing it back and forth:
USE StoreSale;
GO
CREATE TRIGGER dbo.tSyncMnpDate
ON [dbo].[SimActivation]
AFTER UPDATE
AS
DECLARE #date datetime
DECLARE #msisdn varchar(10)
SET #date = (select ProcessDate from inserted)
SET #msisdn = (select Msisdn from inserted)
EXEC [spUpdateMnpDate]
#msisdn, #date;
GO
Can anyone point me in the right direction?
Thanks :)
The problem you have is that a trigger will fire when one or more rows have been updated. At the moment you are assuming your trigger will fire for each row, which is not the case in SQL Server.
If the stored procedure you are trying to call is fairly simple I'd pull the code out of there and in to the trigger. But remember you are working with sets of changed rows (even if the change is to only one row) so you have to write your SQL accordingly.
EDIT: I assume your procedure is updating a date where the PK is equal to #msisdn, if so you can do this in your trigger:
UPDATE Your_Table
SET Your_Table.ProcessDate = inserted.ProcessDate
FROM Your_table INNER JOIN inerted ON Your_Table.Msisdn = inserted.Msisdn
Joining the tables ensures it will work for one or many updated rows.
I have a situation where I need to merge two product tables into one and need to keep both id fields. The Id field is the pk and an identity column. On insert I want to update the prodId to match the Id of the newly inserted row. This is what I have but I get an error saying that I cannot insert null into ProductId. What am I doing wrong?
ALTER TRIGGER SyncId
ON Product
FOR INSERT
AS
BEGIN
DECLARE #ID INT
SET #ID = (SELECT ID FROM Inserted)
UPDATE Product SET
ProdId = #ID
WHERE
Id = #ID
END
You could create an INSTEAD OF trigger for the INSERT. You would then have to recreate the actual INSERT logic. You might need a view of the table for the trigger to sit on though... I don't recall if an INSERT within the trigger will work or not and can't test it right now.
Also, your trigger as written will only work if a single row is being updated. You should always write your triggers to be able to handle multiple rows in the INSERTED and DELETED tables.