SQL Server Multi-row insert trigger - sql

I have tested that trigger but it works only with one insert row.
The trigger fails with multiple-row insert and I didn't find the syntax to fix the trigger.
Any suggestion to fix that?
Thanks to the stackoverflow users that let me notice the problem.
USE [MY_DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[tr_update_father]
ON [dbo].[PROD_IVR_CALL]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #tnumber nvarchar(50), #id_inserted int, #id_prod_file int;
select
#id_inserted = ins.id,
#tnumber = ins.tnumber ,
#id_prod_file = pf.ID_PROD_FILE
from inserted ins
inner join prod_failure pf on (ins.ID_PROD_FAILURE = pf.ID);
update prod_ivr_call
set id_father = sq.ID
from
(select min(pic.id) as ID
from prod_ivr_call pic
inner join prod_failure pf on (pic.ID_PROD_FAILURE = pf.ID)
where pic.tnumber = #tnumber
and pic.is_requested = 0
and pf.ID_PROD_FILE = #id_prod_file
group by pic.tnumber ) sq
END

Your UPDATE statement is not syntactically correct. You can actually merge the two statements of your trigger using a CTE, and then do the UPDATE on this CTE:
ALTER TRIGGER [dbo].[tr_update_father]
ON [dbo].[PROD_IVR_CALL]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
;WITH ToUpdate AS (
SELECT pic.id_father,
MIN(pic.id) OVER (PARTITION BY pic.tnumber) AS min_id
FROM prod_ivr_call pic
INNER JOIN join prod_failure pf ON pic.ID_PROD_FAILURE = pf.ID
JOIN inserted ins ON ins.ID_PROD_FAILURE = pf.ID
WHERE pic.tnumber = ins.tnumber AND pic.is_requested = 0
)
UPDATE ToUpdate
SET id_father = min_id
END

Related

Trigger After Update Returns Null

I wrote the following trigger to update two columns in my table when any record is inserted or updated:
CREATE TRIGGER test
ON mytable
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE t
SET UPDATE_TIMESTAMP = GETDATE(),
UPDATE_USER_NAME = SUSER_SNAME()
FROM mytable t
INNER JOIN inserted i ON t._ID = i._ID
END
GO
However, it does not update the columns UPDATE_TIMESTAMP and UPDATE_USER_NAME and still returns the default null. Could you please help me how to fix that? Thanks!
I think that you are facing a recursing call in your trigger
Try with this :
CREATE TRIGGER test
ON mytable
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF TRIGGER_NESTLEVEL() > 1
RETURN;
UPDATE t
SET UPDATE_TIMESTAMP = GETDATE(),
UPDATE_USER_NAME = SUSER_SNAME()
FROM mytable t
INNER JOIN inserted i ON t._ID = i._ID
END
GO

After Update Trigger puzzlement

I am trying to get my head round an AFTER UPDATE trigger.
Currently in our DB there is a Trigger that contains a cursor. From my understanding cursors in triggers are generally bad performing, so I'm trying to get rid of the cursor.
Currently the trigger looks like this:
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE #rowcheck int
DECLARE #MovementID INT
DECLARE #SiteFromID INT
DECLARE #SiteToID INT
DECLARE #SiteResponsibleID INT
DECLARE #FromAddress_Postcode Varchar(20)
DECLARE #ToAddress_Postcode Varchar(20)
DECLARE zcursor CURSOR FOR SELECT ID, SiteFromID, SiteToID, SiteResponsibleID
, FromAddress_Postcode, ToAddress_Postcode FROM inserted
OPEN zcursor
SELECT #rowcheck=1
WHILE #rowcheck=1
BEGIN
FETCH NEXT FROM zcursor INTO #MovementID, #SiteFromID, #SiteToID, #SiteResponsibleID, #FromAddress_Postcode, #ToAddress_Postcode
IF (##FETCH_STATUS = 0)
BEGIN
UPDATE Tasks_Movement
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(#FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(#ToAddress_Postcode)
WHERE Tasks_Movement.ID = #MovementID
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](#SiteFromID)
WHERE Tasks_Movement.ID = #MovementID
AND (#SiteResponsibleID Is NULL OR #SiteResponsibleID=0)
AND (#SiteFromID Is NOT NULL AND #SiteFromID>0)
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](#SiteToID)
WHERE Tasks_Movement.ID = #MovementID
AND (#SiteResponsibleID Is NULL OR #SiteResponsibleID=0)
AND (#SiteToID Is NOT NULL AND #SiteToID>0)
END
ELSE
SELECT #rowcheck=0
END
CLOSE zcursor
DEALLOCATE zcursor
END
From what I can tell the cursor in this is completely unnecessary(?)
Would I be right in thinking that the following would work better:
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE Tasks_Movement
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(inserted.FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(inserted.ToAddress_Postcode)
FROM inserted
WHERE Tasks_Movement.ID IN (SELECT id FROM inserted)
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](inserted.SiteFromID)
FROM inserted
WHERE Tasks_Movement.ID IN (SELECT id FROM inserted
WHERE (inserted.SiteResponsibleID Is NULL OR inserted.SiteResponsibleID=0)
AND (inserted.SiteFromID Is NOT NULL AND inserted.SiteFromID>0))
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](#SiteToID)
FROM inserted
WHERE Tasks_Movement.ID IN (SELECT id FROM inserted
WHERE (inserted.SiteResponsibleID Is NULL OR inserted.SiteResponsibleID=0)
AND (inserted.SiteToID Is NOT NULL AND inserted.SiteToID>0))
END
I think your trigger should be something like this:
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE tm
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(i.FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(i.ToAddress_Postcode)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID;
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteFromID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteFromID > 0
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteToID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteToID > 0
END
I've changed it to use SQl Server's UPDATE .. FROM syntax, and also removed the redundant null check when you are checking if a site ID > 0. NULL is not greater than or less than 0, so if SiteID is null SiteID > 0 can never evaluate to true, so it is a redundant additional check.
Finally, I would also recommend removing the user defined functions, although I can't see under the hood of these, based on the name they look very much like they are simple loukup functions that could be achived much more efficiently with joins.
EDIT
Rather than using the UPDATE(column) function I would add an additional join to the update to filter for updated rows, e.g.:
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteToID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
LEFT JOIN deleted d
ON d.ID = i.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteToID > 0
AND AND ISNULL(i.SiteToID, 0) != ISNULL(d.SiteToID);
I'd do it this way because UPDATE(siteToID) will return true if any row has an updated value, so if you update 1,000,000 rows and one has a change it will perform the update on all of them, not just the ones that have changed, by joining to deleted you can limit the update to relevant rows.

trigger to allow multiple updates

I have this trigger I want to make it allow multiple row updates, currently it handling only single row update.when i update record, it says sub query returning more then 1 value..
GO
ALTER TRIGGER [dbo].[OnReceiptUpdate]
ON [dbo].[paymentReceipt]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
Declare #correctdate VARCHAR(19);
Declare #receiptNo VARCHAR(50);
DECLARE #customerID NCHAR(50)
SET #customerID= (SELECT customerID FROM inserted)
set #correctdate = (SELECT CONVERT(VARCHAR(19),paymentDate,103) FROM inserted)
set #receiptNo = (SELECT receiptNo FROM inserted)
BEGIN
UPDATE Paymentreceipt
SET paymentDate = #correctdate
WHERE customerID = #customerID and receiptNo=#receiptNo
END
END
Update p
Set p.paymentDate = CONVERT(VARCHAR(19),i.paymentDate,103)
From Paymentreceipt p
inner join inserted i
On p.customerID = i.customerID and p.receiptNo = i.receiptNo
should do it I think.
PS why is p.paymentdate a string? That's asking for it.
The easiest way is to use an update statement such as the one below in the Trigger.
UPDATE Paymentreceipt
SET paymentDate = CONVERT(VARCHAR(19),paymentDate,103)
FROM inserted
WHERE Inserted.receiptNo = Paymentreceipt.receiptNo
AND Inserted.customerID = Paymentreceipt.customerID
Note I don't have SQL server in front of me so the syntax might not be 100% correct but that gives you the general idea.
In general I try and avoid triggers but if your really need the trigger then use it but it may be possible to address this issue through the use of a stored procedure.

Handling muliple rows with insert trigger

I have a trigger on an orders table that inserts order details into the order_details table. This works when only one row is entered, but not when multiple rows are inserted at once. I have read multiple threads > on various sites about using a cursor, while statements, temp tables, etc. I have tried a few but no success. Any suggestions on the BEST/Easiest way to ensure I get all the detailed rows added when an order is placed.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[CreateOrderDetails]
ON [dbo].[Orders]
FOR INSERT
AS
declare #OrderStagingId uniqueidentifier
declare #OrderType as varchar(50)
declare #OrderID uniqueidentifier
declare #LocationID uniqueidentifier
declare #status as varchar (25)
declare #vendorid as uniqueidentifier
SELECT #OrderID = OrderID, #LocationID = LocationID,
#vendorid = vendorid, #OrderStagingId = OrderStagingId
FROM inserted
Begin
SET NOCOUNT ON;
-- Insert statements for trigger here
Insert into [Brain].dbo.[Order_Details]
SELECT
NEWID() AS OrderDetailId, #OrderID AS OrderId,
dbo.OrderStaging_Details.EcProductID,
dbo.OrderStaging_Details.Qty, dbo.OrderStaging_Details.Qty_Type,
dbo.OrderStaging_Details.Cost, dbo.OrderStaging_Details.Ext_Cost,
dbo.OrderStaging_Details.EnteredBy,
NULL AS ReceivedBy, NULL AS ReceivedDate, NULL AS ReceivedQty,
dbo.OrderStaging_Details.OrderNote, NULL AS ReceivedNote,
dbo.OrderStaging_Details.UpdatedBy, dbo.OrderStaging_Details.UpdateDate
FROM
dbo.Vendor_Assigned_Locations
INNER JOIN
dbo.Vendor_Contacts
INNER JOIN
dbo.Vendors
INNER JOIN
dbo.OrdersStaging
INNER JOIN
dbo.OrderStaging_Details ON dbo.OrdersStaging.OrderStagingID = dbo.OrderStaging_Details.OrderStagingID
ON dbo.Vendors.VendorID = dbo.OrderStaging_Details.VendorId
ON dbo.Vendor_Contacts.Vendor_ID = dbo.Vendors.VendorID
ON dbo.Vendor_Assigned_Locations.LocationID = dbo.OrdersStaging.LocationID
AND dbo.Vendor_Assigned_Locations.VCID = dbo.Vendor_Contacts.VCID
INNER JOIN
dbo.Orders ON dbo.OrdersStaging.OrderStagingID = dbo.Orders.OrderStagingID
AND dbo.Vendors.VendorID = dbo.Orders.VendorID
AND dbo.Vendor_Contacts.VCID = dbo.Orders.VendorContactID
AND dbo.OrdersStaging.LocationID = dbo.Orders.LocationID
LEFT OUTER JOIN
dbo.Order_Details ON dbo.Orders.OrderID = dbo.Order_Details.OrderID
WHERE
(dbo.OrderStaging_Details.OrderStagingID = #OrderStagingID)
AND (dbo.OrdersStaging.LocationID = #Locationid)
AND (dbo.Vendors.VendorID = #Vendorid)
AND (dbo.Order_Details.OrderID IS NULL)
end
This trigger is probably necessary to you
ALTER TRIGGER [dbo].[CreateOrderDetails] ON [dbo].[Orders]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for trigger here
INSERT INTO [Brain].dbo.[Order_Details]
SELECT
NEWID() AS OrderDetailId, i.OrderID AS OrderId,
dbo.OrderStaging_Details.EcProductID,
dbo.OrderStaging_Details.Qty, dbo.OrderStaging_Details.Qty_Type,
dbo.OrderStaging_Details.Cost, dbo.OrderStaging_Details.Ext_Cost,
dbo.OrderStaging_Details.EnteredBy,
NULL AS ReceivedBy, NULL AS ReceivedDate, NULL AS ReceivedQty,
dbo.OrderStaging_Details.OrderNote, NULL AS ReceivedNote,
dbo.OrderStaging_Details.UpdatedBy, dbo.OrderStaging_Details.UpdateDate
FROM
dbo.OrdersStaging
INNER JOIN
dbo.OrderStaging_Details ON dbo.OrdersStaging.OrderStagingID = dbo.OrderStaging_Details.OrderStagingID
INNER JOIN
dbo.Vendors ON dbo.Vendors.VendorID = dbo.OrderStaging_Details.VendorId
INNER JOIN
dbo.Vendor_Contacts ON dbo.Vendor_Contacts.Vendor_ID = dbo.Vendors.VendorID
INNER JOIN
dbo.Vendor_Assigned_Locations ON dbo.Vendor_Assigned_Locations.LocationID = dbo.OrdersStaging.LocationID
AND dbo.Vendor_Assigned_Locations.VCID = dbo.Vendor_Contacts.VCID
INNER JOIN
dbo.Orders ON dbo.OrdersStaging.OrderStagingID = dbo.Orders.OrderStagingID
AND dbo.Vendors.VendorID = dbo.Orders.VendorID
AND dbo.Vendor_Contacts.VCID = dbo.Orders.VendorContactID
AND dbo.OrdersStaging.LocationID = dbo.Orders.LocationID
INNER JOIN
inserted i ON dbo.Orders.OrderID = i.OrderID
END
The outer select statement where you are picking up #OrderID, #LocationID etc, is only picking the first row out of inserted which contains all the orders that are being inserted in the transaction that pulled the trigger.
Use insert into detail(...)
Select NewID(), Inserted.OrderID, ...
From Inserted
inner join ...
Where etc
instead.
It's because you're thinking about rows rather than columns. Dump the #variables, and join on inserted.

Writing a complex trigger

This posting is an update (with trigger code) from my earlier posting yesterday
I am using SQL Server 2000. I am writing a trigger that is executed when a field Applicant.AppStatusRowID
Table Applicant is linked to table Location, table Company & table AppStatus.
My issue is creating the joins in my query.
When Applicant.AppStatusRowID is updated, I want to get the values from Applicant.AppStatusRowID, Applicant.FirstName, Applicant.Lastname, Location.LocNumber, Location.LocationName, Company.CompanyCode, AppStatus.DisplayText
The joins would be :
Select * from Applicant A
Inner Join AppStatus ast on ast.RowID = a.AppStatusRowID
Inner Join Location l on l.RowID = a.LocationRowID
Inner Join Company c on c.RowID = l.CompanyRowID
This is to be inserted into an Audit table (fields are ApplicantID, LastName, FirstName, Date, Time, Company, Location Number, Location Name, StatusDisposition, User)
The trigger query is:
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER tri_UpdateAppDispo ON dbo.Test_App
For Update
AS
declare #Approwid int
Declare #triggername sysname
Set #rowCnt = ##rowcount
Set #triggername = object_name(##procid)
If #rowCnt = 0
RETURN
If Update(appstatusrowid)
BEGIN
-----------------------------------------------------------------------------
-- insert a record to the AppDispo table, if AppstatusRowid
-- is being Updated
-----------------------------------------------------------------------------
Insert AppDispo(AppID, LastName, FirstName, [DateTime],Company,Location,LocationName,
StatusDispo,[Username])
Select d.Rowid,d.LastName, d.FirstName, getDate(),C.CompanyCode,l.locnum,l.locname, ast.Displaytext,
SUSER_SNAME()+' '+User
From deleted d with(nolock)
Inner join Test_App a with (nolock) on a.RowID = d.rowid
inner join location l with (nolock) on l.rowid = d.Locationrowid
inner join appstatus ast with (nolock) on ast.rowid = d.appstatusrowid
inner join company c with (nolock) on c.rowid = l.CompanyRowid
--Inner Join Deleted d ON a.RowID = d.RowID
--Where (a.rowid = #Approwid)
END
GO
Any ideas?
Try with this code
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER tri_UpdateAppDispo ON dbo.Test_App
For Update
AS
declare #Approwid int
Declare #triggername sysname
Set #rowCnt = ##rowcount
Set #triggername = object_name(##procid)
If #rowCnt = 0
RETURN
If Update(appstatusrowid)
BEGIN
-----------------------------------------------------------------------------
-- insert a record to the AppDispo table, if AppstatusRowid
-- is being Updated
-----------------------------------------------------------------------------
Insert AppDispo(AppID, LastName, FirstName, [DateTime],Company,Location,LocationName,
StatusDispo,[Username])
Select d.Rowid,d.LastName, d.FirstName, getDate(),C.CompanyCode,l.locnum,l.locname, ast.Displaytext,
SUSER_SNAME()+' '+User
From deleted d with(nolock),location l with (nolock),appstatus ast with (nolock),company c with (nolock)
where d.Locationrowid =l.rowid and
d.appstatusrowid = ast.rowid and
c.rowid = l.CompanyRowid
END GO
whith this code you get the update-deleted row(the old_value)
see you.