SQL Server ON DELETE trigger not working as expected - sql-server

I'm trying to create a trigger in my database.
I have two tables, one for customer information and one for customer orders.
I want to create a FOR DELETE trigger so when I delete an order from the order table, the cost of that order should be reduced from the customer's account.
First I create an AFTER INSERT trigger, it works well!
Here is the code:
ALTER TRIGGER [dbo].[set_customer_debt]
ON [dbo].[orders]
AFTER INSERT
AS
BEGIN
UPDATE customer
SET debt = debt + (SELECT cost FROM orders
INNER JOIN services ON services.service# = orders.service#
WHERE orders.order# IN (SELECT order# FROM inserted))
WHERE customer# IN (SELECT customer# FROM inserted)
END
Then I copied this and change it to FOR DELETE, but it just return NULL for debt:
ALTER TRIGGER [dbo].[delete_customer_debt]
ON [dbo].[orders]
FOR DELETE
AS
BEGIN
UPDATE customer
SET debt = debt - (SELECT cost FROM orders
INNER JOIN services ON services.service# = orders.service#
WHERE orders.order# IN (SELECT order# FROM deleted))
WHERE customer# IN (SELECT customer# FROM deleted)
END
Where is the problem?

Related

SQL Server : AFTER INSERT trigger and INSERT INTO

I want to create a trigger that will fill up my sales history base after firing in the ORDER table.
I am creating a specific order in regular data base and after that this order automatically goes to sales_history database.
Below part works properly.
When I create a new order in regular database my sales_history database is growing with new ID_ORDERS, hooray! :)
ALTER TRIGGER [dbo].[InsertTrig]
ON [dbo].[ORDER]
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM inserted i
WHERE i.ID_TYPE = 1) -- specific order type
BEGIN
INSERT INTO id.dbo.sales_history (id_order)
SELECT i.ID_ORDER FROM inserted i
END
The problem arises when I want join another table. The trigger stops working
ALTER TRIGGER [dbo].[InsertTrig]
ON [dbo].[ORDER]
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM inserted i
WHERE i.ID_TYPE = 1) -- specific order type
BEGIN
INSERT INTO id.dbo.sales_history (id_order, id_item)
SELECT
inserted.ID_ORDER, ORDER_DETAILS.ID_ITEM
FROM
inserted
INNER JOIN
ORDER_DETAILS ON ORDER_DETAILS.ID_ORDER = inserted.ID_ORDER
END
I also tried this way, and still nothing :(
ALTER TRIGGER [dbo].[InsertTrig]
ON [dbo].[ORDER]
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM inserted i
WHERE i.ID_TYPE = 1) -- specific order type
BEGIN
DECLARE #xyz AS numeric(18, 0)
SET #xyz = (SELECT inserted.ID_ORDER FROM inserted)
INSERT INTO id.dbo.sales_history (id_order, id_item)
SELECT
ORDER.ID_ORDER, ORDER_DETAILS.ID_ITEM
FROM
ORDER
INNER JOIN
ORDER_DETAILS ON ORDER_DETAILS.ID_ORDER = ORDER.ID_ORDER
WHERE
ORDER.ID_ORDER = #xyz
END
I want to create a trigger that will automatically fill up my sales history base after firing in ORDER table.
Your trigger is on the Order table, meaning SQL Server fires it after you insert records into the Order table. At which point, the relevant records in the Order_Details table couldn't have been inserted yet, because they have a foreign key to the Order table.
This is why an inner join between your inserted table and the Order_details table returns 0 rows.
If you want your sales_history from the order_details table, you have to populate it after you insert the records to the order_details table.
CREATE OR ALTER TRIGGER [dbo].[OrderDetails_AfterInsert]
ON [dbo].[ORDER_DETAILS]
AFTER INSERT
AS
INSERT INTO id.dbo.sales_history (id_order, id_item)
SELECT
inserted.ID_ORDER, inserted.ID_ITEM
FROM
inserted
INNER JOIN
[ORDER] ON [ORDER].ID_ORDER = inserted.ID_ORDER
WHERE [ORDER].ID_TYPE = 1 -- specific order type
As a side note: InsertTrig is bad name. Note the name of the trigger in my answer - it tells you exactly what this trigger is for, and on what table.

