Trigger referring to another subelement table - sql

In SQL Server there two tables: Invoices (InvoiceId, Number, Date, Customer, TotalValue) and InvoicesElements (InvoiceId, Good, Qty, Value).
Each transaction inserts one row into Invoices and one/many rows into InvoicesElements.
I need to set a trigger on the Invoices table that will raise an error and rollback transaction when Good in the InvoicesElements table is 'Bike' and Customer is 'ABC'.
Any help much appreciated.
Przemek

ALTER TABLE
InvoicesElements
ADD CONSTRAINT
CHK_GOOD
CHECK (good <> 'Bike' OR good IS NULL)
Update:
CREATE TRIGGER
TR_InvoicesElements_AIU
ON InvoicesElements
AFTER INSERT, UPDATE
AS
IF EXISTS
(
SELECT NULL
FROM INSERTED ie
JOIN Invoices inv
ON inv.id = ie.invoiceId
WHERE ie.good = 'bike'
AND inv.customer = 'ABC'
)
THROW 50000, 'Not sure why but you cannot sell bikes to ABC', 0
GO
CREATE TRIGGER
TR_Invoices_AIU
ON Invoices
AFTER INSERT, UPDATE
AS
IF EXISTS
(
SELECT NULL
FROM InvoiceElements ie
JOIN INSERTED inv
ON inv.id = ie.invoiceId
WHERE ie.good = 'bike'
AND inv.customer = 'ABC'
)
THROW 50000, 'Not sure why but you cannot sell bikes to ABC', 0
GO

Related

Trigger to insert multiple rows based on many-to-many relationship

I have three tables Reservation, Reservation_Passenger and Ticket.
Each reservation can have multiple passengers. I need to create a trigger to insert a ticket (according to the number of passengers) every time the Reservation status is updated to 'Booked'. How can I achieve it?
Reservation (reservationId, status)
Reservation_Passenger (reservationId, passengerId)
Ticket (ticketId, passengerId, issuedDate)
What I have tried:
CREATE
TRIGGER Generate_Ticket
ON Reservation
AFTER UPDATE
AS
DECLARE #reservationStatus varchar(15)
SELECT #reservationStatus = INSERTED.Status from INSERTED
IF #reservationStatus = 'Booked'
BEGIN
--stuck here
END
GO
The same way you store the status into a variable, you could also retrieve the reservationId
DECLARE #reservationStatus varchar(15)
DECLARE #reservationId int
SELECT #reservationId = INSERTED.reservationId,
#reservationStatus = INSERTED.Status
FROM INSERTED
Now in the part where you are stuck, to create a Ticket to every passenger on the reservation you can feed an INSERT with a SELECT of the related passengers.
INSERT INTO Ticket (passengerId, issuedDate)
SELECT passengerId, getdate()
FROM Reservation_Passenger
WHERE reservationId = #reservationId
PS You will need to be careful that your code doesn't change more than one reservation to booked on the same UPDATE command. Because in that case the trigger is only fired once, with all the updated reservations stored in the INSERTED dataset. You will need to use a CURSOR to loop through all those reservations to apply your logic, or switch to this simpler trigger that creates tickets for all the passengers of all the booked reservations in one single step:
CREATE TRIGGER Generate_Ticket ON Reservation AFTER UPDATE
AS
INSERT INTO Ticket (passengerId, issuedDate)
SELECT P.passengerId, getdate()
FROM INSERTED as R
INNER JOIN Reservation_Passenger as P on P.reservationId = R.reservationID
WHERE R.Status = 'Booked'
You should also be careful because the trigger fires when any field is updated on the Reservation table. If you were to update another field, for example a comment, on an already booked reservation, your trigger will duplicate all his tickets again.
I recommend you to check not only that INSERTED.Status = 'Booked', but also that DELETED.Status <> 'Booked', so you only create tickets when the Status field has changed to Booked from something else.
That would be :
CREATE TRIGGER Generate_Ticket ON Reservation AFTER UPDATE
AS
INSERT INTO Ticket (passengerId, issuedDate)
SELECT P.passengerId, getdate()
FROM INSERTED as I
INNER JOIN DELETED as D on D.reservationId = I.reservationID
INNER JOIN Reservation_Passenger as P on P.reservationId = I.reservationID
WHERE I.Status = 'Booked' and coalesce(D.Status, '') <> 'Booked'

Update multiple columns by ids

I have tables called tblPurchase and tblInvoice. When I Insert new record I want to update my TotalStock form tblInvoice. I tried following code.
Create procedure [dbo].[spUpdateTotalStock] #Stock int, #PurchaseId int
As
Begin
Begin Transaction
Update tblPurchase
Set TotalStock = #Stock
Where PurchaseId = #PurchaseId
RollBack Transaction
end
It worked as expected. But I want to update TotalStock column by many ids. It means when I insert more than one invoice at the same time, I want to update my TotalStock column. like this
tblInvoice
ItemName | Quantity
---------+----------
Phone 3
Mouse 6
tblPurchase
ItemName | TotalStock
-----------+------------
phone 3
Mouse 6
ID comes with parameter. Always more than 10 ids come with parameter.
When I Insert new record I want to update my TotalStock form tblInvoice
This would normally be handled via a trigger not a stored procedure. That would look something like this:
create trigger trig_invoice_update on tblInvoice
after insert, update, delete
begin
update p
set quantity = p.quantity + coalesce(i.quantity, 0) - coalesce(d.quantity, 0)
from tblPurchase p left join
(select i.itemName, sum(i.quantity) as quantity
from inserted i
group by i.itemName
) i
on p.itemName = i.itemName left join
(select d.itemName, sum(d.quantity) as quantity
from deleted d
group by d.itemName
) d
on p.itemName = d.itemName
where d.quantity <> 0 or i.quantity <> 0;
end;
This version handles inserts, updates, and deletes, but assumes that all items are already in the purchases table.

