Display Results Before Delete SQL Server - sql-server

I'm so close to getting the answer to this, but I feel like I'm missing the final part.
I've created a DELETE trigger that should display values from the SELECT statement before they are deleted from the table. However, when the trigger is called I get a blank result, but the values (order_id=10001 AND product_id=25) are still deleted from the table.
I've verified the SELECT statement works before I've run the trigger, so I'm confident that part is correct.
I've tried using an INSTEAD OF DELETE trigger, but the values end up not being deleted. I don't believe there is a BEFORE DELETE function for SQL Server? Is there a work around?
Suggestions?
CREATE TRIGGER deleteOrderTrigger
ON order_details
FOR DELETE
AS
SELECT order_details.product_id, products.name,
order_details.quantity AS 'Quantity being deleted from order',
SUM(products.quantity_in_stock) + order_details.quantity AS 'In Stock Quantity after Deletion'
FROM order_details
LEFT JOIN products ON products.product_id = order_details.product_id
WHERE order_details.order_id = 10001 AND products.product_id = 25
GROUP BY order_details.product_id, products.name, order_details.quantity
GO
-- Below is the code that will fire the trigger
DELETE order_details
WHERE order_id = 10001 AND product_id = 25

#JoeC - using a proc is probably the best answer.
If you must use a trigger then remmeber that it must be coded to support sets. If someone executes delete order_details where order_id = 10001 then your trigger will need to return the stock level for every product on the order.
Also, when coding a trigger, you have access to a built in table named deleted. This table contains the records deleted.
So you can do something like this:
CREATE TRIGGER deleteOrderTrigger
ON order_details
FOR DELETE
AS
INSERT INTO deleted_order_products_log
SELECT order_details.product_id
,products.name
,[Quantity being deleted from order] = order_details.quantity
,[In Stock Quantity after Deletion] = SUM(products.quantity_in_stock) + order_details.quantity
FROM order_details
INNER JOIN deleted d
ON order_details.primaryKey = d.primaryKey
LEFT JOIN products
ON products.product_id = order_details.product_id
GROUP BY order_details.product_id
,products.name
,order_details.quantity;
You can then query the log file to get the results of the calculation.

I haven't used triggers a lot, but it seems you have an AFTER trigger, which is not able to read the deleted rows, that's why you get blank result.
The thing with triggers are the inserted and deleted tables, maybe this would help: https://learn.microsoft.com/en-us/sql/relational-databases/triggers/use-the-inserted-and-deleted-tables
You need a INSTEAD OF trigger as you mentioned, but you have to do the delete operation yourself. More on this subject here: https://learn.microsoft.com/en-us/sql/relational-databases/triggers/dml-triggers and one example in here: https://stackoverflow.com/a/3267726/5605866

Put the trigger results into an audit table and you will see the results of the trigger. I have never seen a trigger with a where clause similar to yours.
Do a insert into table x select.... You also want to use the deleted table(there is an inserted table) that is created just for each occurrence of the trigger that will contain any/all of the rows that were just deleted. The delete occurs in the main body of the code. The trigger is then invoked and the deleted table (you can do a delete * from deleted only in the trigger) that will contain the row(s) that were deleted.

Related

Why trigger is doing unnecessary subtraction after a query in sql?

I have two tables:
Order and
Product.
I want a specific column(OnShelfQuantity) in the Product table to be updated as a new row is added in the Order table. I have used the below query to implement a trigger which will do that. But the problem is that when I insert a row in the Order table and then later check the Product table to see the changes, I notice that the Product table has been updated 3 times. For e.g: Order quantity inserted = 10, then only 10 should be subtracted from Product_TAB.OnShelfQuantity. But 30 gets subtracted. Please help!
create trigger dbo.Trigge
ON dbo.Ordertable
AFTER INSERT
AS
BEGIN
update Product_TAB set OnShelfQuantity= Product_TAB.OnShelfQuantity - Ordertable.Quantity
FROM dbo.Product_TAB
INNER JOIN Ordertable
ON Ordertable.ProductID = Product_TAB.ProductID;
END;
I think, you can use INSERTED table to resolve this Issue.
Inserted table is a table which is used by triggers to store the frequently inserted records in the tables.
So, you can use the same in your update statement to avoid this.
update Product_TAB set OnShelfQuantity= Product_TAB.OnShelfQuantity -
Ordertable.Quantity
FROM dbo.Product_TAB
INNER JOIN Ordertable ON Ordertable.ProductID = Product_TAB.ProductID
INNER JOIN inserted INS ON INS.Order_ID=Ordertable.Order_ID
You can have multiple rows in the inserted table. And, these rows could have the same product. A row in the target table is only updated once in an update statement. Hence, you want to aggregate the data before the update:
create trigger dbo.Trigge
ON dbo.Ordertable
AFTER INSERT
AS
BEGIN
update p
set OnShelfQuantity= p.OnShelfQuantity - i.total_quantity
from dbo.Product_TAB p JOIN
(SELECT i.ProductId, SUM(i.Quantity) as total_quantity
FROM inserted i
GROUP BY i.ProductId
) i
on i.ProductID = p.ProductID;
END;
Note that this only uses inserted and not the original table.
So the issue was that I was inserting new rows but with the same Order ID. This is why it was doing additional subtraction which I didn't require. So now I have to just insert a new row but with unique OrderID. Thanks to everyone who replied above!