Creating Trigger to get data from one table to another and generating a timestamp

I am trying to track Inventory where data would be entered in an Excel sheet (SQL Spreads) and then updates the SQL table and then gather the sum of that data and put it onto another table that would then generate a timestamp to when it was changed/updated/inserted.
The Pictures with highlighted columns is where I want to have the data in.
(TotalBinLo --> Binlocation)
and then when Binlocation is populated (inserted/updated/deleted) generating a timestamp (MM/DD/YYYY HH:MM:SS)
This is what I've come up so far.
---This Trigger is working when pulling data from one table into another--
Create Trigger tr_BC_totalbinLoc
on bincount
After Update
AS
Begin
update OnHandInv
set OnHandInv.binlocation = bincount.totalbinlo
from bincount
inner join OnHandInv on bincount.partnumber = OnHandInv.PartNumber;
End
---Another Trigger (Works) but enters in date for all rows. (Don't want) (only need for one column.)
Create Trigger tr_totalbinLoc_OHI
On Onhandinv
After Update
AS
Update Onhandinv
set dateupdated = getutcdate()
where PartNumber in (select distinct PartNumber from onhandinv)
totalbinlo
ColNeedToPopu
You need to join the inserted table to OnHandInv. I have assumed that PartNumber is the primary key to join on.
You also need to remove deleted rows which exactly match, in other words rows where no change has actually been made.
CREATE OR ALTER TRIGGER tr_BC_totalbinLoc
ON bincount
AFTER UPDATE
AS
SET NOCOUNT ON;
IF NOT EXISTS (SELECT 1 FROM inserted)
RETURN;
UPDATE o
SET
binlocation = i.totalbinlo
dateupdated = getutcdate()
FROM (
SELECT partnumber, totalbinlo
FROM inserted i
EXCEPT
SELECT partnumber, totalbinlo
FROM deleted d
) i
INNER JOIN OnHandInv o ON i.partnumber = i.PartNumber;
GO

How do I use where clause in the sql trigger?

I am trying to create a trigger where the account balance is automatically updated from the transaction table, but I am not being able to update a particular table. My trigger changes the account balance of every customer. I want to know how I can use where clause to make it customer-specific. Here is what I have:
create trigger trig_one
on dbo.transactions
for insert, update, delete
as begin
declare #amount int
select #amount = (select amount from inserted)
update dbo.account set balance = balance + #amount
end
Please help me out. Thanks!
You structure the query like this:
update dbo.account
set balance = balance + i.amount
from dbo.account a join
inserted i
on i.accountid = a.accountid ;
(The join key is whatever the join key should be.)
Of course, you need to do the same thing for the deleted. Note: this will work for multiple rows being inserted. That is handy. Your version will fail unexpectedly.

Microsoft sql trigger update multiple lines

I have a two tables, a Customers and a Sales table.
I am trying to create a trigger to update the amount of sales in the customer table when the Sales table is updated.
CREATE TRIGGER salesUPDATE
ON SALES
AFTER INSERT
AS
UPDATE Customers
SET salesAmount = Sales.Amount
GO
But I get that sales does not exist. Should I be using a join?
Will this trigger update all columns or do I need to specify which column to update?
CREATE TRIGGER salesUpdate ON SALES
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
;WITH cteAffectedCustomers AS (
SELECT DISTINCT CustomerId
FROM
inserted
UNION
SELECT DISTINCT CustomerId
FROM
deleted
)
, cteAggregations AS (
SELECT
ca.CustomerId
,SUM(ISNULL(s.Amount,0)) as SalesAmount
,COUNT(s.SalesId) as NumOfSales
FROM
cteAffectedCustomers ca
INNER JOIN Customers c
ON ca.CustomerId = c.CustomerId
LEFT JOIN Sales s
ON ca.CustomerId = s.CustomerId
GROUP BY
ca.CustomerId
)
UPDATE c
SET SalesAmount = ca.SalesAmount
,NumOfSales = ca.NumOfSales
FROM
Customers c
INNER JOIN cteAggregations ca
ON c.CustomerId = ca.CustomerId
END
Here is an example of the type of logic you would need to create to maintain a pre-agregated value. If you want to SUM an Amount in the Sales table you will need an AFTER INSERT, UPDATE, and DELETE. Then you would need to:
determine all of the affected customers So you don't update the entire customer table
do the aggregation
update with an inner join to the aggregated data
A note about triggers, they are a set based operation NOT a scalar. That means they fire once for x# of rows NOT x# of times for x# of rows. So you have to account for multiple records during updates and do joins just like would outside of a trigger when updating one table with another.
This has a performance impact to write operations but does expedite your reads, however if you are not in an extremely extremely high read volume operation you would do better to use a view/query and optimize your indexes. There is less likely hood of the synchronization of the aggregate data getting messed up. If you do go the trigger route I suggest you also have a SQL job set up on some reasonable increment (nightly) that checks and rectifies any inconsistencies that may occur.
Use magic table inserted
CREATE TRIGGER salesUPDATE
ON SALES
AFTER INSERT
AS
BEGIN
Declare #Amount varchar(50) = (Select top 1 Amount from inserted)
UPDATE Customers
SET salesAmount = #Amount
END
GO
Note : top 1 for insertion of multiple records
Use the inserted table name which contains the new value:
CREATE TRIGGER salesUPDATE
ON SALES
AFTER INSERT
AS
UPDATE Customers
SET salesAmount = inserted.Amount
GO

I would like to know on how to convert Oracle triggers into SQL Server triggers

As I understand, SQL SERVER Triggers does not support FOR EACH ROW. Also I am aware that you have to use inserted tables and deleted tables. Other than that, I have no clue how to write SQL Server triggers. They look so different. Can some help please?
Below is the code for Oracle Triggers
create or replace TRIGGER Ten_Percent_Discount
BEFORE INSERT OR UPDATE ON Bookings
FOR EACH ROW
DECLARE CURSOR C_Passengers IS
SELECT StatusName
FROM Passengers
WHERE PassengerNumber = :NEW.Passengers_PassengerNumber;
l_status_name Passengers.StatusName%TYPE;
BEGIN
OPEN C_Passengers;
FETCH C_Passengers INTO l_status_name;
CLOSE C_Passengers;
Below is what I have written so far. I know I am using the inserted tables wrong
IF l_status_name = 'Regular'
THEN
:New.TotalCost := 0.90 * :New.TotalCost;
END IF;
END;
create TRIGGER Ten_Percent_Discount
ON Customer
FOR INSERT ,UPDATE
AS
DECLARE C_Passengers CURSOR FOR
SELECT StatusLevel
FROM Customer
WHERE CustomerID = inserted.CustomerID
Thanks for all the help in advance.
Table structure for customer
Table structure for Order
Below answer is only for reference purpose that you can use to build gradually towards final solution:
create table dbo.customer
(
customerid varchar(10),
firstname nvarchar(50),
statuslevel varchar(50)
)
go
create table dbo.customerorder
(
orderid varchar(10),
totalprice numeric(5,2),
productid varchar(10),
customerid varchar(10)
)
go
go
create trigger dbo.tr_customer on dbo.customer for insert,update
as
begin
update co
set co.totalprice = .9*co.totalprice
from dbo.customerorder co
inner join inserted i
on co.customerid = i.customerid
where i.statuslevel = 'Standard'
end
go
--test for above code
insert into dbo.customer values (1,'jayesh','')
insert into dbo.customerorder values (1,500.25,1,1)
insert into dbo.customerorder values (1,600.25,2,1)
select * from dbo.customer
select * from dbo.customerorder
update dbo.customer set statuslevel = 'Standard' where customerid = 1
select * from dbo.customer
select * from dbo.customerorder
But what I am pretty sure is that when customer is created for the first time, there will not be any orders to apply discounts on, so you will certainly need UPDATE Trigger as well.

Resources