SQL: Update columns based from a matched pattern in a same table - sql

So I have a table 'Accounts'. The entries below are all in the same table. I need to update entries based on a matched pattern. Notice the Ids have same pattern except after the underscore.
The old accounts statuses will change to inactive. For the new accounts, their statuses will change to active and their emails will be updated to the ones from the old accounts that matches the Id pattern.
Also, if there are new accounts that have identical patterns like for example X65423_KEL and X65423_KEL1, the email column of both entries would still be updated based on what is stored in X65432_REL.
FYI: The entries are already stored in the table. So All I just need is to update the entries.

Based on the OP's comment "You would know through their statuses. Old accounts are active and new ones are inactive before updating. This is somehow like a one-time update script" this seems that it's therefore as simple as:
UPDATE YourTable
SET [Status] = CASE [Status] WHEN 'Active' THEN 'Inactive'
WHEN 'Inactive' THEN 'Active'
ELSE [Status] END;
That will change every inactive account to active and every active account to inactive. If they have a different status, they will be unchanged.

I am not sure how big your Accounts table is or if there are additional columns you have to update. But given your data set one approach is to pull out the Active data and then apply it once for new accounts and then once for the old ones. Such as:
CREATE TABLE #Accounts(ID varchar(10), email varchar(50), Status varchar(10))
INSERT INTO #Accounts VALUES('X65423_REL','test#gmail.com','Active')
INSERT INTO #Accounts VALUES('X32562_LET','test2#gmail.com','Active')
INSERT INTO #Accounts VALUES('X65423_KEL','default#gmail.com','Inactive')
INSERT INTO #Accounts VALUES('X32562_OTK','default#gmail.com','Inactive')
SELECT ID, LEFT(ID, charindex('_', ID) - 1) LK_ID, Email
INTO #LookUp
from #Accounts
WHERE Status = 'Active'
UPDATE #Accounts
SET email = t2.email, Status = 'Active'
from #Accounts t1
INNER JOIN #LookUp t2 on LEFT(t1.ID, charindex('_', t1.ID) - 1) = t2.LK_ID
where t1.Status <> 'Active'
UPDATE #Accounts
Set Status = 'Inactive'
from #Accounts t1
INNER JOIN #LookUp t2 on t1.ID = t2.ID

Actually it can be done in single query.
Tested on MySQL 5.7.24
UPDATE accounts,
(
SELECT email as old_email FROM accounts
WHERE status = 'active'
) as temp
SET email = temp.old_email,
status = (SELECT IF(status = 'active', 'inactive', 'active'))

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'

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

Make a trigger to only trigger if a certain column is updated

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

SQL trigger on INSERT, and DELETE with 2 Tables

I've 2 tables called Patient and Diagnosis. as follows
Patient Diagnosis
--------- ------------
ID (PK) ID (PK)
Name PatientID (FK: Reference to Patient => ID)
Status
****** *****
---------- -----------
Here, Patient Status may be [Registered, Diagnosed and OnCourse]
During,
New Patient Insert, Patient Status will be Registered
New Diagnosis Insert, Patient Status will be Diagnosed
Now, On
Diagnosis Delete, I need to check If Patient has at least one Diagnosis entry in Diagnosis table, then Patient Status will be Diagnosed otherwise Registered
So, How to do all these conditions in Single Trigger?
Please help me.
Trigger to update Patient.Status based on INSERTs and DELETEs on the Diagnosis Table.
CREATE TRIGGER dbo.Diagnosis_TrgInsDel
ON dbo.Diagnosis
AFTER DELETE, INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Change the Status to 'Registered' after the last
-- Diagnosis record is deleted
--------------------------------------------------------------------------------
UPDATE
Patient
SET
[Status] = 'Registered'
FROM
Patient
INNER JOIN
Deleted
ON Deleted.PatientID = Patient.ID
WHERE
NOT EXISTS (SELECT * FROM Diagnosis WHERE PatientID = Deleted.PatientID)
-- Change the Status to 'Diagnosed' after an Insert and the
-- current Status is 'Registered'
--------------------------------------------------------------------------------
UPDATE
Patient
SET
[Status] = 'Diagnosed'
FROM
Patient
INNER JOIN
Inserted
ON Inserted.PatientID = Patient.ID
WHERE
Patient.[Status] = 'Registered'
END
I would actually have don this as two triggers. One for AFTER DELETE and one for AFTER INSERT. That would mean that the DELETE code wouldn't run when there is an INSERT and vice versa. But the above code will indeed work correctly.
EDIT
AS spotted by Nikola; if multiple diagnosise are inserted or updated, in the same operation, for the same patient, then this will potentially update a single patient record multiple times. These modifications should address that...
UPDATE
Patient
SET
[Status] = 'Registered'
WHERE
NOT EXISTS (SELECT * FROM Diagnosis WHERE PatientID = Patient.ID)
AND EXISTS (SELECT * FROM Deleted WHERE PatientID = Patient.ID)
And...
UPDATE
Patient
SET
[Status] = 'Diagnosed'
WHERE
Patient.[Status] = 'Registered'
AND EXISTS (SELECT * FROM Inserted WHERE PatientID = Patient.ID)
For the first part, you may simply add default constraint:
alter table patient add constraint df_status default 'Registered' for status
If this would not be enough because your front end is incapable of omitting status in insert or setting value to DEFAULT, you can create a trigger:
create trigger PatientInsertTrigger on patient
after insert
as
-- trigger should not alter ##rowcount
set nocount on
update patient
set status = 'Registered'
from Patient
-- Inserted is a pseudotable holding newly inserted rows
-- This is how we know what records to update
inner join Inserted
on Patient.ID = Inserted.ID
When records are added to or removed from diagnosis, patient status should be updated according to number of matching records in diagnosis. Luckily, at the time trigger is invoked records are already in table, so it is sufficient to take count() in both cases.
create trigger DiagnosisTrigger on diagnosis
after insert, delete
as
set nocount on
update patient
set status = case when d.NumberOfDiagnosis <> 0
then 'Diagnosed'
else 'Registered'
end
from Patient
inner join
(
select PatientID, count(*) NumberOfDiagnosis
from Diagnosis
-- Get all patients with added or removed diagnosis
where PatientID in (select PatientID
from Inserted
union
select PatientID
-- Pseudotable holding removed records
from Deleted)
group by PatientID
) d
on Patient.ID = d.PatientID
Status should be StatusID, but you did not mention appropriate id numbers.

Select row matching a condition if no other rows match a different condition

Very basic, but didn't know what this type of query is called...I want a query that will basically do this (pseudo-SQL):
SELECT name
FROM Table
WHERE activity_status != 'active'
AND there are no rows with that same name where activity_status = 'active'
(in other words, return names of the inactive only when not one with that name is active)
This will be used to allow users an option to reactivate inactive items, but I want to query the db to make sure that item isn't already active.
You're looking for a NOT EXISTS pattern.
"For each name that is not active, no rows exist for the same name that are active"
SELECT name FROM Table T1
WHERE activity_status != 'active'
AND
NOT EXISTS (SELECT *
FROM Table T2
WHERE
T2.activity_status = 'active' AND T1.name = T2.name)