SQL Server trigger to update one value in a table after insert into another table if criteria are met

Update: Just to eliminate one possible suggestion, I saved the first query as a view and substituted it in the trigger. No change in behavior has been observed. I have updated the code below to reflect the new arrangement.
I have just started working with SQL Server triggers and have had some success. The current bit that I'm working on has me confused.
I am working with two tables. One is an order header and the other contains the line items. The trigger is watching for a new row to be inserted in the line item table and, if the item added meets certain criteria, update a value in the order header table. When I break my query down and run it separately, I get what I expect. A list of orders that contain the items I am watching for.
--QUERY WORKS AS EXPECTED
SELECT ord_no, cmt_cd_1
FROM OrderHeader
WHERE ord_no IN
(SELECT DISTINCT OrderHeader.ord_no
FROM OrderLine INNER JOIN OrderHeader
ON OrderLine.ord_no = OrderHeader.ord_no
WHERE (OrderLine.item_no LIKE 'A%') OR
(OrderLine.item_no LIKE 'B%') OR
(OrderLine.item_no LIKE 'C30%'))
However, when I try to use this in a trigger, it doesn't work.
--TRIGGER DOES NOT UPDATE ANY RECORDS
CREATE TRIGGER add_cmt_cd
ON [OrderLine]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE OrderHeader
SET cmt_cd_1 = 'O'
FROM OrderHeader
INNER JOIN inserted ON OrderHeader.ord_no = inserted.ord_no
WHERE (OrderHeader.ord_no IN (SELECT OrderListView.ord_no
FROM OrderListView))
END
I have verified that the header record is created first and does exist before the line item records are created. I have another trigger on this table that works fine, though the value it updates is in the same table as the trigger.
--TRIGGER WORKS AS DESIGNED
CREATE TRIGGER add_line_cmt
ON [OrderLine]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE OrderLine
SET cmt_1 = 'Comment 1',
cmt_2 = 'Comment 2'
FROM OrderLine
INNER JOIN inserted ON inserted.ord_no = OrderLine.ord_no
WHERE (OrderLine.item_no IN (SELECT CAST(item_no AS CHAR) AS item
FROM ItemTable
WHERE (group = 3)))
END
I've been banging my head against the desk for 3 hours and I know it's something simple I've missed. I'm hoping some other sets of eyes will spot it before I go nuts.

Triggers Inner join inserted with orginial table

