How to avoid duplicate key error in mysql - mysql

I have a problem to get a next sequence id in my code. Though it was a legacy code i have to follow the same. Let me explain the logic which was followed.
CREATE TABLE `emp_seq` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1234 DEFAULT CHARSET=utf8
Above table used to get the next sequence id for the below table. and also emp_seq table will have only one entry for the id.
CREATE TABLE `emp_info` (
`id` BIGINT(8) UNSIGNED NOT NULL,
`name` VARCHAR(128) DEFAULT '',
`active` TINYINT(2) DEFAULT '1',
`level` MEDIUMINT(8) DEFAULT '100',
PRIMARY KEY (`id`),
KEY `level` (`level`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='employee information'
so whenever we trying to insert a new record to emp_info table, we are getting next sequence id from the table emp_seq by using below queries.
INSERT INTO emp_seq () VALUES ();
DELETE FROM emp_seq WHERE id < LAST_INSERT_ID;
Now the problem is, some times because of multiple asynchronous calls in the application, the same increment id has been shared to multiple records and trying to insert in the emp_info table and we are getting below error
"code":"ER_DUP_ENTRY","errno":1062,"sqlMessage":"Duplicate entry 1234 for key
Kindly help me how to resolve the issue.

Related

Is it possible to make a batch insert/update if the uniqueness of the record is a bundle of two fields?

I have the following table structure (example)
CREATE TABLE `Test` (
`id` int(11) NOT NULL,
`order_id` int(11) NOT NULL,
`position_id` int(11) NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`price` decimal(10,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `Test` ADD PRIMARY KEY (`id`);
ALTER TABLE `Test` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
This table contains data that is constantly in need of updating. There is also new data that needs to be entered. Since there is a lot of data, it will take quite a long time to check each record to make it insert or update.
After studying the question, I realized that I need to use batch insert/update with:
INSERT on DUPLICATE KEY UPDATE
But the documentation says that the fields must have a unique index. But I don't have any unique fields, I can't use the ID field. The uniqueness of the record can only be in a combination of two fields order_id and position_id.
Is it possible to make a batch insert/update if the uniqueness of the record is a bundle of two fields?
You need a composite primary-key. You also don't need your AUTO_INCREMENT id column, so you can drop it.
Like so:
CREATE TABLE `Test` (
`order_id` int NOT NULL,
`position_id` int NOT NULL,
`name` varchar(255) NOT NULL COLLATE utf8mb4_unicode_ci,
`price` decimal(10,2) NOT NULL,
CONSTRAINT PK_Test PRIMARY KEY ( `order_id`, `position_id` )
) ENGINE=InnoDB
Then you can use INSERT ON DUPLICATE KEY UPDATE.

MySql trigger insert new entry with wrong timestamp

I have an old database with version 5.0.95 (which unfortunately I cannot upgrade).
In this database I have a table clientes_states:
CREATE TABLE `clientes_states` (
`id` int(11) NOT NULL auto_increment,
`client_id` int(11) NOT NULL,
`state_id` int(11) NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1239670 DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED;
For each change of a client state, a new entry is generated. From this reason this table is huge, which makes it complicated to achieve last state of all clients.
In order to access easily the current state of each client, I created the following table, and added a trigger:
CREATE TABLE `actual_clients_states` (
`id` int(11) NOT NULL auto_increment,
`client_id` int(11) NOT NULL,
`state_id` int(11) NOT NULL,
`updated_at` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `client_id` (`client_id`)
) ENGINE=InnoDB AUTO_INCREMENT=21022 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
CREATE TRIGGER clients_states_insert
AFTER INSERT
ON clientes_states FOR EACH ROW
INSERT INTO actual_clients_states (client_id, state_id)
VALUES (new.client_id, new.state_id)
ON DUPLICATE KEY UPDATE state_id=NEW.state_id;
I have two databases, one for testing and one for staging.
From some reason, the updated_at field is updated with wrong date (almost a month ago) on the staging server, but works fine on the testing server.
Any idea what might be the problem?

MySql: AUTO_INCREMENT is missing from tables

MySql: AUTO_INCREMENT is missing from some tables after running for about one month.
Initially: (show create table Foo)
CREATE TABLE `Foo` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(10) NOT NULL,
`type` tinyint(2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8
After one month:
CREATE TABLE `Foo` (
`id` bigint(20) NOT NULL,
`name` varchar(10) NOT NULL,
`type` tinyint(2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
AUTO_INCREMENT is missing. What is the cause?
Mysql Server version: 5.6.25, Linux
Someone must have changed it. This change does not happen spontaneously.
I can reproduce this change myself:
CREATE TABLE Foo ( id BIGINT AUTO_INCREMENT, ...
ALTER TABLE Foo MODIFY COLUMN id BIGINT;
SHOW CREATE TABLE Foo\G
*************************** 1. row ***************************
Table: foo
Create Table: CREATE TABLE `foo` (
`id` bigint(20) NOT NULL,
`name` varchar(10) NOT NULL,
`type` tinyint(2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Now the column shows it is BIGINT but not AUTO_INCREMENT.
Every time you MODIFY COLUMN or CHANGE COLUMN, you must repeat all the column options like NOT NULL and AUTO_INCREMENT and DEFAULT, or else it will revert to defaults (i.e. not auto-increment).
So I would interpret this shows that someone did an ALTER TABLE and didn't remember to include the AUTO_INCREMENT column option.
Just a thought.
If you have binary logs, you may see the alter query on the logs and when it was run. :)
Check if the binary log is enabled by
show variable like 'log_bin';
If binary log is enabled, find the likely period that the query could have been executed and then use mysqlbinlog to help you find it.
If binary log is not enabled, bad luck - as the previous post by Bill Karwin has suggested mysql does not change it on its own - someone must have changed it.

Concurrent writes to DB with conditional unique

I am using spring-boot, mysql and JDBC in my application.
I have a table which is like below
CREATE TABLE `post` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`ref` varchar(255) DEFAULT '',
`userId` bigint(20) NOT NULL,
`text` varchar(255),
`count` bigint(20) NOT NULL,
`version` bigint(11) DEFAULT NULL
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
In this table, I have a column count which depends on the combination of unique ref, userId and text columns eg. if the combination of these columns is not present in DB then the count will be 1 but if the combination already exists in DB, the count value will be 0.
I run into the problem, when the two or more users are trying to post with same ref, userId and text at the same time. Out of these request only one should get count as one and other ones as zero.
How can I handle this case when multiple users are trying to post with same values?

MySQL - how to lock a single row?

My aim is to create a system to book trips. My system is built in MySQL + PHP. I'm using InnoDB as engine in MySQL.
When an user choose which trip he wants to book, I want the ipothetic page "*.php" to lock one of those trip for some times (ex. 10 minutes) and after this time release it.
Which is the best way to lock the trip in the DB?
Notice that I didn't talk about rows or table because I'm asking for the easiest way to implement this kind of feature and I'm not sure if mines is that one. So I've tried to design a table to contain the trips, and look like this:
CREATE TABLE IF NOT EXISTS Trip (
id INT(1) NOT NULL AUTO_INCREMENT,
descr VARCHAR(50) NOT NULL,
quantity INT(2) NOT NULL DEFAULT 10,
PRIMARY KEY (id)
) Engine=InnoDB
This is your Ticket table
CREATE TABLE IF NOT EXISTS Trip (
id INT(1) NOT NULL AUTO_INCREMENT,
descr VARCHAR(50) NOT NULL,
quantity INT(2) NOT NULL DEFAULT 10,
PRIMARY KEY (id)
) Engine=InnoDB
Let me visualize your user table as this ( just for testing )
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1 ;
Now You need add an extra table where you can track your users booking order ( Example like this )
CREATE TABLE IF NOT EXISTS `trip_track` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`trip_id` int(11) NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1 ;
Now Every time you insert a booking query you need to insert user_id,ticket_id and timestamp ( You can use date or Mysql's default time stamp )
Then whenever user tries to book any ticket use a Query to check the time difference between current time and the timestamp from the database
That's it! .. see? that's simple :)