I have written an inventory application in C# and want to track changes to inventory quantity. To that end, I have two tables: tblInventory and tblInvChange. Currently I have an AFTER UPDATE Trigger which works great by adding the following to the tblInvChange: (using d. for Deleted and i. for inserted) d.lastphysical; i.lastphysical; d.quantity; i.quantity.
My problem is if, on the same date, I go back and change the quantity of an item I will get two records for the same date on the same item. I would like to have the trigger insert a record into the tblInvChange table if the date does not exist and update a current record if it does.
BEGIN
If(Select [fldlastPhysical] from deleted) <> (Select [fldLastPhysical] from inserted)
INSERT tblInvTracking(keyProductID, fldLocationId, fldLastPhysical, fldQuantity, fldInventoryChange, fldNewQuantity)
SELECT
D.keyProductID, D.fldLocationID, D.fldLastPhysical, d.fldQuantity, i.fldLastPhysical, i.fldQuantity
FROM
DELETED D JOIN INSERTED I ON D.keyProductID = I.keyProductID AND D.fldLocationID = I.fldLocationID;
Else
UPDATE tblInvTracking(keyProductID, fldLocationId, fldLastPhysical, fldQuantity, fldInventoryChange, fldNewQuantity)
SELECT
D.keyProductID, D.fldLocationID, D.fldLastPhysical, d.fldQuantity, i.fldLastPhysical, i.fldQuantity
FROM
DELETED D JOIN INSERTED I ON D.keyProductID = I.keyProductID AND D.fldLocationID = I.fldLocationID;
END
This was my understanding but does not work. An example on the correct way to accomplish this would be appreciated.
You need to create 2 statements, an insert for records that don't exist and an update for records that exist. As best I can tell it would be something like the following, but this won't be 100% correct, you will need to work through the logic and make sure it matches what you are trying to achieve.
NOTE: This is assuming that fldLastPhysical is the date of interest, and that its a date not a datetime.
-- WHERE THE RECORD DOESN'T EXIST FOR THE GIVEN DATE - ADD A NEW ONE
INSERT tblInvTracking(keyProductID, fldLocationId, fldLastPhysical, fldQuantity, fldInventoryChange, fldNewQuantity)
SELECT D.keyProductID, D.fldLocationID, D.fldLastPhysical, d.fldQuantity, i.fldLastPhysical, i.fldQuantity
FROM DELETED D
JOIN INSERTED I ON D.keyProductID = I.keyProductID AND D.fldLocationID = I.fldLocationID
-- ONLY ADD A NEW RECORD FOR THIS DATE IF IT DOESN"T ALREADY EXIST
WHERE NOT EXISTS (
SELECT 1
FROM tblInvTracking T
WHERE T.fldlastPhysical = D.fldLastPhysical
AND T.keyProductID = D.keyProductID AND T.fldLocationID = D.fldLocationID
);
-- WHERE THE RECORD EXISTS FOR THE GIVEN DATE - UPDATE EXISTING
UPDATE T SET
fldQuantity = T.fldQuantity + X.fldQuantity
-- It appears the following line is a datetime column, so you will need to determine what logic is required there - maybe just a straight update.
, fldInventoryChange = X.fldInventoryChange
, fldNewQuantity = T.fldNewQuantity + X.fldNewQuantity
FROM tblInvTracking T
INNER JOIN (
SELECT D.keyProductID, D.fldLocationID, D.fldLastPhysical, d.fldQuantity, i.fldLastPhysical fldInventoryChange, i.fldQuantity fldNewQuantity
FROM DELETED D
JOIN INSERTED I ON D.keyProductID = I.keyProductID AND D.fldLocationID = I.fldLocationID
-- ONLY ADD A NEW RECORD FOR THIS DATE IF IT DOESN"T ALREADY EXIST
WHERE EXISTS (
SELECT 1
FROM tblInvTracking T
WHERE T.fldlastPhysical = D.fldLastPhysical
AND T.keyProductID = D.keyProductID AND T.fldLocationID = D.fldLocationID
)
) X ON T.fldlastPhysical = X.fldLastPhysical AND T.keyProductID = X.keyProductID AND T.fldLocationID = X.fldLocationID
Related
We have table that holds each item on an order in a separate row. In basic terms, I want to use a trigger that fires when an order is entered to set a field for each item to either A if the total value of the order is >= $50 and set it to B if it's less than that. I've written the trigger and it works but only on each item.
For example, item 1 is $75 and item 2 is $25. Total of the order is $100 so I want the trigger to set both items to A. However, the trigger is working on each separately item 1 is being set to A and item 2 is being set to B.
Is it possible to make the trigger work on the entire order instead of each line item separately? If so, how? If not, is there another method I can use to accomplish this?
This is simplified version of my trigger so far:
ALTER TRIGGER Disposition
ON order_line
FOR UPDATE
AS
IF (##rowcount = 0)
RETURN
SET NOCOUNT ON
DECLARE #order_min int
SET #order_min = (SELECT DISTINCT SUM(item_price) order_min
FROM order_line ol
INNER JOIN inserted i ON i.order_no = o.order_no
IF #order_min >= 50
BEGIN
UPDATE order_line
SET disposition = 'A'
FROM inserted
INNER JOIN order_line ON inserted.order_no = order_line.order_no
AND inserted.item = order_line.item
WHERE order_line.order_no = inserted.order_no
AND inserted.line_no = order_line.line_no
RETURN
END
ELSE IF #order_min < 50
BEGIN
UPDATE order_line
SET disposition = 'B'
FROM inserted
INNER JOIN order_line ON inserted.order_no = order_line.order_no
AND inserted.item = order_line.item
WHERE order_line.order_no = inserted.order_no
AND inserted.line_no = order_line.line_no
RETURN
END
I have two table (InvoiceRequests, InvoiceRequestsLineItems), InvoiceRequestsLineItems has InvoiceRequestId as forign key, Qauntity and Rate columns.
My both tables has change tracking enabled.
I'm writing a query that returns the Only the InvoiceRequests that has been changed with there respective Amount(calculated from Qauntity and Rate columns of InvoiceRequestsLineItems).
the scenario is when I update/insert into LineItems I Join the LineItems table and ChangeTable to get the InvoiceRequestId. So I shows customers that 'InvoiceRequest' Amount changed.
But when I delete any LineItems I lost the InvoiceRequestId(foreign key) from that row. Then I cant able to tell the amount has been changed for perticular InvoiceRequests.
Is there any way that ChangeTable function can return other columns(InvoiceRequestId) apart from PrimaryKey(InvoiceRequestLineItemId).
Solution like adding trigger and keep the deleted record in separate table will increase lot of overhead.
Please provide any suggestion, that I can do with minimal changes. Thanks
SELECT
CTIRL.INVOICEREQUESTSID [InvoiceRequestId],
IR.STATUS [Status],
ERIL.[InvoiceRequestAmount],
CAST(0 as BIT) [Deleted]
FROM
(SELECT [IRS].INVOICEREQUESTSID, CTL.SYS_CHANGE_COLUMNS, CTL.sys_change_operation
FROM INVOICEREQUESTLINEITEMS [IRS]
JOIN CHANGETABLE( CHANGES dbo.INVOICEREQUESTLINEITEMS, #ctversion) CTL
ON CTL.INVOICEREQUESTLINEITEMSID = [IRS].INVOICEREQUESTLINEITEMSID
) AS CTIRL
LEFT JOIN dbo.INVOICEREQUESTS IR
ON CTIRL.INVOICEREQUESTSID = IR.INVOICEREQUESTSID
LEFT JOIN (
SELECT
IRLI.INVOICEREQUESTSID,
SUM(
IRLI.QUANTITY * IRLI.RATE + COALESCE(IRLI.TAXAMOUNT, 0)
) [InvoiceRequestAmount]
FROM
INVOICEREQUESTLINEITEMS IRLI
GROUP BY
IRLI.INVOICEREQUESTSID
) AS ERIL
ON ERIL.INVOICEREQUESTSID = RES.InvoiceRequestId
WHERE
(
CHANGE_TRACKING_IS_COLUMN_IN_MASK(5, CTIRL.SYS_CHANGE_COLUMNS) = 1
OR CHANGE_TRACKING_IS_COLUMN_IN_MASK(7, CTIRL.SYS_CHANGE_COLUMNS) = 1
OR CHANGE_TRACKING_IS_COLUMN_IN_MASK(11, CTIRL.SYS_CHANGE_COLUMNS) = 1
OR CTIRL.sys_change_operation = 'D'
OR CTIRL.sys_change_operation = 'I'
)
I'm comparing (one-way) two tables of different databases in SQL.
I want to update only the second table with the differences.
The comparing part gives me the correct results, but I have no clue how to get these result in the updating part
-- The comparing part DB1 with DB2
WITH RecordsWithUpdates AS
(SELECT DeviceID, DeviceName, DeviceNumber, Active FROM DB1.dbo.devices
EXCEPT
SELECT DeviceID, DeviceName, DeviceNumber, Active FROM DB2.dbo.devices
)
-- displaying the differences
Select * from DB2.dbo.Devices
WHERE DeviceID IN (SELECT DeviceID FROM RecordsWithUpdates)
-- Updating then columns of DB2
Update DB2.dbo.devices SET DeviceName = ?????, SET DeviceNumber = ??????, Active = ???????
The Code part with the ????? is where it should insert the found differences, but I cannot get it to work.
I would approach this with the UPDATE ... SET ... FROM ... JOIN syntax, like:
UPDATE
t2
SET
t2.DeviceName = t1.DeviceName,
t2.DeviceNumber = t1.DeviceNumber,
t2.Active = t1.Active
FROM
DB2.dbo.devices t2
INNER JOIN DB1.dbo.devices t1
ON t1.DeviceID = t2.DeviceID
AND NOT (
t1.DeviceName = t2.DeviceName
AND t1.DeviceNumber = t2.DeviceNumber
AND t1.Active = t2.Active
)
If you want at the same time to INSERT records that do not yet exists, then you can use the MERGE syntax instead:
MERGE DB2.dbo.devices t2
USING DB1.dbo.devices t1
ON (t1.DeviceID = t2.DeviceID)
WHEN MATCHED
THEN UPDATE SET
t2.DeviceName = t1.DeviceName,
t2.DeviceNumber = t1.DeviceNumber,
t2.Active = t1.Active
WHEN NOT MATCHED
THEN INSERT(DeviceID, DeviceName, DeviceNumber, Active)
VALUES (t1.DeviceID, t1.DeviceName, t1.DeviceNumber, t1.Active)
Finally: if you want to delete the records that exist in t2 but not in t1, just add this at the end of the MERGE query:
WHEN NOT MATCHED BY SOURCE
THEN DELETE;
I have found a couple of similar questions in here that I have used to try solving this case, but nothing seems to work for me.
So, basically I have a Pallet table, and I need to change Status field when Loaded field changes to a value <> to what it was or <> to NULL, and Status is also different. The trigger is basically doing nothing. So, I have:
ALTER TRIGGER [dbo].[TR_Status_Change]
ON [dbo].[Pallet]
AFTER UPDATE
AS BEGIN
SET NOCOUNT ON;
IF UPDATE (Loaded)
BEGIN
UPDATE [Pallet]
SET PStatus = 3
FROM [Pallet] P
INNER JOIN Inserted I ON P.ID = I.ID
WHERE P.PStatus <> 3
AND P.Loaded <> I.Loaded
AND I.Loaded IS NOT NULL
END
END
Where ID is primary key. Row values are as follows:
before update: Loaded = NULL, Status = 1
after: Loaded = 'somevalue' and Status remains = 1
expected: Status = 3
Thanks in advance.
"Something compared to NULL" comparisons are always false so this fails
AND P.Loaded <> I.Loaded
Try this. You need both "before" and "after" values of Loaded too
UPDATE [Pallet]
SET PStatus = 3
FROM [Pallet] P
INNER JOIN Deleted D ON P.ID = D.ID
INNER JOIN Inserted I ON P.ID = I.ID
WHERE P.PStatus <> 3
AND NOT EXISTS (SELECT I.Loaded INTERSECT SELECT D.Loaded)
AND I.Loaded IS NOT NULL
INTERSECT internally does a "is different from" comparison, not equality. If they are different, the intersect gives no rows, so NOT EXISTS gives true
For more on INTERSECT and EXCEPT, please see my answer here Why does EXCEPT exist in T-SQL?
Here's the thing:
P.Loaded <> I.Loaded
This will never be true, because you are comparing the data of the table after the update ([Pallet P]) with....the data that of the table after the update (Inserted I).
I think you want to change Inserted I to the deleted table, which holds the values BEFORE the update operation.
I have written an UPDATE trigger to look for changes in my Test.dbo.receiving_podio_dump table and then perform one of two different scenarios:
IF there is an existing receiving_id and existing sku_no match and this is an update to that combination in the Test.dbo.receiving_detail table, the appropriate row gets updated, OR
IF there is an existing receiving_id and but a new sku_no item, a new row needs to be added/inserted into the Test.dbo.receiving_detail table.
Here is my trigger:
CREATE TRIGGER trigger_receiving_detail_sku1_update
ON [Test].[dbo].[receiving_podio_dump]
FOR UPDATE
AS
BEGIN
--UPDATES DATA IN THE ROW FOR THAT RECEIVING LOG/SKU NO COMBO IF IT ALREADY EXISTS--
IF (SELECT ISNULL(COUNT(*), 0)
FROM inserted
LEFT JOIN dbo.receiving_detail AS rec ON (inserted.receiving_id = rec.receiving_id AND '001' = rec.sku_no)) <> 0
BEGIN
UPDATE [dbo].[receiving_detail]
SET receiving_assigned_id = inserted.receiving_assigned_id,
sku_id = sku1_id, sku = sku1, sku_lot = sku1_lot,
sku_whloc = sku1_whloc, sku_weight_of_unit = sku1_weight_of_unit,
sku_unit_of_weight_type = sku1_unit_of_weight_type,
sku_quantity_of_units = sku1_quantity_of_units,
sku_unit_of_measure = sku1_unit_of_measure,
sku_temperature = sku1_temperature
FROM inserted
WHERE ([dbo].[receiving_detail].receiving_id = inserted.receiving_id
AND [dbo].[receiving_detail].sku_no = '001')
END
--INSERTS A ROW FOR A NEW SKU NO FOR AN EXISTING RECEIVING LOG NUMBER--
IF (SELECT ISNULL(COUNT(*), 0)
FROM inserted
LEFT JOIN dbo.receiving_detail AS rec ON (inserted.receiving_id = rec.receiving_id AND '001' = rec.sku_no)) = 0
BEGIN
INSERT INTO [dbo].[receiving_detail] (receiving_id, receiving_assigned_id, sku_id, sku_no, sku, sku_lot, sku_whloc, sku_weight_of_unit, sku_unit_of_weight_type, sku_quantity_of_units, sku_unit_of_measure, sku_temperature)
SELECT
inserted.receiving_id, inserted.receiving_assigned_id, sku1_id,
'001', sku1, sku1_lot, sku1_whloc, sku1_weight_of_unit,
sku1_unit_of_weight_type, sku1_quantity_of_units,
sku1_unit_of_measure, sku1_temperature
FROM
inserted
WHERE
sku1 IS NOT NULL
END
END
GO
The first part of the trigger is working, and quantities of products are being updated on the Test.dbo.receiving_detail table if there is an update in the Test.dbo.receiving_podio_dump table.
However, if a totally new SKU is being added, a new row is not being inserted into my Test.dbo.receiving_detail table.
I am at a loss as to what to change, I have tried several things, any advice is welcome and appreciated. Thanks!
PS: the receiving log has a total of 24 SKU Items that can potentially be added or updated. I have only included the first trigger, but all of them follow this pattern.
I remove both IF statements and added those Conditions on the UPDATE and INSERT statements to support the scenario of a mass update with both conditions are true.
CREATE TRIGGER trigger_receiving_detail_sku1_update
ON [Test].[dbo].[receiving_podio_dump]
FOR UPDATE
AS
BEGIN
--UPDATES DATA IN THE ROW FOR THAT RECEIVING LOG/SKU NO COMBO IF IT ALREADY EXISTS--
UPDATE rec
SET receiving_assigned_id = i.receiving_assigned_id,
sku_id = sku1_id, sku = sku1, sku_lot = sku1_lot,
sku_whloc = sku1_whloc, sku_weight_of_unit = sku1_weight_of_unit,
sku_unit_of_weight_type = sku1_unit_of_weight_type,
sku_quantity_of_units = sku1_quantity_of_units,
sku_unit_of_measure = sku1_unit_of_measure,
sku_temperature = sku1_temperature
FROM inserted i
INNER JOIN [dbo].[receiving_detail] rec ON rec.receiving_id = i.receiving_id
WHERE (rec.sku_no = '001')
--INSERTS A ROW FOR A NEW SKU NO FOR AN EXISTING RECEIVING LOG NUMBER--
INSERT INTO [dbo].[receiving_detail] (receiving_id, receiving_assigned_id, sku_id, sku_no, sku, sku_lot, sku_whloc, sku_weight_of_unit, sku_unit_of_weight_type, sku_quantity_of_units, sku_unit_of_measure, sku_temperature)
SELECT
i.receiving_id, i.receiving_assigned_id, sku1_id,
'001', sku1, sku1_lot, sku1_whloc, sku1_weight_of_unit,
sku1_unit_of_weight_type, sku1_quantity_of_units,
sku1_unit_of_measure, sku1_temperature
FROM
inserted i
WHERE sku1 IS NOT NULL
AND NOT EXISTS (SELECT * FROM [dbo].[receiving_detail] rec WHERE rec.receiving_id = i.receiving_id AND rec.sku_no = '001')
END
GO