Using Triggers for UPDATE Tables MySQL - mysql

I have a TEMP table that is fed data using a Stored Proc when the table is shown through the website. This works great. I have this table as an editable one so that if/when the data in this table is edited then it will send the updated data back to my TEMP table, again this updates as it should. I now have a Trigger that will take certain fields from the TEMP and update my LIVE db table.
My problem is that when I add in other fields to update based on what is UPDATED it will do so for each row within the TEMP table. I have tried various ways, below being my last attempt to just update that particular row.
CREATE
DEFINER = 'user'#'%'
TRIGGER update_epcplanneddate
AFTER UPDATE
ON ims.temp_tobebooked
FOR EACH ROW
BEGIN
UPDATE epc_planning p
INNER JOIN temp_tobebooked b ON b.PropID = p.PropID
SET
p.EPCStatus = CASE WHEN p.PropID = b.PropID THEN 3 END,
p.EPCPlannedDate = b.EPCPlannedDate,
p.EPCBookingDate = CASE WHEN p.PropID = b.PropID THEN NOW() END,
p.LastUpdateDate = CASE WHEN p.PropID = b.PropID THEN NOW() END,
p.LastUpdateUser = b.InputBy
WHERE p.PropID = b.PropID;
END
How can I update what I need to update along with the extra fields based on this UPDATE?

So changing the JOIN to using NEW.colum the Trigger now looks like this
CREATE
DEFINER = 'user'#'%'
TRIGGER update_epcplanneddate
AFTER UPDATE
ON temp_tobebooked
FOR EACH ROW
BEGIN
UPDATE epc_planning p
SET
p.EPCStatus = 3,
p.EPCPlannedDate = NEW.EPCPlannedDate,
p.EPCBookingDate = NOW(),
p.LastUpdateDate = NOW(),
p.LastUpdateUser = NEW.InputBy
WHERE p.PropID = NEW.PropID;
END

Related

How do I create a trigger with Inner Joins

I'm trying to create a trigger to populate an aud Loan Table when there is an insert in the Loan table. I want this aud Table to have data from both the Loan table and another table, so I'm trying to set variables that get this data.
When creating the trigger I'm getting the error "Unknown system variable 'var1'"
This is the database layout:
https://cdn.discordapp.com/attachments/582912082450710528/583696750322253824/unknown.png
DELIMITER $$
CREATE TRIGGER Loan_Insert AFTER INSERT ON loan
FOR EACH ROW
BEGIN
SET var1 =
(SELECT loan_type.type_of_loan
FROM loan INNER JOIN loan_type ON
loan.loan_type_idloan_type = loan_type.idloan_type
AND
loan.loan_type_idapp_type = loan_type.idapp_type
WHERE loan.loan_type_idloan_type = new.loan_type_idloan_type
AND loan.loan_type_idapp_type = new.loan_type_idapp_type);
SET var2 =
(SELECT loan_type.app_type
FROM loan INNER JOIN loan_type ON
loan.loan_type_idloan_type = loan_type.idloan_type
AND
loan.loan_type_idapp_type = loan_type.idapp_type
WHERE loan.loan_type_idloan_type = new.loan_type_idloan_type
AND loan.loan_type_idapp_type = new.loan_type_idapp_type);
INSERT INTO Aud_Loan(bk_Loan, type_of_loan, type_of_loan_description, application_type,
application_type_description, insert_date)
VALUES(new.idloan, new.loan_type_idloan_type, var1, new.loan_type_idapp_type, var2,CURDATE());
END $$
Local variables must be DECLAREd. docs
When using SET, the right hand side must only return one value (single result with single field).
In you particular case, you can probably tweak the queries used in your SET statements like this:
SET var1 = (
SELECT loan_type.type_of_loan
FROM loan_type
WHERE loan_type.idloan_type = NEW.loan_type_idloan_type
AND loan_type.idapp_type = NEW.loan_type_idapp_type
);
and you can probably even reduce it to one query with SELECT INTO:
SELECT loan_type.type_of_loan, loan_type.app_type
INTO var1, var2
FROM loan_type
WHERE loan_type.idloan_type = NEW.loan_type_idloan_type
AND loan_type.idapp_type = NEW.loan_type_idapp_type
;

