MYSQL Trigger - storing updated table data - mysql

I have a table that tracks the number of hours an employee took on a job.
CREATE TABLE HOURLYWORKLOG (
EMPLOYEEREF INT(5) NOT NULL,
ORDERREF INT(5) NOT NULL,
HOURSWORKED VARCHAR(10),
TOTALPAY NUMERIC(10),
NOTES VARCHAR(10),
CONSTRAINT HOURLYWORKLOG_EMPLOYEES_FOREIGN_KEY FOREIGN KEY (EMPLOYEEREF) REFERENCES EMPLOYEES (EMPLOYEEREF),
CONSTRAINT HOURLYWORKLOG_ORDERS_FOREIGN_KEY FOREIGN KEY (ORDERREF) REFERENCES WORKORDER (ORDERREF));
I am looking to create a trigger that stores this data in a separate table if the hoursworked column is updated. Reading around, I can't see anything that explains what I need to do, at least I can't understand the steps involved after reading. As such my current solution is through creating a mirrored table (with a different name)
CREATE TABLE MODIFIEDHOURLYWORKLOG (
EMPLOYEEREF INT(5) NOT NULL,
ORDERREF INT(5) NOT NULL,
HOURSWORKED VARCHAR(10),
TOTALPAY NUMERIC(10),
NOTES VARCHAR(10));
And then creating a trigger as such
CREATE TRIGGER MODIFIEDHWL ON HOURLYWORKLOG
AFTER INSERT
AS
BEGIN
INSERT INTO MODIFIEDHOURLYWORKLOG
(EMPLOYEEREF, ORDERREF, HOURSWORKED, TOTALPAY, NOTES)
SELECT I.EMPLOYEEREF, I.ORDERREF, I.HOURSWORKED, I.TOTALPAY, I.NOTES
FROM HOURLYWORKLOG T
INNER JOIN INSERTED I ON T.EMPLOYEEREF-I.EMPLOYEEREF
END;
This obviously isn't working and is throwing up errors that my syntax is incorrect. I'm not sure how to write a trigger to transfer the data from the original table into the secondary one basically...
Thank you for any help.

You want to use trigger syntax for MySQL, not SQL Server. Something like this:
DELIMITER $$
CREATE TRIGGER MODIFIEDHWL ON HOURLYWORKLOG AFTER INSERT
FOR EACH ROW AS
BEGIN
INSERT INTO MODIFIEDHOURLYWORKLOG(EMPLOYEEREF, ORDERREF, HOURSWORKED, TOTALPAY, NOTES)
SELECT NEW.EMPLOYEEREF, NEW.ORDERREF, NEW.HOURSWORKED, NEW.TOTALPAY, NEW.NOTES
FROM DUAL
WHERE NOT NEW.HOURSWORKED <=> OLD.HOURSWORKED;
END;$$
DELIMITER ;

CREATE TRIGGER MODIFIEDHWL ON HOURLYWORKLOG
after update
for each row
AS
BEGIN
INSERT INTO MODIFIEDHOURLYWORKLOG
(EMPLOYEEREF, ORDERREF, HOURSWORKED, TOTALPAY, NOTES)
SELECT NEW.EMPLOYEEREF, NEW.ORDERREF, NEW.HOURSWORKED, NEW.TOTALPAY,
NEW.NOTES
from dual where NEW.HOURSWORKED != OLD.HOURSWORKED
END;
/

Related

After Creating after update trigger i cant update any

