MySql auto_increment value incorrect - mysql

I found working on the problem.
Using InnoDB
CREATE TABLE UserAccount (
UserID INT NOT NULL AUTO_INCREMENT,
Email CHAR(32) NOT NULL UNIQUE,
Password CHAR(32) NOT NULL,
Status TINYINT DEFAULT 0,
PRIMARY KEY(UserID)
);
Try Insert data.
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test#gmail.com', 3341234, 0);
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test#gmail.com', 3341234, 0);
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test#gmail.com', 3341234, 0);
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test2#gmail.com', 3341234, 0);
The UserID of test#gmail.com is 1 and
the UserID of test2gmail.com is 4, not 2.
I want the result is 2.
What's the solution?

The problem
The default innodb_autoinc_lock_mode is "1". This means InnoDB only locks the auto-increment column on the table until the end of the INSERT statement if the number of rows to be inserted cannot be determined in advance (for example in an INSERT ... SELECT query). For a simple INSERT like your queries, it assigns the auto-increment ID in advance and then allows other inserts to the table immediately, for faster writing.
In general this means the IDs are consecutive but if you use INSERT IGNORE this is a simple insert so the ID will be assigned and then potentially not actually used because the row is a duplicate.
Changing the lock mode
If you absolutely must have a consecutive identifier for each row, you can change the InnoDB auto-increment lock mode by adding the following line to your my.cnf and restarting your MySQL server.
innodb_autoinc_lock_mode = 0
There is more information about this in the manual. Even after this change, there may be gaps in the sequence if the transaction that generates the IDs is rolled back or if the rows are deleted later, but for your example the IDs generated are "1" and "2" as you expected.
Using a trigger to simulate the same effect
If you can't edit your my.cnf or the gaps after roll backs is an issue, you could write a trigger to update the primary key instead. Something like the following works OK. It generates a warning ("Column 'UserID' cannot be null") for each insert but it inserts successfully with consecutive IDs. Unfortunately if you delete the user with the highest ID, the next user will get the same ID again, whereas if you delete a user in the middle of the sequence there will be a gap as with auto-increment.
DROP TABLE UserAccount;
CREATE TABLE UserAccount (
UserID INT NOT NULL,
Email CHAR(32) NOT NULL UNIQUE,
Password CHAR(32) NOT NULL,
Status TINYINT DEFAULT 0,
PRIMARY KEY(UserID)
);
CREATE TRIGGER UserAccount_before_insert BEFORE INSERT ON UserAccount
FOR EACH ROW SET NEW.UserId = (
SELECT COALESCE(MAX(UserId), 0) + 1 FROM UserAccount);
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test#gmail.com', 3341234, 0);
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test#gmail.com', 3341234, 0);
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test#gmail.com', 3341234, 0);
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test2#gmail.com', 3341234, 0);
I'd have to add, I'm not completely sure how this trigger would perform if you made lots of inserts concurrently from more than one connection. So if that's an issue you might need to do some more research.

