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.
Related
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'
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
I'm trying to make a trigger to only trigger if a certain column is updated, and then only if the column is updated to 'Executed'. I can update if the column is changed, but I can't seem to find a way to update if column is updated to 'Executed'
CREATE TRIGGER dbo.NewTrigger
ON dbo.Database
AFTER UPDATE
AS
IF Update(Status) = 'Executed'
BEGIN
--MY insert into statement. This adds data to another table, but I only want the whole process to run if the original table column "Status" is set to "Executed"
END
Could someone assist please?
You'd need to use the inserted and deleted tables in the trigger, see here:
Use Inserted and Deleted Tables
In case of an update:
inserted table: contains new column values of rows that have been updated
deleted table: contains old column values of rows that have been updated
Your trigger could look something like this:
create table t (id int identity, status varchar(100));
create table audit(id int, old_status varchar(100), new_status varchar(100), updated_at datetime);
create trigger StatusUpdate
on t
After UPDATE as
if (update(status)
and exists(select * from inserted i
inner join deleted d on d.id = i.id
where d.status != 'Executed'
and i.status = 'Executed'))
begin
insert into audit (id, old_status, new_status, updated_at)
select i.id, d.status, i.status, getdate()
from inserted i
inner join deleted d on d.id = i.id
where d.status != 'Executed'
and i.status = 'Executed'
end
See Demo on DB Fiddle
Demo 2 - Multiple rows updated together
I have a table in my database with productinstances in it. The tables name is productinstance with the columns id, imei, invoicedate, invoiceNo, serialNr, debitStartDate.
In this table about 68 instances have a doublet (the same imei nr). I want to delete the doublet.
The problem is that there is info on the doublet that I need in the original record, so Before I delete the doublet I would like to merge the 2 records together.
On all the original records invoiceNo and invoicedate are null, And on all the doublet serialno have a null value.
So i would like to take the value of invoiceNo and invoicedate from the dublet post merge it into the original record and then delete the doublet.
Is this possible. If yes how?
Not very sure below is what you are looking for, you'd better provider us with some exmaples, but if it is, here is one of the way to achieve :to add an extra column for the derived field.
ALTER TABLE productinstance
ADD ExtraColumn as ISNULL(invoiceNo,'') + ISNULL(CAST(invoicedate) as VARCHAR(50),'')
Then you can delete any records based on the merged column.
One simple way to do it is by using a self join for the update statement and another self join for the delete statement:
UPDATE t0
SET invoiceNo = t1.invoiceNo,
invoicedate = t1.invoicedate
FROM productinstance t0
INNER JOIN productinstance t1 ON t0.imei = t1.imei
WHERE t0.invoiceNo IS NULL
AND t0.invoicedate IS NULL
AND t1.invoiceNo IS NOT NULL
AND t1.invoicedate IS NOT NULL
DELETE t0
FROM productinstance t0
INNER JOIN productinstance t1 ON t0.imei = t1.imei
AND t0.invoiceNo = t1.invoiceNo
AND t0.invoicedate = t1.invoicedate
AND t0.serialno IS NULL
I have 2 tables Employee and Employee_Backup
Employee has 3 columns
IDEmployee
Name
Status
and Employee_Backup also has 3 columns:
IDEmployee
Detail
Status
For every row inserted into or updated in table Employee, I want to set the Status in table Employee_Backup using this criteria
WHERE employee_backup.IDEmployee = employee.IDEmployee (inserted / updated)
Something like that??
CREATE TRIGGER tr_Employee_Insert
ON dbo.Employee
FOR INSERT
AS
UPDATE b
SET Status = 'Inserted'
FROM dbo.Employee_Backup b
INNER JOIN Inserted i ON b.EmployeeID = i.EmployeeID
CREATE TRIGGER tr_Employee_Update
ON dbo.Employee
FOR UPDATE
AS
UPDATE b
SET Status = 'Updated'
FROM dbo.Employee_Backup b
INNER JOIN Inserted i ON b.EmployeeID = i.EmployeeID
You basically need to join the Inserted pseudo table which contains all rows that have been inserted (or updated) from the base table (dbo.Employee) and the Employee_Backup table - and then use that result set from the JOIN as the basis for your UPDATE statement.
Note: this will NOT insert any new rows into Employee_Backup when you add new rows to dbo.Employee - is that what you want? If not, you'd have to change the FOR INSERT trigger a bit ....