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
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 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
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'm trying to update a temporary table called #deletedRecords which looks like this:
With the data from a table called log that looks like this:
The KeyValue in the log table is the same as the ID in #deletedRecords.
There is a column in #deletedRecords for every FieldName for any particular key value.
I tried to extract the values using the following query:
UPDATE #deletedRecords
SET PatientName = (SELECT ACL.OldValue WHERE ACL.FieldName = 'CptCode'),
ChargeNotes = (SELECT ACL.OldValue WHERE ACL.FieldName = 'ChargeNotes'),
Units = (SELECT ACL.OldValue WHERE ACL.FieldName = 'Units'),
ChargeStatusID = (SELECT ACL.OldValue WHERE ACL.FieldName = 'Units')
FROM Log ACL
JOIN #deletedRecords DR ON ACL.KeyValue = DR.ID
WHERE ACL.TableName = 'BillingCharge'
AND ACL.EventType = 'DELETE'
However when I run the query all of the columns to be updated in #deletedRecords are null. Can somebody please help explain what I'm missing?
Thanks in advance.
EDIT:
In response to #Yogesh Sharma's answer, I elected to use the CTE method. I would this that using the values from the CTE to join to additional tables and extract their values during the update.
e.g. The Log table doesn't contain an old value for the StatusName but it does contain the ChargeStatusID which could be used to join to another table that contains that information such as this table ChargeStatus:
Thus I modified #Yogesh Sharma's code to the following:
WITH cte AS
...
UPDATE d
SET d.PatientName = c.PatientName
, d.StatusName = cs.StatusName
FROM #deletedBillingChargeTemp d
JOIN cte c ON c.KeyValue = d.chargeID
JOIN ChargeStatus cs ON c.ChargeStatusID = cs.ChargeStatusID
However, once I add that secondary join, all of the updated values return to null as they were before #Yogesh Sharma's suggestions were implemented.
Your query does not work because the UPDATE is executed multiple times for each row in DR, considering only the conditions specified in the last three rows of your query (not the ones specified in the subqueries). The values that remain in the table are the ones that correspond to the ACL row used in the last execution (and the order of execution cannot be controlled). If for ACL row used in the last execution the subqueries return NULL, you will get a NULL result.
See the example in the https://learn.microsoft.com/en-us/sql/t-sql/queries/update-transact-sql topic, where it says "The results of an UPDATE statement are undefined if the statement includes a FROM clause that is not specified in such a way that only one value is available for each column occurrence that is updated, that is if the UPDATE statement is not deterministic.".
You should rewrite your query like this:
UPDATE #deletedRecords
SET PatientName = (
SELECT ACL.OldValue FROM Log ACL
WHERE ACL.FieldName = 'CptCode' AND ACL.KeyValue = DR.ID
AND ACL.TableName = 'BillingCharge' AND ACL.EventType = 'DELETE'
),
ChargeNotes = (
SELECT ACL.OldValue FROM Log ACL
WHERE ACL.FieldName = 'ChargeNotes' AND ACL.KeyValue = DR.ID
AND ACL.TableName = 'BillingCharge' AND ACL.EventType = 'DELETE'
),
Units = (
SELECT ACL.OldValue FROM Log ACL
WHERE ACL.FieldName = 'Units' AND ACL.KeyValue = DR.ID
AND ACL.TableName = 'BillingCharge' AND ACL.EventType = 'DELETE'
),
ChargeStatusID = (
SELECT ACL.OldValue FROM Log ACL
WHERE ACL.FieldName = 'Units' AND ACL.KeyValue = DR.ID
AND ACL.TableName = 'BillingCharge' AND ACL.EventType = 'DELETE'
)
FROM #deletedRecords DR
You would required to do some conditional aggregation for log table and do the JOINs in order to update the temporary table #deletedRecords records
So, the conditional approach could be achieve via CTE or Subquery
WITH cte AS
(
SELECT KeyValue,
MAX(CASE WHEN FieldName = 'CptCode' THEN OldValue END) PatientName,
MAX(CASE WHEN FieldName = 'ChargeNotes' THEN OldValue END) ChargeNotes,
...
FROM Log
WHERE TableName = 'BillingCharge' AND EventType = 'DELETE'
GROUP BY KeyValue
)
UPDATE d
SET d.PatientName = c.PatientName,
...
FROM #deletedRecords d
INNER JOIN cte c ON c.KeyValue = d.ID
The other way is to update your temporary table via correlation approach
UPDATE d
SET d.PatientName = (SELECT TOP 1 OldValue FROM Log WHERE KeyValue = d.ID AND
TableName = 'BillingCharge' AND EventType = 'DELETE' AND FieldName = 'CptCode'),
d.ChargeNotes= (SELECT TOP 1 OldValue FROM Log WHERE KeyValue = d.ID AND
TableName = 'BillingCharge' AND EventType = 'DELETE' AND FieldName = 'ChargeNotes'),
...
FROM #deletedRecords d
If your updated columns are NULL, these are it's possible causes:
Since you are doing a INNER JOIN, records might not be joining correctly by their joining column. Make sure both tables have the same values on the joining columns.
Since you are filtering in a WHERE clause, records might not fulfill your TableName and EventType filters. Make sure there are records that sucessfully INNER JOIN between them and they have the supplied TableName and EventType.
The values you are asigning are NULL. Make sure your subqueries return a not null value.
Table reference is off. When updating a table in SQL Server, always use the updating table alias if you are using one.
Use
UPDATE DR SET
YourColumn = Value
FROM
Log ACL
JOIN #deletedRecords DR ON -...
Instead of
UPDATE #deletedRecords SET
YourColumn = Value
FROM
Log ACL
JOIN #deletedRecords DR ON -...
Make sure you are NOT checking the variable table values on another batch, script or procedure. Variable tables scope are limited to current batch or procedure, while temporary tables remain as long as the session is alive.
Make sure that there isn't another statement that is setting those values as NULL after your update. Also keep an eye on your transactions (might not be commited or rolled back).