DROP TABLE IF EXISTS Sales;
CREATE TABLE Sales (
id INT AUTO_INCREMENT,
product VARCHAR(100) NOT NULL,
quantity INT NOT NULL DEFAULT 0,
fiscalYear SMALLINT NOT NULL,
fiscalMonth TINYINT NOT NULL,
CHECK(fiscalMonth >= 1 AND fiscalMonth <= 12),
CHECK(fiscalYear BETWEEN 2000 and 2050),
CHECK (quantity >=0),
UNIQUE(product, fiscalYear, fiscalMonth),
PRIMARY KEY(id)
);
DROP TABLE IF EXISTS log;
CREATE TABLE log (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
text VARCHAR(100)
);
Triggers
CREATE DEFINER=`root`#`localhost` TRIGGER `sales_AFTER_UPDATE` AFTER UPDATE ON `sales`
FOR EACH ROW
BEGIN
INSERT INTO log VALUES(NOW(),CONCAT('Update Student Record ', OLD.quantity));
END
UPDATE test for.sales SET quantity = 36
WHERE (id = 1);
ERROR 1136: 1136: Column count doesn't match value count at row 1
Iam new in mySQL Please help
You should specify columns in INSERT statement in your trigger explicitly, as you do not set all values in a row (auto incremented column excluded).
So it would be
INSERT INTO log(timestamp, text) VALUES (NOW(),CONCAT('Update Student Record ', OLD.quantity));
You have some errors.
First based on your trigger you need another column on log table which is as following
quantity INT NOT NULL DEFAULT 0
Second , do not use Keywords and Reserved Words like text and timestamp, it is a bad practice. If you do please put it inside backticks
Third your insert statement should be
INSERT INTO log(`timestamp`,`text`,quantity) VALUES(NOW(),'Update Student Record', OLD.quantity);
there is no need for CONCAT.
Fourth,
`sales`
table is not the same as Sales table, because you have used backticks.
Full working trigger below:
DELIMITER //
CREATE TRIGGER `sales_AFTER_UPDATE` AFTER UPDATE ON Sales
FOR EACH ROW
BEGIN
INSERT INTO log(`timestamp`,`text`,quantity) VALUES(NOW(),'Update Student Record', OLD.quantity);
END//
DELIMITER ;
Check working demo:
https://www.db-fiddle.com/f/iqwShcHK3AGJvU4MDbxDku/0

Cannot create trigger