MySQL trigger to update other table

I have 2 tables:
table 1 = SAMPLE_TABLE
table 2 = RESULT_TABLE (with proposed trigger)
I would like to use a trigger in RESULT table that, when a new record is inserted into into it, will update a field in SAMPLE table called, 'status' = "complete". The field 'status' to be updated in SAMPLE is related to RESULT by:
table 1 = SAMPLE_TABLE ('client_sampleID')
=
table 2 = RESULT_TABLE ('sampleID')
This is the proposed trigger
CREATE DEFINER = `user`#`%` TRIGGER `database`.`RESULT_TABLE_BEFORE_INSERT`
AFTER INSERT ON `RESULT_TABLE` FOR EACH ROW
BEGIN
UPDATE SAMPLE_TABLE
SET status = 'complete'
WHERE SAMPLE_TABLE.client_sampleID = RESULT_TABLE.sampleID;
END
My questions:
is this above trigger OK?
there are 100+ 'client_sampleID' (all same, entered as a batch) per 'sampleID'. Is there a more efficient way of setting the 'status' so that it happens only after encountering the first instance?
You are very close. You just need to use new in the trigger:
CREATE DEFINER = `user`#`%` TRIGGER `database`.`RESULT_TABLE_BEFORE_INSERT`
AFTER INSERT ON `RESULT_TABLE` FOR EACH ROW
BEGIN
UPDATE SAMPLE_TABLE st
SET status = 'complete'
WHERE st.client_sampleID = new.sampleID;
END

Trigger: Referencing updated attribute

I have a Trigger on UPDATE.
What is the correct procedure for referencing attribute from the table that is not updated by the UPDATE SQL command? Is the attribute still in the UPDATE variable? I would like to get the value of that attribute for the updated row.
You can access a values of a column before update and after update in MySQL by using keywords OLD and NEW.
For example if you want to determine whether a value of a column actually has been changed during updated you can do
IF NOT OLD.column_name <=> NEW.column_name THEN
-- do something here
END IF;
Note: <=> is NULL-safe comparison operator in MySQL
BTW: There is no UPDATED virtual table in MySQL. It's from SQL Server.
Here is a SQLFiddle demo. Note that even though update affected all records in the table, only one message has been logged in log table. It's because value for a row with id 2 in the end stayed the same.
UPDATE: to keep your finished flag in sync you need triggers for all events (insert, update, delete).
DELIMITER //
CREATE TRIGGER tg_ai_event
AFTER INSERT ON event
FOR EACH ROW
BEGIN
UPDATE activity a
SET status = (EXISTS(SELECT *
FROM event
WHERE activity = a.activity_id
AND done = 0))
WHERE activity_id = NEW.activity;
END//
CREATE TRIGGER tg_ad_event
AFTER DELETE ON event
FOR EACH ROW
BEGIN
UPDATE activity a
SET status = (EXISTS(SELECT *
FROM event
WHERE activity = a.activity_id
AND done = 0))
WHERE activity_id = OLD.activity;
END//
CREATE TRIGGER tg_au_event
AFTER UPDATE ON event
FOR EACH ROW
BEGIN
IF NOT OLD.activity <=> NEW.activity THEN
-- if activity id was changed for an event then clculate finished flag
-- for both old and new activity id
UPDATE activity a
SET status = (EXISTS(SELECT *
FROM event
WHERE activity = a.activity_id
AND done = 0))
WHERE activity_id IN(OLD.activity, NEW.activity);
ELSE
-- otherwise calculate finished flag only if done flag is changed
IF NOT OLD.done <=> NEW.done THEN
UPDATE activity a
SET status = (EXISTS(SELECT *
FROM event
WHERE activity = a.activity_id
AND done = 0))
WHERE activity_id = NEW.activity;
END IF;
END IF;
END//
DELIMITER ;
Here is SQLFiddle demo

Updating table values in a trigger depending on data subset

