MySQL Autoupdate Timestamp and trigger execution order - mysql

Is there any documentation (couldn't fine in https://dev.mysql.com/doc/refman/5.5/en/timestamp-initialization.html) that specifies whether an automatic update on Timestamp column or trigger executes first?
CREATE TABLE IF NOT EXISTS `table1` (
`id` VARCHAR(255) NOT NULL,
update_time TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`id`)
);
CREATE TABLE IF NOT EXISTS `table2` (
`id` VARCHAR(255) NOT NULL,
update_time TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`id`)
);
delimiter $$
DROP TRIGGER if EXISTS `trigger_upd_table1` $$
CREATE TRIGGER `trigger_upd_table1`
AFTER UPDATE ON `table1`
FOR EACH ROW BEGIN
INSERT INTO `table2` VALUES (new.`id`, new.`update_time`);
END $$
delimiter ;
INSERT INTO `table1` (`id`) VALUES ('100');
UPDATE `table1` t1
SET t1.id = '101'
WHERE t1.id = '100';
To further clarify with an example. When I run the update query for the single record in table1, will the automatic update on update_time column execute first or the trigger that is specified on table1. Currently, I am testing in v8.0.15 and seeing that automatic update on update_time column takes place first. Then the updated record is inserted into table2.
But is there any documentation that specifies the behavior?

Related

How to find the recently modified row in tables of a MySQL database?

I have a MySQL database (employee) consists of 5 tables. How to find the recently modified row by column name id in tables?
I tried to find the table using the coding below. It works fine.
USE
information_schema;
SELECT DISTINCT TABLE_NAME
FROM TABLES
WHERE
UPDATE_TIME IS NOT NULL AND UPDATE_TIME < NOW() AND TABLE_SCHEMA = 'employee'
Please help to find the row by column name id (all tables have this column name as identifier) in that table.
I think the work around for this problem is to create a trigger that will insert a record into a different table
CREATE TABLE `table_row_monitor` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tbl` varchar(100) DEFAULT NULL,
`col` varchar(100) DEFAULT NULL,
`val` int,
`dtecreated` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`process_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TRIGGER IF EXISTS chk_pickup_contact_for_delivery;
DELIMITER $$
CREATE TRIGGER `[table_name]_log_row_insert` AFTER INSERT ON `[table_name]` FOR EACH ROW
BEGIN
INSERT INTO table_row_monitor (tbl, col, val)
SELECT '[table_name]', 'id', MAX(id) FROM `[table_name]`;
END$$
DELIMITER ;
just replace the [table_name]

SQL After Update Trigger

Im having some trouble with sql triggers in MySQL. The tables below are just used for testing.
First table:
CREATE TABLE `test`.`t1` (
`c1` INT NOT NULL,
`c2` VARCHAR(45) NULL,
`c3` VARCHAR(45) NULL,
PRIMARY KEY (`c1`));
Second Table:
CREATE TABLE `test`.`t2` (
`cc1` INT NOT NULL,
`cc2` VARCHAR(45) NULL,
`cc3` VARCHAR(45) NULL,
PRIMARY KEY (`cc1`));
cc1 references c1
ALl i want to do is, if the values in c2 are updated, then the corresponding field should be updated in cc2.
The trigger i came up with is:
DROP TRIGGER IF EXISTS `test`.`t1_AFTER_UPDATE`;
DELIMITER $$
USE `test`$$
CREATE DEFINER = CURRENT_USER TRIGGER `test`.`t1_AFTER_UPDATE` AFTER UPDATE ON `t1` FOR EACH ROW
BEGIN
update t2
set cc2=c2
where t2.cc1=t1.c1;
END$$
DELIMITER ;
This executes with no errors but if i try to update the values of c2,
UPDATE `test`.`t1` SET `c2` = '23' WHERE (`c1` = '11');
Operation failed: There was an error while applying the SQL script to
the database. ERROR 1054: 1054: Unknown column 't1.c1' in 'where
clause'
The update goes through if i remove the trigger.
In a Trigger, we refer to columns in the rows being updated/inserted/deleted by NEW (after operation), and OLD (before operation) keywords.
You will need to use these keywords in order to be able to update the other table.
Also, we can optimize the Trigger to initiate UPDATE operation only when there has been a change observed in the c2 value.
DROP TRIGGER IF EXISTS `test`.`t1_AFTER_UPDATE`;
DELIMITER $$
USE `test`$$
CREATE DEFINER = CURRENT_USER TRIGGER `test`.`t1_AFTER_UPDATE`
AFTER UPDATE ON `t1` FOR EACH ROW
BEGIN
-- Check if there has been any change in the c2 value or not
IF (NEW.c2 <> OLD.c2) THEN
-- Update only when there is some change
UPDATE t2
SET cc2 = NEW.c2 -- access the recent updated value of c2 using NEW keyword
WHERE cc1 = NEW.c1; -- access the c1 value of updated row using NEW keyword
END IF;
END$$
DELIMITER ;