I am just want to create after insert trigger to insert a new row in history table. Why am I getting an error when I run the query?
orders
create table orders
(
id int auto_increment
primary key,
id_user int not null,
picture_name varchar(100) not null,
time date not null,
constraint FK_USER
foreign key (id_user) references stef.users (id)
)
;
create index FK_USER_idx
on orders (id_user)
;
history
create table history
(
id int auto_increment
primary key,
id_order int not null,
id_action int not null,
time date not null,
constraint FK_ORDER
foreign key (id_order) references stef.orders (id),
constraint FK_ACTION
foreign key (id_action) references stef.actions (id)
)
;
create index FK_ORDER_idx
on history (id_order)
;
create index FK_ACTION_idx
on history (id_action)
;
my trigger...
CREATE TRIGGER orders_AFTER_INSERT
AFTER INSERT ON stef.orders
FOR EACH ROW
BEGIN
INSERT INTO history('id_order', 'id_action', 'time')
VALUES (NEW.id, 1, NOW());
END;
I am just want to create after insert trigger to insert a new row in history table. Why am I getting an error when I run the query?
Try this
DELIMITER $$
CREATE TRIGGER orders_AFTER_INSERT
AFTER INSERT ON stef.orders
FOR EACH ROW
BEGIN
INSERT INTO history(`id_order`, `id_action`, `time`)
VALUES (NEW.id, 1, NOW());
END$$
DELIMITER ;
You need to temporarily override the delimiter so MySQL can differentiate between the end of a statement within the body of a trigger (or procedure, or function) and the end of the body.
Edit: Single quotes (') are only ever used to denote string values, for field names use the ` (or in some configurations the ")
CREATE TRIGGER orders_AFTER_INSERT
AFTER INSERT ON stef.orders
FOR EACH ROW
BEGIN
INSERT INTO stef.history()
VALUES (null, NEW.id, 1, NOW());
END

MySql if else statement, if not valid in this position

I have a vet table and a medical table with a 1 to many relationship, and the ID's are auto incremented.
CREATE TABLE vet(
vetID INT NOT NULL AUTO_INCREMENT,
vetPractice varchar(35),
Address varchar(150),
contactNumber varchar (15),
PRIMARY KEY (VetID)
);
CREATE TABLE medical(
medicalID INT NOT NULL AUTO_INCREMENT,
medication VARCHAR (200),
PRIMARY KEY (medicalID),
FOREIGN KEY (vetID) REFERENCES vet(vetID)
);
Users can enter details of a vet, i want a query to determine;
if the the vet details entered already exist, then update the foreign key in vetID(medical) with the entered vetID.
else if the vet does not exist create a new vet and update the foreign key in vetID(medical) with the newly created vetID.
I have the following query
IF EXISTS(SELECT * FROM vet WHERE vetPractice = "inputValue")
THEN
UPDATE medical set value vetID = (Select max(vetID) from vet)
ELSE
INSERT INTO vet values (null, "newVetPractice", "NewAddress", "newContactNumber", "NewEmergencyNumber" );
Then
update medical set value vetID = (Select max(vetID) from vet);
END IF;
However, i am not familiar with if else's in mySQL is this the correct format, i have seen somethings about stored procedures.
Any help would be appreciate.
I'm not really clear about your logic; but it seems like you wanted it in a stored procedure format.
CREATE PROCEDURE 'sp_Med' (IN 'in_vetPractice' VARCHAR(35))
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
BEGIN
DECLARE ckExists int;
SET ckExists = 0;
SELECT count(*) INTO ckExists from vet WHERE vetPractice = in_vetPractice;
IF (ckExists > 0) THEN
UPDATE medical SET vetID = (Select max(vetID) FROM vet WHERE vetPractice = in_vetPractice)
ELSE
INSERT INTO vet VALUES (NULL, "newVetPractice", "NewAddress", "newContactNumber", "NewEmergencyNumber");
UPDATE medical SET vetID = LAST_INSERT_ID();
END IF;
END;
Execute it like
CALL sp_Med('newPractice')
I think you have to update your query, and this is the general syntax you have to use rather tha n yours:-
INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
This query will insert records if they are not present, and on presence it will update them.
So use this rather than your approach

How to create a trigger the triggers a self-referencing trigger...yeah

I have a MySQL database with...interesting architecture and a convoluted enrollment process. There are several program tables that need to insert rows on an historic enrollment table when they're updated, inserted or deleted. I've got that working using triggers on each of those tables (around 30 different tables) using the following iterated over all of the tables:
DROP TRIGGER IF EXISTS programTable_afterinsert;$$
CREATE TRIGGER programTable_afterinsert AFTER INSERT ON programTable
FOR EACH ROW
BEGIN
IF NEW.Enrolled = 1
THEN
INSERT INTO enrollment (ID, Action, Date_Updated, User, Program, Reason, Action_Date)
VALUES (NEW.ID, 'Enrolled', NOW(), 'programUser', 'programName', 'Enrolled in program', NOW());
ELSEIF NEW.Enrolled = 0
THEN
INSERT INTO enrollment (Member_ID, Action, Date_Updated, User, Program, Reason, Action_Date)
VALUES (NEW.ID, 'Disenrolled', NOW(), 'programUser', 'programName', 'Disenrolled from program', NOW());
END IF;
END;$$
DROP TRIGGER IF EXISTS programTable_afterupdate;$$
CREATE TRIGGER programTable_afterupdate AFTER UPDATE ON programTable
FOR EACH ROW
BEGIN
IF NEW.Enrolled = 1
THEN
INSERT INTO enrollment (ID, Action, Date_Updated, User, Program, Reason, Action_Date)
VALUES (NEW.Member_ID, 'Enrolled', NOW(), 'programUser', 'programName', 'Enrolled in program', NOW());
ELSEIF NEW.Enrolled = 0
THEN
INSERT INTO enrollment (ID, Action, Date_Updated, User, Program, Reason, Action_Date)
VALUES (NEW.ID, 'Disenrolled', NOW(), 'programUser', 'programName', 'Disenrolled from program', NOW());
END IF;
END;$$
DROP TRIGGER IF EXISTS programTable_afterdelete;$$
CREATE TRIGGER programTable_afterdelete AFTER DELETE ON programTable
FOR EACH ROW
BEGIN
IF OLD.Enrolled = 1
THEN
INSERT INTO enrollment (ID, Action, Date_Updated, User, Program, Reason, Action_Date)
VALUES (OLD.ID, 'Disenrolled', NOW(), 'programUser', 'programName', 'Removed from program', NOW());
END IF;
END;$$
A stripped down version of the enrollment and program tables can be created with the following:
delimiter $$
CREATE TABLE `programTable1` (
`ID` varchar(15) NOT NULL,
`Enrolled` tinyint(3) unsigned NOT NULL,
`Referral_Date` datetime NOT NULL,
`Referral_Source` varchar(255)
);$$
CREATE TABLE `programTable2` (
`ID` varchar(15) NOT NULL,
`Enrolled` tinyint(3) unsigned NOT NULL,
`Referral_Date` datetime NOT NULL,
`Referral_Source` varchar(255)
);$$
CREATE TABLE `enrollment` (
`ID` varchar(15) NOT NULL,
`Action` varchar(12) NOT NULL,
`Date_Updated` timestamp NOT NULL,
`User` varchar(12) default NULL,
`Program` varchar(25) NOT NULL,
`Notes` varchar(100) default NULL,
`Reason` varchar(45) default NULL,
`Action_Date` datetime NOT NULL
);$$
The hurdle I'm running into is the enrollment table needs to update the program tables' enrollment when it's modified or a row is added to it. Meaning if someone is enrolled on the program table, they need to have an entry on the enrollment table for that action; if someone is enrolled via the enrollment table, they need to be updated as enrolled or disenrolled on the program table that row applies to.
The main problem is that there are two different sources people are enrolling in the programs from.
Like I said, convoluted. I know the architecture of this application isn't the best, but it's not something that can be changed.
Any ideas would be welcome! Please let me know if there anyone has questions or if any clarification is needed. I've been working on this for awhile now, so I know I'm probably leaving some stuff out of the equation due to being so familiar with it.
There's no problem creating triggers that update each others' tables -- as long as the triggers don't continue to insert back and forth in an infinite loop.
You need to make sure that the INSERT performed by a trigger inserts a row to the other table that will not result in a reciprocal action.
I wrote an example in another recent question: Mirror tables: triggers, deadlock and implicit commits

MySQL Trigger on after insert

I am new to MySQL. I have two tables total_loaner and available_loaner. I am trying to create a trigger for every new row added in total_loaner, I would also like to add that new row to available_loaner.
Here how my tables look like:
CREATE TABLE `total_loaner` (
`Kind` varchar(10) NOT NULL,
`Type` varchar(10) NOT NULL,
`Sno` varchar(10) NOT NULL,
PRIMARY KEY (`Sno`)
)
CREATE TABLE `available_loaner` (
`Kind` varchar(10) NOT NULL,
`Type` varchar(10) NOT NULL,
`Sno` varchar(10) NOT NULL,
`Status` char(10) NOT NULL DEFAULT '',
PRIMARY KEY (`Sno`)
)
My trigger does not seem to work.
CREATE TRIGGER new_loaner_added
AFTER INSERT ON 'total_loaner' for each row
begin
INSERT INTO available_loaner (Kind, Type, Sno, Status)
Values (new.Kind, new.Type, new.Sno, 'Available');
END;
In your case you can rewrite your trigger like this
CREATE TRIGGER new_loaner_added
AFTER INSERT ON total_loaner
FOR EACH ROW
INSERT INTO available_loaner (Kind, Type, Sno, Status)
VALUES (NEW.Kind, NEW.Type, NEW.Sno, 'Available');
Note:
single quotes removed from table name total_loaner, because quotes effectively makes it a string literal instead of a proper identifier. You can use back ticks if you want but it's unnecessary since it's not a reserved word and it don't contain any special characters.
since it's a one-statement trigger now you don't need to use DELIMITER command and BEGIN...END block
Here is SQLFiddle demo
You probably need to set your delimiter:
DELIMITER $$
CREATE TRIGGER new_loaner_added
AFTER INSERT ON `total_loaner` for each row
begin
INSERT INTO available_loaner (Kind, Type, Sno, Status)
Values (new.Kind, new.Type, new.Sno, 'Available');
END$$
DELIMITER ;
Right now, it's confusing the semi-colon at the end of the INSERT statement with the end of the CREATE TRIGGER statement.
This one worked for me, more simplified version..
CREATE TRIGGER new_loaner_added
AFTER INSERT ON `DB1`.`table_name`
FOR EACH ROW
INSERT INTO `DB2`.`table_name` (messageID, conversationID, fromJID)
VALUES (NEW.messageID,NEW.conversationID, NEW.fromJID);
AFTER INSERT ON `total_loaner`
Use backticks.