I was reviewing creating DML triggers in SQL Server in SQL docs: Use the inserted and deleted Tables
There is an example which do the following:
The following example creates a DML trigger. This trigger checks to make sure the credit rating for the vendor is good when an attempt is made to insert a new purchase order into the PurchaseOrderHeader table. To obtain the credit rating of the vendor corresponding to the purchase order that was just inserted, the Vendor table must be referenced and joined with the inserted table. If the credit rating is too low, a message is displayed and the insertion does not execute.
Note that this example does not allow for multirow data modifications.
USE AdventureWorks2012;
GO
IF OBJECT_ID ('Purchasing.LowCredit','TR') IS NOT NULL
DROP TRIGGER Purchasing.LowCredit;
GO
-- This trigger prevents a row from being inserted in the Purchasing.PurchaseOrderHeader table
-- when the credit rating of the specified vendor is set to 5 (below average).
CREATE TRIGGER Purchasing.LowCredit
ON Purchasing.PurchaseOrderHeader
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM Purchasing.PurchaseOrderHeader p
JOIN inserted AS i ON p.PurchaseOrderID = i.PurchaseOrderID
JOIN Purchasing.Vendor AS v ON v.BusinessEntityID = p.VendorID
WHERE v.CreditRating = 5)
BEGIN
RAISERROR ('A vendor''s credit rating is too low to accept new purchase orders.', 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;
GO
I wonder why the example inner joined the inserted table with Purchasing.PurchaseOrderHeader table and then join the vendor table.
Can I get the same result using only the inserted table joining the vendor table directly without joining with Purchasing.PurchaseOrderHeader table?
Not only do I believe you are correct, I think there are further inaccuracies on this documentation page - which I have to admit is pretty rare in my experience.
Take for example this part:
Note that this example does not allow for multirow data modifications.
This is a false claim. The trigger code example will handle multiple rows insert as well as a single row insert.
Note that according to the documented (and observed) behavior, the inserted table will contain all the rows inserted (or updated) to the trigger's target table:
The inserted table stores copies of the affected rows during INSERT and UPDATE statements. During an insert or update transaction, new rows are added to both the inserted table and the trigger table. The rows in the inserted table are copies of the new rows in the trigger table.
Therefor, the join to inserted should be enough in this case to enforce the business rule discussed.
That being said, using triggers to enforce business rules might prove difficult and even problematic - note that this trigger only covers inserted rows, but not updated rows. This means that a new row might be inserted with valid values, and later on updated to invalid values.

Create Trigger to modify a row to the value on a joined table

I have a tables job_costcodes(id, cost_code_no, dept_id) and cost_codes(code_code_no, dept_id).
I am trying to make it so if job_costcodes.cost_code_no is modified, job_costcodes.dept_id is filled with the appropriate one from the cost_codes table, based on a matching code_code_no.
So referring to the tables below, if the top row in job_costcodes is changed to 10, the dept_id should change to 1212. Or 20 to 1313, etc.
I am not sure exactly how the syntax works... here is what I have so far.
UPDATE: updated code.. i think it works now.
create trigger update_test on dbo.job_costcodes
for update, insert
as
begin
set nocount on
update dbo.job_costcodes
set dept_id = (select CASE WHEN COUNT(1) > 0 THEN MIN(dbo.cost_codes.dept_id) ELSE NULL END as Expr1
FROM inserted INNER JOIN
dbo.cost_codes ON dbo.cost_codes.cost_code_no = inserted.cost_code_no)
from inserted as i
inner join dbo.[job_costcodes] on dbo.[job_costcodes].id = i.id
end
Treating your question as academic, start off by looking up the CREATE TRIGGER command in TSQL to get a solid understanding of the virtual tables inserted and deleted.
Then here is what I would do, in pseudo-cod-ish descriptive terms:
In your trigger, simply UPDATE job_costcodes and set the value of dept_id to the corresponding dept_id in cost_codes by JOINing to cost_codes and inserted in the FROM clause of the UPDATE.
There is no need to verify that the cost_code_no changed when doing this, the result will be the same, but if you feel you must do this, then look at the IF UPDATE() function in TSQL. You can then compare the value of cost_code_no in inserted vs deleted to know if it changed at all.

updating multiple tables with triggers - SQL Server

I need to update my tableA based on the insertions in tableB. i have created a trigger which fires when a row is inserted into tableB and simply copies it to tableA (which is exactly what i want). But the problem comes when i update my existing records in tableB, the rows in tableA already exist with the same primary key.
to deal with it, i tried to delete the existing row in tableA where the primary key matches for the updated row and then simply insert the row from the "inserted" table. But, tsql simply does not let me delete me from a trigger and gives an error stating "The multi-part identifier "INSERTED.Id" could not be bound."
Here is what i am trying to accomplish code-wise:
delete from dbo.CurrentItems where dbo.CurrentItems.CurrentItemId = INSERTED.Id
INSERT INTO CurrentItems
SELECT * FROM INSERTED
WHERE IsActive = 1
Any ideas guys?
In the DELETE FROM statement, you have to add the INSERTED pseudo-table to the tables you're operating on (in the FROM clause), like so:
DELETE dbo.CurrentItems
FROM dbo.CurrentItems
, INSERTED
WHERE dbo.CurrentItems.CurrentItemId = INSERTED.Id
INSERT INTO CurrentItems
SELECT * FROM INSERTED
WHERE IsActive = 1
Alternatively, you could use an INNER JOIN:
DELETE dbo.CurrentItems
FROM dbo.CurrentItems CI
INNER JOIN INSERTED I ON CI.CurrentItemId = I.Id
Why can't you just simply update the existing rows in TableA when your rows in TableB get updated?? UPDATE instead of DELETE/INSERT.
You could have a AFTER INSERT trigger which does what you want now, plus an AFTER UPDATE trigger which would be updating rows in TableA (instead of inserting new ones).
You said you have a trigger that fires when "a row is inserted into tableB". So you have the trigger to fire on Inserts. It sounds to me like you also have the trigger to fire on Updates as well. Did you want the trigger to fire on updates as well? If not, remove "Update" from the trigger definition.
Randy

Resources