In your insert query use IGNORE, means its not insert duplicate record.
every time when you insert same UserID AUTO_INCREMENT count increase and then different UserID come in, it insert incremented auto_increment's value (like 4 in your case)
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test#gmail.com, 3341234, 0); -- auto_increment = 1
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test#gmail.com, 3341234, 0); -- auto_increment = 2
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test#gmail.com, 3341234, 0); -- auto_increment = 3
INSERT IGNORE INTO UserAccount VALUES (NULL, 'test2#gmail.com, 3341234, 0); -- auto_increment = 4
Link

Related

SQL Insert with AUTO INCREMENT

I am trying to insert 50 values into a DB, the table has two column names, one is an ID column set to auto increment and the other is for a code.
When I attempt to do the insert I get a error.
This is my SQL code:
INSERT INTO Names (Id, Code)
VALUES (NULL, 'CodeHere', NULL, 'CodeHere', NULL, 'CodeHERE' );
Don't include the ID column if it is auto increment and split the input to one one value per time
INSERT INTO Names (Code)
VALUES ('CodeHere'),('CodeHere'),('CodeHERE' );
INSERT INTO Names VALUES ('CodeHere'),('2CodeHere'),('3CodeHere'),('4CodeHere')
just ignore auto increment column.
Use this
INSERT INTO Names (Id, Code)
VALUES (NULL, 'CodeHere'), (NULL, 'CodeHere') ,( NULL, 'CodeHERE' );

Insert Increasing Auto Increment Value

I'm having a problem with the auto increment id increasing when I don't want to it. I'm aware that the auto increment id increases when using INSERT IGNORE so I'm working around that, but still getting a behavior I can't figure out.
I'm building a normalized table of transactions, and in this table there is a column for first name which will have a reference table of transaction_first_names. My workflow is that I load data into a non normalized staging table, compare the values in the staging table with the values in the reference tables and if they do not exist into the reference table, then move the data from the staging table to the normalized table.
The issue I'm having is that when I try to insert any "new" values from the staging table into the reference tables, it seem to increment the autoincrement id's in the reference table in a way I can't explain. I wouldn't normally be ocd or stingy with id's, but as a continuing process I don't want the id's to continually be chewed through.
Here is my setup, link & code. As you can see in the second result the last inserted value was given the id of 16, whereas the goal is that id should be 9:
Runnable Example - http://rextester.com/KVMO89341
CREATE TABLE IF NOT EXISTS `transaction_first_names` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`first_name` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `u_first_name` (`first_name`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `transaction_stage` (
`transaction_id` BIGINT(20) UNSIGNED NOT NULL,
`first_name` VARCHAR(255) NULL DEFAULT NULL,
PRIMARY KEY (`transaction_id`),
INDEX `first_name` (`first_name`(191))
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB;
TRUNCATE transaction_stage;
TRUNCATE transaction_first_names;
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3658822144, 'Michael');
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3658825319, 'Pete');
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3658828867, 'Robert');
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3658865656, 'Martin');
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3659080925, 'Charlews');
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3659943769, 'Christopher');
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3660191699, 'Robert');
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3660192662, 'Errol');
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3660194469, 'Frank');
INSERT INTO `transaction_stage` (`transaction_id`, `first_name`) VALUES (3660200483, 'Frank');
-- first select
SELECT DISTINCT st.first_name
FROM transaction_stage st
LEFT JOIN transaction_first_names f ON st.first_name <=> f.first_name
WHERE f.id IS NULL
AND st.first_name IS NOT NULL;
-- first insert
INSERT INTO transaction_first_names (`first_name`)
SELECT DISTINCT st.first_name
FROM transaction_stage st
LEFT JOIN transaction_first_names f ON st.first_name <=> f.first_name
WHERE f.id IS NULL
AND st.first_name IS NOT NULL;
-- second insert
INSERT INTO transaction_first_names (`first_name`)
VALUES ('Another name');
-- check autoincrement
SELECT * FROM transaction_first_names order by id asc;
DROP TABLE IF EXISTS transaction_first_names;
DROP TABLE IF EXISTS transaction_stage;
I've tried wrapping the select distinct in the first insert statement, but no luck.
Ah, InnoDB handles things a bit differently depending on how the system variable innodb_autoinc_lock_mode is set.
For lock modes 1 or 2, gaps may occur between successive statements
because for bulk inserts the exact number of auto-increment values
required by each statement may not be known and overestimation is
possible.
https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html

How to implement this mysql query?

Insert Into table if a duplicate exits select that row primary key else insert into that table and return last insert id ?
SELECT IF (EXISTS(SELECT * FROM users WHERE userName='adminchat')) THEN
BEGIN
SELECT userId FROM users WHERE userName='adminchat';
end;
ELSE
BEGIN
INSERT INTO `users`( `userRole`, `userName`, `createdOn`, `emailId`, `is_active`, `password`) VALUES (1,'user1_chat',NOW(),'sdmd1#sdmd1.com',1,'123456')
select LAST_INSERT_ID();
END;
If a table contains an AUTO_INCREMENT column and INSERT ... UPDATE inserts a row, the LAST_INSERT_ID() function returns the AUTO_INCREMENT value. If the statement updates a row instead, LAST_INSERT_ID() is not meaningful. However, you can work around this by using LAST_INSERT_ID(expr). Suppose that id is the AUTO_INCREMENT column. To make LAST_INSERT_ID() meaningful for updates, insert rows as follows:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;
A way to make things work is to use a dummy column,
so if you have a table with auto_increment column ID and unique key a,b and a smallint dummy column for instance, the query might look like this:
INSERT INTO test (a,b) VALUES ('1','2') ON DUPLICATE KEY UPDATE ID=LAST_INSERT_ID(ID),Dummy = NOT dummy;
Now, SELECT LAST_INSERT_ID(); will return the correct ID.

