I've created a log system based on trigger.
Every time a row is inserted or updated the trigger store a new row in another table.
The trigger works fine but after some time I found this message in logs:
[Warning] Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly. Statement: update `gl_item` set `is_shown` = '0', `updated_at` = '2016-03-21 16:56:28' where `list_id` = '1' and `is_shown` = '1'
I've already red some post related to this issue i like:
- MySQL Replication & Triggers
But I don't understand the nature of the problem.
What this warning mean?
I don't have to insert into auto increment column with triggers?
Which is the best way to create a log system in order to avoid this warning?
Update
Output of SHOW CREATE TABLE, this is the table where the trigger will enter THE rows.
gl_item_log | CREATE TABLE `gl_item_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Item log unique id',
`item_id` bigint(20) unsigned DEFAULT NULL ,
`updated_by` bigint(20) unsigned DEFAULT NULL ,
`switch_shown` tinyint(4) DEFAULT NULL ,
`switch_checked` tinyint(4) DEFAULT NULL ,
`logged_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`logged_at_microtime` decimal(6,6) unsigned NOT NULL ,
`logged_at_microtime_int` mediumint(8) unsigned NOT NULL DEFAULT '0' ,
PRIMARY KEY (`id`),
KEY `gl_item_log_updated_by_foreign` (`updated_by`),
KEY `gl_item_log_item_id_updated_by_switch_shown_switch_checked_index`
(`item_id`,`updated_by`,`switch_shown`,`switch_checked`),
CONSTRAINT `gl_item_log_item_id_foreign`
FOREIGN KEY (`item_id`) REFERENCES `gl_item` (`id`),
CONSTRAINT `gl_item_log_updated_by_foreign`
FOREIGN KEY (`updated_by`) REFERENCES `gl_general_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Could be a good idea drop the id column with the auto increment field in order to have logs entries without a unique identifier?
Thanks
What this warning mean?
According to the MySQL documentation:
A statement invoking a trigger (or function) that causes an update to an AUTO_INCREMENT column is not replicated correctly using statement-based replication. MySQL 5.7 marks such statements as unsafe. (Bug #45677)
With statement-based replication, the exact SQL which is run on your master database is also run on your slave(s). When your trigger is fired, if it exists on every one of your databases, it is run on each database and inserts into your log. This can be a tricky situation for your databases to remain in sync.
I don't have to insert into auto increment column with triggers?
Correct. It's never necessary to insert your own values into an auto-increment column.
Which is the best way to create a log system in order to avoid this warning?
First, either keep the trigger on every database and turn off replication for your log table, or have the trigger only on your master database and let replication copy the log table inserts to the other databases.
To work around this specific warning, configure your log table to have no auto-increment column. Your trigger can then insert into it and it shouldn't cause any replication warnings.
Another option is to switch to row-based replication. Then the trigger will only be fired automatically on the master and the auto-increment values will always replicate without issue.
Related
I got a MySQL database with some tables.
In one of these tables i want to insert by a SQL script some new rows.
Unfortunately i have to insert in two columns an empty string and the two columns are part of an unique key for that table.
So i tried to set UNIQUE_CHECKS before and after the insert, but i'm getting errors because of duplicate entries.
Here is the definition of the table:
CREATE TABLE `Table_A` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`number` varchar(25) DEFAULT NULL,
`changedBy` varchar(150) DEFAULT NULL,
`changeDate` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`,`number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And the INSERT statement which causes error:
SET UNIQUE_CHECKS = 0;
INSERT INTO `Table_A`
(`name`, `number`, `changedBy`, `changeDate`)
SELECT DISTINCT '', 'myUser', CURRENT_TIMESTAMP
FROM Table_A
AND id NOT IN
(
SELECT DISTINCT id
FROM Table_A
);
SET UNIQUE_CHECKS = 1;
As You can see, i'm using UNIQUE_CHECKS.
But as i said this doesn't work properly.
Any help or suggestion would be appreciated.
Patrick
Switching off Unique Keys for the insert operation doesn't indicate that it will check uniqueness only for the operations that happen after you switch it on again. It just means that database will not waste time to check the constraint during the time it is switch off but it will check the constraint when you switch it on again.
What it measn is that you nead to ensure that column has unique value in a columns with Unique Keys before you can turn it on. Which you don't do.
If you want to maintain Uniqueness somehow for new records you insert after some point in time you would need to create trigger and manually check the new records against already existing data. The same possibly goes for updates. But I don't recommend it - you should probably redesign data so either the Unique Key is not there or the data is truly unique for all the records there are and will be.
That's a strange issue.
There are source MySQL DB (MASTER) and its replic (SLAVE). It's a (as you understand its a MASTER-SLAVE) statement-bases replication, because I need triggers to run on the SLAVE side.
The original triggers were replaced by new ones.
Every table has 3 trigers: on INSERT, on UPDATE and on DELETE.
Trigers were generated by a single pattern and differ only by params.
Every trigger does a single INSERT query to a table (CHANGES) on the SLAVE.
This table is not replicated and exists only on the SLAVE.
There is an autoincremented column (ID - bigint) in this table.
None of the triggers set or modify values of the column ID. The DB sets a default values for it.
It's about 20 inserts executed on CHANGES per minute.
I see errors with duplicated values.
How it's possible?
Let's again:
An INSERT / UPDATE / DELETE query is executed on MASTER.
This change is replicated to SLAVE.
A trigger is called and inserts a row to the CHANGES.
Duplicated values error is generated.
And as I said before, none of the triggers set or modify value of the autoincremented field (ID). And only triggers work with table CHANGES.
I understand that two or more triggers can be called together, and try to do INSERT together, but I think DB should easy solve this. Or that's a very bad DB.
UPD:
CREATE TABLE `CHANGES` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`field1` ENUM(...) NOT NULL,
`field2` BOOL NOT NULL DEFAULT FALSE,
`field3` VARCHAR(64) NOT NULL,
`field4` VARCHAR(255) NOT NULL,
`field5` TEXT,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
CREATE TRIGGER `tr_TABLE_insert` AFTER INSERT ON `TABLE`
FOR EACH ROW BEGIN
INSERT INTO `CHANGES` (`field1`, `field3`, `field4`, `field5`)
VALUES ("value1", "value3", "value4", "value5");
END
UPD 2:
I found a temporary and more dirty method - added BEFORE INSERT trigger for CHANGES to set ID manually.
It works. But I still can't figure out why the native AUTO_INCREMENT mechanism is generating duplicated ids.
I have an InnoDB table with a timestamp in it, and I wish to have another field which carries only the date part of the timestamp, so that I can create an index on it. (My temporal queries will always be bound by date, so having an index with high cardinality on the timestamp is not really needed.)
Is it possible to have the date field update automatically ON UPDATE from the timestamp field (similar to how CURRENT_TIMESTAMP works)?
I tried the following but it MySQL says I have an error in my SQL syntax.
CREATE TABLE test_table (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`full_ts` timestamp NULL DEFAULT NULL COMMENT 'The full timestamp',
`only_date` date NULL DEFAULT NULL ON UPDATE date(full_ts) COMMENT 'This field carries only the date part of the full timestamp for better indexing.',
PRIMARY KEY (`id`),
KEY `ONLY_DATE_IDX` (`only_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I could of course update both fields everywhere in the code, but it would be cleaner if the only_date field was a slave of the full_ts field, updated and kept consistent by the database.
I know that in MySQL 5.7.5 there was a new feature added for stored generated columns, which seem to do exactly this. Unfortunately it is not possible to upgrade the database version at the moment.
Is there a way to achieve this in MySQL 5.5?
This will update the "only_date" column when you update the "full_ts" column
CREATE TRIGGER `autoDate` BEFORE UPDATE ON `test_table` FOR EACH ROW BEGIN
SET NEW.only_date=DATE(NEW.full_ts);
END
EDIT:
For further reading on triggers, please refer to https://dev.mysql.com/doc/refman/5.5/en/create-trigger.html
Also worth reading about triggers https://dev.mysql.com/doc/refman/5.5/en/faqs-triggers.html
I have a column with data that exceeds MySQL's index length limit. Therefore, I can't use an unique key.
There's a solution here to the problem without using an unique key: MySQL: Insert record if not exists in table
However, in the comments, people are having issues with inserting the same value into multiple columns. In my case, a lot of my values are 0, so I'll get duplicate values very often.
I'm using Node and node-mysql to access the database. I'm thinking I can have a variable that keeps track of all values that are currently being inserted. Before inserting, I check if the value is currently being inserting. If so, I'll wait until it finishes inserting, then continue execution as if the value was originally inserted. However, I feel like this will be very error prone.
Here's part of my table schema:
CREATE TABLE `links` (
`id` int(10) UNSIGNED NOT NULL,
`url` varchar(2083) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
`likes` int(10) UNSIGNED NOT NULL,
`tweets` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `links`
ADD PRIMARY KEY (`id`),
ADD KEY `url` (`url`(50));
I cannot put an unique key on url because it can be 2083 bytes, which is over MySQL's key size limit. likes and tweets will often be 0, so the linked solution will not work.
Is there another possible solution?
If you phrase your INSERT in a certain way, you can make use of WHERE NOT EXISTS to check first if the URL does not exist before completing the insert:
INSERT INTO links (`url`, `likes`, `tweets`)
SELECT 'http://www.google.com', 10, 15 FROM DUAL
WHERE NOT EXISTS
(SELECT 1 FROM links WHERE url='http://www.google.com');
This assumes that the id column is a primary key/auto increment, and MySQL will automatically assign a value to it.
brief:
I'm experiencing strange MySQL behavior - on update CURRENT_TIMESTAMP attribute is being added although I don't want it to be added. I want to find out why is this happening - is it a matter of MySQL server or MySQL Workbench I'm using (v5.2.38).
detail:
I've modelled the database structure using EER diagrams, an example table is below:
CREATE TABLE IF NOT EXISTS `privilege` (
`id` INT NOT NULL ,
`name` VARCHAR(64) NOT NULL COMMENT 'privilege name (just a label)' ,
`created_at` TIMESTAMP NOT NULL COMMENT 'when the privilege was created' ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
The above script fragment is created using Export > Forward Engineer SQL Create Script inside WorkBench. The created_at column is siginifant here. It is NOT NULL and there is no default value defined for the timestamp when a record is defined. So I guess, that if someone tries to insert a record without defining created_at, an error will be raised.
I run this script inside MySQL server to create the whole structure. And the created structure is different - show create table privilege returns the following:
CREATE TABLE `privilege` (
`id` int(11) NOT NULL,
`name` varchar(64) NOT NULL COMMENT 'privilege name (just a label)',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP COMMENT 'when the privilege was created',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Where did "on update current_timestamp" come from? I'm 100% sure that I didn't choose any appropriate option, so MySQL should not create anything he's not asked to.
Does anyone have an idea why those clauses are added?
As documented under Automatic Initialization and Updating for TIMESTAMP:
The following rules describe the possibilities for defining the first TIMESTAMP column in a table with the current timestamp for both the default and auto-update values, for one but not the other, or for neither:
[ deletia ]
With neither DEFAULT CURRENT_TIMESTAMP nor ON UPDATE CURRENT_TIMESTAMP, it is the same as specifying both DEFAULT CURRENT_TIMESTAMP and ON UPDATE CURRENT_TIMESTAMP.
[ deletia ]
To suppress automatic properties for the first TIMESTAMP column, do either of the following:
Define the column with a DEFAULT clause that specifies a constant default value.
Specify the NULL attribute. This also causes the column to permit NULL values, which means that you cannot assign the current timestamp by setting the column to NULL. Assigning NULL sets the column to NULL.