MySQL: Integrity error when inserting into database

So, I have this one column in my table that gets filled by a trigger when a new entry is inserted.
CREATE TABLE `users` (
`idusers` int(11) NOT NULL AUTO_INCREMENT,
`uid` char(64) DEFAULT NULL,
`uname` varchar(80) NOT NULL,
`password` char(128) NOT NULL,
`mail` varchar(120) NOT NULL,
PRIMARY KEY (`idusers`),
UNIQUE KEY `uname_UNIQUE` (`uname`),
UNIQUE KEY `mail_UNIQUE` (`mail`),
UNIQUE KEY `uid_UNIQUE` (`uid`)
);
DELIMITER $$
TRIGGER `flask`.`users_BEFORE_INSERT` BEFORE INSERT ON `users` FOR EACH ROW
BEGIN
set new.uid = sha2(new.idusers, 256);
END$$
DELIMITER ;
The problem now is that when I try to add a new row (only have a test one yet because of the error), in the trigger the value of new.idusers is somehow always 0 instead of the current auto_increment value.
What do I need to change in my trigger code so that the value used for generating the uid is the actual id and not always 0?
Since idusers is an AUTO_INCREMENT field, its value is known only after the record is inserted in the table.
Use an AFTER INSERT trigger instead of a BEFORE INSERT trigger, and update the newly inserted record:
DELIMITER $$
CREATE TRIGGER `flask`.`users_AFTER_INSERT` AFTER INSERT ON `users` FOR EACH ROW
BEGIN
UPDATE users SET uid = sha2(NEW.idusers, 256) WHERE idusers = NEW.idusers;
END $$
DELIMITER ;

Two MySQL timestamp columns in one table

I would like to create a table that has both a column for "created" and another for "updated". The column "created" will be set at insert and never change. The column "updated" will change every time a row is updated. I don't want to mess with either of these columns in the subsequent INSERT or UPDATE statements. So what should my CREATE TABLE statement look like if I start with something like this?
CREATE TABLE IF NOT EXISTS `mydb`.`mytable` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`updated` TIMESTAMP,
`created` TIMESTAMP,
`deleted` TINYINT DEFAULT 0,
`notes` TEXT DEFAULT '',
`description` VARCHAR(100)
) TYPE=innodb;
I seem to be having trouble creating a table with two TIMESTAMP columns. I don't care if the columns are TIMESTAMP or DATETIME or whatever, I just want them to be populated by MySQL without explicit instructions from the insert or update statements.
I would like to be able to do inserts like this:
INSERT INTO `mydb`.`mytable` (notes,description) VALUES ('some note','some description');
and updates like this:
UPDATE `mydb`.`mytable` SET notes=CONCAT(notes,'some more notes') WHERE id=1;
both without having to explicitly set the "created" column or set (or reset) the "updated" column in the insert or update statement.
Try this one to create your table:
CREATE TABLE IF NOT EXISTS db.test_table
(
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
created DATETIME DEFAULT NULL,
updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted TINYINT DEFAULT 0,
notes TEXT DEFAULT NULL,
description VARCHAR(100)
)
Note that
updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
will allow to update this field automatically.
And set this one for a trigger before inserting records:
DELIMITER $$
CREATE
/*[DEFINER = { user | CURRENT_USER }]*/
TRIGGER `db`.`on_before_insert` BEFORE INSERT
ON `db`.`test_table`
FOR EACH ROW BEGIN
SET new.created = NOW();
END$$
DELIMITER ;
Then you can use this to insert:
INSERT INTO db.test_table(description) VALUES ("Description")
and to update your record
UPDATE db.test_table SET description = "Description 2" where Id=1
And your created and updated fields will be set appropiately.
News flash: In mysql, TIMESTAMP columns are always updated with now() every time any other column in the row is updated - this is a deliberate feature of this datatype.
DATETIME on the other hand does not have this weird behaviour - it's completely normal.
The answer: created must be DATETIME, but due to this bug, you also need a trigger, like this:
CREATE TABLE IF NOT EXISTS mytable (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`updated` TIMESTAMP, -- This will be updated to now(), if you don't set it or set it to null
`created` DATETIME NOT NULL, -- This will never be magically updated once written
`deleted` TINYINT DEFAULT 0,
`notes` TEXT DEFAULT '',
`description` VARCHAR(100)
) TYPE=innodb;
DELIMITER ~
CREATE TRIGGER mytable_insert_trigger
BEFORE INSERT ON mytable
FOR EACH ROW BEGIN
SET NEW.created = CURRENT_TIMESTAMP;
END;~
DELIMITER ;
insert into mytable (notes) values ('test');
select * from mytable;
+----+---------------------+---------------------+---------+-------+-------------+
| id | updated | created | deleted | notes | description |
+----+---------------------+---------------------+---------+-------+-------------+
| 1 | 2011-07-05 11:48:02 | 2011-07-05 11:48:02 | 0 | test | NULL |
+----+---------------------+---------------------+---------+-------+-------------+
Try this:
CREATE TABLE IF NOT EXISTS mydb.mytable
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
updated DATETIME,
created TIMESTAMP,
deleted TINYINT DEFAULT 0,
notes TEXT DEFAULT '',
description VARCHAR(100)
) TYPE=innodb;
Edit: Use a trigger.
CREATE TRIGGER mytable_update
BEFORE UPDATE ON mydb.mytable
FOR EACH ROW SET NEW.updated = NOW();
alternative is to change the order of timestamp column
OR
set first column DEFAULT value like this
ALTER TABLE `tblname` CHANGE `first_timestamp_column`
`first_timestamp_column` TIMESTAMP NOT NULL DEFAULT 0;
Reference
Unfortunately MySQL doesn't let you have two TIMESTAMP columns in one table. I would use ON UPDATE CURRENT_TIMESTAMP for the updated column and set created manually using the NOW() function.