OK, the title is a mouthful.
Basically, it means that when dealing with rows from the inserted table, depending on a value in a specific column, which splits the rows into one of two subsets, the data get dealt with in one of two manners. Now this could have been iterated over with a cursor, likely CTE too, but is there another way, the following (pseudo) code looks UGLY and doesn't actually work, but it gives you an idea of what I'm looking for:
Trigger
ALTER trigger [dbo].[tcdA_Combined_ActiveUnitShiftExpiredStatus_UpdateShift] on [dbo].[cd_units] after update as
begin
SET NOCOUNT ON
IF UPDATE(shift_start)
BEGIN
IF(inserted.ag_id <> 'FIRE')
BEGIN
update cd_units set shift_expired_status = 0
from inserted
where inserted.unid = cd_units.unid and inserted.shift_start <= dbo.get_dts()
END
ELSE
BEGIN
update cd_units set shift_expired_status = 0
from inserted
where inserted.unid = cd_units.unid and inserted.shift_start >= dbo.get_dts()
END
update cd_units set sask911_shift_end = (select substring(shift_start,5,2)+'/'+substring(shift_start,7,2)
+' '+substring(shift_start,9,2)+':'+substring(shift_start,11,2) from inserted)
from cd_units join inserted on cd_units.unid=inserted.unid;
END
END
As always, thanks in advance for all the help
I think the main problem here is that you are treating inserted as a single row, whereas all triggers in SQL Server are table level triggers. Thus, inserted is a table, and you can't compare a column to a single value. I would do it with something like the following.
This part is for both branches:
update cd_units set shift_expired_status = 0
from inserted
where inserted.unid = cd_units.unid
and inserted.shift_start <= dbo.get_dts();
This part only updates when inserted.ag_id = 'FIRE':
update cd_units
set sask911_shift_end = substring(inserted.shift_start,5,2) + '/' +
substring(inserted.shift_start,7,2) + ' ' +
substring(inserted.shift_start,9,2) + ':' +
substring(inserted.shift_start,11,2)
from cd_units join inserted on cd_units.unid=inserted.unid
where inserted.ag_id = 'FIRE';

Finding min and max value of the table in a constant time

I have a table which contains relative large data,
so that it takes too long for the statements below:
SELECT MIN(column) FROM table WHERE ...
SELECT MAX(column) FROM table WHERE ...
I tried index the column, but the performance still does not suffice my need.
I also thought of caching min and max value in another table by using trigger or event.
But my MySQL version is 5.0.51a which requires SUPER privilege for trigger and does not support event.
It is IMPOSSIBLE for me to have SUPER privilege or to upgrade MySQL.
(If possible, then no need to ask!)
How to solve this problem just inside MySQL?
That is, without the help of OS.
If your column is indexed, you should find min(column) near instantly, because that is the first value MySQL will find.
Same goes for max(column) on an indexed column.
If you cannot add an index for some reason the following triggers will cache the MIN and MAX value in a separate table.
Note that TRUE = 1 and FALSE = 0.
DELIMITER $$
CREATE TRIGGER ai_table1_each AFTER INSERT ON table1 FOR EACH ROW
BEGIN
UPDATE db_info i
SET i.minimum = LEAST(i.minimum, NEW.col)
,i.maximum = GREATEST(i.maximum, NEW.col)
,i.min_count = (i.min_count * (new.col < i.minumum))
+ (i.minimum = new.col) + (i.minimum < new.col)
,i.max_count = (i.max_count * (new.col > i.maximum))
+ (i.maximum = new.col) + (new.col > i.maximum)
WHERE i.tablename = 'table1';
END $$
CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
DECLARE new_min_count INTEGER;
DECLARE new_max_count INTEGER;
UPDATE db_info i
SET i.min_count = i.min_count - (i.minimum = old.col)
,i.max_count = i.max_count - (i.maximum = old.col)
WHERE i.tablename = 'table1';
SELECT i.min_count INTO new_min_count, i.max_count INTO new_max_count
FROM db_info i
WHERE i.tablename = 'table1';
IF new_max_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MAX(col) as new_max FROM table1) m
SET i.max_count = 1
,i.maximum = m.new_max;
END IF;
IF new_min_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MIN(col) as new_min FROM table1) m
SET i.min_count = 1
,i.minimum = m.new_min;
END IF;
END $$
DELIMITER ;
The after update trigger will be some mix of the insert and delete triggers.