Inserting new random uuid() in two tables in every insert

I have created two tables which i want to insert similar data in.
CREATE TABLE one(
one_id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (one_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE two(
two_id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (two_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
every time in run insert.
To do that,i am using transactions
START TRANSACTION;
SET #name = uuid();
INSERT INTO one(one_id,name) VALUES (Null,#name);
INSERT INTO two(two_id, name) VALUES (Null, #name);
COMMIT;
This does not produce new values on new inserts.It however inserts the same data in the field name as i wanted.
How can i make this work?.
I don't see a need to move to transactions in order to do that, just add an before insert trigger to the table .
Something like :
CREATE TRIGGER `ONE_TABLE_TRIGG` BEFORE INSERT ON `one`
FOR EACH
ROW BEGIN
SET NEW.name= UUID( );
END ;
You can check if it's null before doing that. do this on both tables and you're good or add insert to the other table on 1 trigger.
I solved it without much complexities by having several having several transaction statements in the same file
START TRANSACTION;
SET #name = uuid();
INSERT INTO one(one_id,name) VALUES (Null,#name);
INSERT INTO two(two_id, name) VALUES (Null, #name);
COMMIT;
START TRANSACTION;
SET #name = uuid();
INSERT INTO one(one_id,name) VALUES (Null,#name);
INSERT INTO two(two_id, name) VALUES (Null, #name);
COMMIT;
/*
Etc
*/

Generate unique and random code digit with MySQL

Initial goal:
I would like to generate random and unique codes (6 digits) in a table.
I use a SQL query like this one to do that:
SELECT SUBSTRING(CRC32(RAND()), 1, 6) as myCode
FROM `codes`
HAVING myCode NOT IN (SELECT code FROM `codes`)
I asked me about how it will react when there will be no more available codes so I do the following test
Test context:
MySQL version: 5.5.20
MySQL Table:
CREATE TABLE `codes` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`code` VARCHAR( 10 ) NOT NULL ,
UNIQUE (
`code`
)
) ENGINE = InnoDB;
Initial data:
INSERT INTO `codes` (`id`, `code`)
VALUES (NULL, '1'), (NULL, '2'), (NULL, '3'), (NULL, '4'), (NULL, '5'), (NULL, '6'), (NULL, '7'), (NULL, '8');
SQL Query:
SELECT SUBSTRING(CRC32(RAND()), 1, 1) as myCode
FROM `codes`
HAVING myCode NOT IN (SELECT code FROM `codes`)
By execute this query, I expect that it will always return 9 because it is the only code of one digit which does not exists.
But the result is:
Sometime it return any rows
Sometime it return rows with values that already exists
I don't understand this behavior so if someone can help :)
So the big question is:
How MySQL can return rows with values that already exists?
Thanks
I would fill a sequencetable table with all the possible values, in sequence.
Then the random query just randomly selects records from the sequencetable, and each time it picks a record it deletes it. This way you will surely get all the numbers, without wasting time in finding a "hole" number (not already picked up).
CREATE TABLE `sequencetable`
(
`sequence` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`sequence`)
)
ENGINE=InnoDB
AUTO_INCREMENT=1;
Fill the sequence (no need for the AUTOINCREMENT actually).
DECLARE i INT;
SET i=1;
REPEAT
INSERT INTO sequencetable VALUES (i);
SET i=i+1;
UNTIL i>999999 END REPEAT;
Select a random record from the sequence (do this in a loop until records are available):
DECLARE sequencen INT;
SET sequencen =
(SELECT sequence FROM sequencetable ORDER BY RAND() LIMIT 1);
DELETE FROM sequencetable WHERE sequence = sequencen;