MySQL two column timestamp default NOW value ERROR 1067

I have table as shown below. In order to workaround one default now column restriction of MySQL I used the tip as shown here
CREATE TABLE IF NOT EXISTS mytable (
id INT NOT NULL AUTO_INCREMENT ,
create_date TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00' ,
update_date TIMESTAMP NULL DEFAULT NOW() ON UPDATE NOW() ,
PRIMARY KEY (`parti_id`) )
ENGINE = InnoDB;
My sql_mode does not include NO_ZERO_DATE as pointed here my output :
mysql> SELECT ##sql_mode;
+------------+
| ##sql_mode |
+------------+
| |
+------------+
1 row in set (0.00 sec)
It is still giving the error as shown below:
ERROR 1067 (42000) at line xx in file: '/myschema.sql': Invalid default value for 'create_date'
I use MySQL 5.1.37 on Ubuntu
How can I fix it? Thanks.
You can only have one timestamp column that defaults to CURRENT_TIMESTAMP or NOW() per table. This is a well known bug in MySQL.
To overcome this, make your default for the created column a valid timestamp value, then insert the timestamp in your CRUD application code.
Use NOW() or CURRENT_TIMESTAMP for your updated column default.
Reference material:
http://dev.mysql.com/doc/refman/5.1/en/timestamp.html
To further illustrate MySQL's shortcoming in this area, consider the following code:
CREATE TABLE testing_timestamps (
id INT NOT NULL AUTO_INCREMENT,
pk_id INT NOT NULL,
col1 TIMESTAMP DEFAULT 0,
col2 TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(id)
);
delimiter $$
CREATE TRIGGER testing_timestamps_trigger
AFTER INSERT ON testing_timestamps
FOR EACH ROW
BEGIN
UPDATE testing_timestamps SET col1 = NOW() WHERE id = MAX(id);
END;
$$
delimiter ;
INSERT INTO testing_timestamps (id) VALUES (0);
The output from this will display:
mysql> INSERT INTO testing_timestamps (id) VALUES (0);
ERROR 1442 (HY000): Can't update table 'testing_timestamps' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
This is a bummer because using a trigger in this instance would be a good work around.
Actually, Randy's code is broken. It won't work because it is a mutating trigger. You can't update the table that initiated the updating. For example, if you are updating the table SESSIONS, you can't the go modify the table in your trigger using an UPDATE, INSERT or DELETE statement. The only way you are allowed to modify the table to which the trigger is attached is to use the "NEW" or "OLD" prefixes as demonstrated below. (Of course, you are allowed to update other tables at will.) Here is an example of how to overcome the problem described by the op.
create table sessions (
id integer not null,
created timestamp not null default 0,
updated timestamp not null default 0
);
delimiter //
create trigger bifer_sessions_ts before insert on sessions for each row
begin
set new.created = now();
set new.updated = now();
end;
//
create trigger bufer_sessions_ts before update on sessions for each row
begin
set new.updated = now();
end;
//
delimiter ;
To do this and have it work:
col1 TIMESTAMP DEFAULT 0,
col2 TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
You don't need a trigger for col1, just ensure the value is NULL in your query. The answer is in the certification guide.
Default TIMESTAMP is not supported in MySQL before versions 5.6.1 in the same table.
After version MySQL 5.6.+ it allows two or more TIMESTAMP columns in same table.
You can try updating your mysql version