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.
Related
I have one TSQL command that I use in SSIS sequence container in Execute sql task executable.
I need to insert data into archive DB/table that is same as PROD and attached on same instance of SQL 2016 standard edt.
I insert about 5671294 records and in the mean time update the flag in other help/stage table that use in further checks.
My Loop never ends after updating last 71294 records. On Activity monitor i can see stuck in UPDATE command and no columns for updating.
Another problem is that this works very slow (DB in simple recovery).
SET IDENTITY_INSERT ArchiveDB..Table ON
GO
DECLARE #InsertRowCnt INT
SET #InsertRowCnt = 1
DECLARE #InsertBatchSize INT
SET #InsertBatchSize=100000
WHILE (#InsertRowCnt > 0)
BEGIN
DECLARE #OutputTbl AS TABLE(BSGID INT)
INSERT INTO ArchiveDB..Table ([BSGID], [Identifier],[SystemCombinationItems], [TotalItems], [TotalBankers],[SpecialCombinationID], [ModifiedOn], [ModifiedBy], [CreatedOn])
OUTPUT inserted.BSGID INTO #OutputTbl(BSGID)
SELECT TOP (#InsertBatchSize) s.BSGID, s.Identifier,
s.SystemCombinationItems, s.TotalItems, s.TotalBankers, s.SpecialCombinationID, s.ModifiedOn,
s.ModifiedBy, s.CreatedOn
FROM ProdDB.dbo.Table AS s WITH (NOLOCK) INNER JOIN
ArchiveDB.dbo.StageTable AS t WITH (NOLOCK) ON s.BSGID = t.BSGID
WHERE t.IsInserted IS NULL
----Updating stage table contains 2 columns BSGID int,IsInserted int null
UPDATE arh
SET arh.IsInserted=1
FROM ArchiveDB.dbo.StageTable AS arh WITH(NOLOCK)
INNER JOIN #OutputTbl AS a
ON arh.BSGID=a.BSGID
WHERE arh.IsInserted is null
SET #InsertRowCnt = ##ROWCOUNT;
END
SET IDENTITY_INSERT ArchiveDB..Table OFF
GO
SET IDENTITY_INSERT ArchiveDB..Table ON
GO
DECLARE #InsertRowCnt INT
--------------------------------
SELECT #InsertRowCnt=COUNT(*)
FROM ProdDB.dbo.Table AS s WITH (NOLOCK) INNER JOIN
ArchiveDB.dbo.StageTable AS t WITH (NOLOCK) ON s.BSGID = t.BSGID
WHERE t.IsInserted IS NULL
----------------------------
DECLARE #InsertBatchSize INT
SET #InsertBatchSize=100000
WHILE (#InsertRowCnt > 0)
BEGIN
DECLARE #OutputTbl AS TABLE(BSGID INT)
INSERT INTO ArchiveDB..Table ([BSGID], [Identifier],[SystemCombinationItems],
[TotalItems], [TotalBankers],[SpecialCombinationID], [ModifiedOn], [ModifiedBy],
[CreatedOn])
OUTPUT inserted.BSGID INTO #OutputTbl(BSGID)
SELECT TOP (#InsertBatchSize) s.BSGID, s.Identifier,
s.SystemCombinationItems, s.TotalItems, s.TotalBankers, s.SpecialCombinationID,
s.ModifiedOn,
s.ModifiedBy, s.CreatedOn
FROM ProdDB.dbo.Table AS s WITH (NOLOCK) INNER JOIN
ArchiveDB.dbo.StageTable AS t WITH (NOLOCK) ON s.BSGID = t.BSGID
WHERE t.IsInserted IS NULL
----Updating stage table contains 2 columns BSGID int,IsInserted int null
UPDATE arh
SET arh.IsInserted=1
FROM ArchiveDB.dbo.StageTable AS arh WITH(NOLOCK)
INNER JOIN #OutputTbl AS a
ON arh.BSGID=a.BSGID
WHERE arh.IsInserted is null
--------------------------------
SELECT #InsertRowCnt=COUNT(*)
FROM ProdDB.dbo.Table AS s WITH (NOLOCK) INNER JOIN
ArchiveDB.dbo.StageTable AS t WITH (NOLOCK) ON s.BSGID = t.BSGID
WHERE t.IsInserted IS NULL
----------------------------
END
SET IDENTITY_INSERT ArchiveDB..Table OFF
GO
So I understand recursive triggers. Got to be careful of deadlocks etc. However this is only after an insert not after insert and update. Also, I have an audit trigger table that I am updating to make sure all is well. And querying after to double check. All looks fine but no update happens.
if exists (select 'a' from sys.triggers where name = 'invoicememologic')
begin
drop trigger invoicememologic
end
go
create trigger invoicememologic
on transactiontable
after insert
as
begin
declare #inum varchar(1000)
select #inum = min(transactioninvnum)
from
(select transactioninvnum
from inserted i
inner join project p on left(i.projectid, charindex(':', i.projectid)) = p.projectid
where right(i.projectid, 1) <> ':'
and abs(p.UseProjectMemoOnInv) = 1
group by transactioninvnum) b
while #inum is not null
begin
declare #rCount int
select #rCount = count(*)
from transactiontable
where TransactionInvNum = #inum
if #rCount = 1
begin
declare #tid varchar(100)
select #tid = transactionid
from transactiontable
where TransactionInvNum = #inum
declare #pmemo varchar(MAX)
select #pmemo = p.projectMemo
from transactiontable tt
inner join project p on left(tt.projectid, charindex(':', tt.projectid)) = p.projectid
where transactionInvNum = #inum
insert into audittrigger
values (#pmemo, #tid)
update transactiontable
set transactionmemo2 = #pmemo
where ltrim(rtrim(transactionid)) = ltrim(rtrim(#tid))
end
select #inum = min(transactioninvnum)
from
(select transactioninvnum
from inserted i
inner join project p on left(i.projectid, charindex(':', i.projectid)) = p.projectid
where abs(transactionjointinv) = 1
and right(i.projectid, 1) <> ':'
and abs(p.UseProjectMemoOnInv) = 1
group by transactioninvnum ) a
where transactioninvnum > #inum
end
end
Reason for trigger. 1 Invoice can be multiple rows in the database. 3 rows. So it only should update any one of the 3 rows. Doesn't matter. And it must grab the memo from the parent project of the phases that are being inserted into the database. hence the inner join on the left with charindex.
So I check the audit table. All looks well there. MEMO is there and the transactionid is there. I query after the trigger fires. TransactionID exists in the transactiontable but the memo2 is not being updated.
TransactionMemo2 is type of ntext. I thought it might be varchar with a direct update command will fail. I tried to update manually through setting a var as the text string and call the update manually with the transactionid being required. all worked fine. I am lost
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
Below is a SQL Server 2005 update trigger. For every update on the email_subscriberList table where the isActive flag changes we insert an audit record into the email_Events table. This works fine for single updates but for bulk updates only the last updated row is recorded. How do I convert the below code to perform an insert for every row updated?
CREATE TRIGGER [dbo].[Email_SubscriberList_UpdateEmailEventsForUpdate_TRG]
ON [dbo].[Email_subscriberList]
FOR UPDATE
AS
DECLARE #CustomerId int
DECLARE #internalId int
DECLARE #oldIsActive bit
DECLARE #newIsActive bit
DECLARE #email_address varchar(255)
DECLARE #mailinglist_name varchar(255)
DECLARE #email_event_type varchar(1)
SELECT #oldIsActive = isActive from Deleted
SELECT #newIsActive = isActive from Inserted
IF #oldIsActive <> #newIsActive
BEGIN
IF #newIsActive = 1
BEGIN
SELECT #email_event_type = 'S'
END
ELSE
BEGIN
SELECT #email_event_type = 'U'
END
SELECT #CustomerId = customerid from Inserted
SELECT #internalId = internalId from Inserted
SELECT #email_address = (select email from customer where customerid = #CustomerId)
SELECT #mailinglist_name = (select listDescription from Email_lists where internalId = #internalId)
INSERT INTO Email_Events
(mailshot_id, date, email_address, email_event_type, mailinglist_name)
VALUES
(#internalId, getDate(), #email_address, #email_event_type,#mailinglist_name)
END
example
untested
CREATE TRIGGER [dbo].[Email_SubscriberList_UpdateEmailEventsForUpdate_TRG]
ON [dbo].[Email_subscriberList]
FOR UPDATE
AS
INSERT INTO Email_Events
(mailshot_id, date, email_address, email_event_type, mailinglist_name)
SELECT i.internalId,getDate(),c.email,
case i.isActive when 1 then 'S' else 'U' end,e.listDescription
from Inserted i
join deleted d on i.customerid = d.customerid
and i.isActive <> d.isActive
join customer c on i.customerid = c.customerid
join Email_lists e on e.internalId = i.internalId
Left outer joins, for in case there are no related entries in customer or email_Lists (as is possible in the current code) -- make them inner joins if you know there will be data present (i.e. foreign keys are in place).
CREATE TRIGGER [dbo].[Email_SubscriberList_UpdateEmailEventsForUpdate_TRG]
ON [dbo].[Email_subscriberList]
FOR UPDATE
AS
INSERT INTO Email_Events
(mailshot_id, date, email_address, email_event_type, mailinglist_name)
select
i.InternalId
,getdate()
,cu.Email
,case i.IsaActive
when 1 then 'S'
else 'U'
end
,el.ListDescription
from inserted i
inner join deleted d
on i.CustomerId = d.CustomerId
and i.IsActive <> d.IsActive
left outer join Customer cu
on cu.CustomerId = i.CustomerId
left outer join Email_Lists el
on el.InternalId = i.InternalId
Test it well, especially for concurrency issues. Those joins within the trigger make me nervous.
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.