IF ELSE condition with SQL Server trigger

I am populating a table entirely using triggers, it populates the table if LocationID and ProductID does not exists and if it already does it updates the given data.
I have posted the following code snippet looking for a possible solution or link to one.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[newpurchase]
ON [dbo].[PurchaseMaster]
AFTER INSERT
AS
BEGIN
IF (((SELECT COUNT(*) FROM StockMaster
WHERE LocationID = (SELECT LocationID FROM inserted)) > 0)
AND ((SELECT COUNT(*) FROM StockMaster
WHERE LocationID = (SELECT LocationID FROM inserted)) > 0))
UPDATE StockMaster
SET TotalPurchased = TotalPurchased + (SELECT PurchasedQTY FROM inserted)
WHERE LocationID = (SELECT LocationID FROM inserted)
AND ProductID = (SELECT ProductID FROM inserted);
ELSE
INSERT INTO StockMaster (LocationID, ProductID, TotalPurchased, TotalSold, OnHand)
SELECT LocationID, ProductID, PurchasedQTY, 0, 0 FROM inserted;
END
I am not entirely sure exactly what you are looking for. If your return sets cannot be collected dynamically, and you need another table to always update based off the information persisted by another another table then I do think triggers are fine. There is always overhead with triggers, so keep that in mind.
You could do the way you are doing it, and from what I can see on the surface level the trigger may be what you are looking for (but this also depends upon your business needs and relationships of tables). HOWEVER, you will have to be careful if your Insert is a bulk insert then what you have will throw an error.
I also noticed your If statement is checking LocationId twice instead of LocationId and ProductId.
The trigger you are trying to create works with only one value at a time. You could rewrite this to a more set based trigger for when bulk inserts occur. This way if you have one insert or bulk inserts these two queries can perform for that values where needed.
first you want to update all the values where LocationId and ProductId are the >same found in StockMaster
if no values match then nothing will be updated
Update StockMaster
Set TotalPurchased = sm.TotalPurchased + i.PurchasedQty
From StockMaster sm
inner join inserted i on sm.LocationId = i.LocationId and sm.ProductId = i.ProductId
next you want to insert any row that isn't found in StockMaster
if all values matched above, they will be weeded out with the wheree condition >and,then nothing would be inserted here, those that didn't match would be
inserted
INSERT INTO StockMaster (LocationID, ProductID, TotalPurchased, TotalSold, OnHand)
SELECT i.LocationID, i.ProductID, i.PurchasedQTY, 0, 0
FROM inserted i
left join StockMaster sm on i.LocationId = sm.LocationId and i.ProductId = sm.ProductId
where sm.{Id} is null;--not {id} is for whatever key this table uses

ORACLE SQL Status check trigger

I wanted to create a trigger which when a new order is inserted in the ORDERS table the trigger updates the status level of a customer in the CUSTOMER table, depending on how many orders they have made. If a customer has 0 orders his status level is 'new' else if he has more than 1 he is a 'standard' customer.
The trigger below works if I add the customer_no in manually:
create or replace TRIGGER STATUS_CHECK
AFTER INSERT ON ORDERS
DECLARE
n INT;
BEGIN
SELECT COUNT(ORDER_NO)
INTO n
FROM ORDERS
INNER JOIN CUSTOMER
ON CUSTOMER.CUSTOMER_NO = ORDERS.CUSTOMER_CUSTOMER_NO
WHERE CUSTOMER.CUSTOMER_NO = '01';
IF(n > 1) THEN
UPDATE CUSTOMER
SET CUSTOMER.STATUS_LEVEL = 'STANDARD'
WHERE CUSTOMER.CUSTOMER_NO = '01';
END IF;
END;

SQL Update in Insert clause

I have a query where I am inserting some records as long as those records do not exist in a table, but I would really like to do is to UPDATE a field of that record in that other table if it is there otherwise insert it as it is doing it right now. I would appreciate any help, or advises. Can it be done?
-- Insert into my my Toys table if record not already there (but would like to also update one of its fields)
INSERT INTO Toys (Date, ToyId)
SELECT inv.Date, inv.Id
FROM Inventory inv
JOIN InventoryStats invSt ON inv.Id = invSt.InventoryId
WHERE (invSt.IsFixed = 1 AND invSt.IsSent = 0 AND invSt.Date >= '01/01/2012 12:00 PM') AND (inv.Id NOT IN (SELECT ToyId FROM Toys))
Basically if the inv.Id is already in Toys table I do not want to insert it again but I would like to update one of its flags while on this process , like
UPDATE Toys SET NewShipDate = inv.Date WHERE ToyId = Inv.Id
You are looking for the MERGE Statement - INSERT a record if it does not exist, UPDATE it if it does.