MySQL ENUM to VARCHAR conversion issue - mysql

I have a table "temp_enum_test1" with data type "enum('IE','IS')". When I try to ALTER the table by changing data type into VARCHAR from 'ENUM', getting duplicate entry error. It is accepting records in ENUM type. Even when I query the table I am getting unique rows. Can anyone please help me here. Below are the schema and my approach.
mysql> CREATE TABLE temp_enum_test1 (
-> r_id int(11) NOT NULL,
-> r_type enum('IE','IS'),
-> UNIQUE KEY uk_temp_enum_test1 (r_id,r_type)
-> );
Query OK, 0 rows affected (0.38 sec)
mysql> insert into temp_enum_test1 values(1,'IE');
Query OK, 1 row affected (0.07 sec)
mysql> insert into temp_enum_test1 values(1,'IS');
Query OK, 1 row affected (0.05 sec)
mysql> select * from temp_enum_test1;
+------+--------+
| r_id | r_type |
+------+--------+
| 1 | IE |
| 1 | IS |
+------+--------+
2 rows in set (0.00 sec)
mysql> alter table temp_enum_test1 change column r_type r_type varchar(30);
ERROR 1062 (23000): Duplicate entry '1-I' for key 'uk_temp_enum_test1'
mysql>

The problem seems to be with the create table query. The table has been created with 'UNIQUE KEY' that is causing the error while altering the table. If you can use
PRIMARY KEY (`r_id`)
instead of ...
UNIQUE KEY uk_temp_enum_test1 (r_id,r_type)
Complete CREATE command as below;
CREATE TABLE IF NOT EXISTS temp_enum_test1 (
`r_id` int(11) NOT NULL AUTO_INCREMENT,
`r_type` enum('IE','IS') NOT NULL,
PRIMARY KEY (`r_id`)
)

Related

Set AUTO_INCREMENT on a column with missing values

I have a table called users in my database. It has a field id of type int. As of now this is manually incremented for every user that registers and some intermediate values are missing because of deleted user accounts. I cannot change the user id of other registered users . I tried to change this column to AUTO_INCREMENT using this statement
ALTER TABLE `userinfo` CHANGE `id` `id` BIGINT(20) NOT NULL AUTO_INCREMENT;
But I got the following error
1062 - ALTER TABLE causes auto_increment resequencing, resulting in
duplicate entry '1' for key 'PRIMARY'
I only have around 200 users in my table. So I wanted to start AUTO_INCREMENT from 201. I executed the following statements
ALTER TABLE `userinfo` AUTO_INCREMENT=201;
ALTER TABLE `userinfo` CHANGE `id` `id` INT(10) NOT NULL AUTO_INCREMENT;
But still I encounter the same issue.
If you don't have duplicates (id), you should not have problems:
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.17 |
+-----------+
1 row in set (0.00 sec)
mysql> DROP TABLE IF EXISTS `userinfo`;
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE IF NOT EXISTS `userinfo` (
-> `id` INT(11) NOT NULL
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO `userinfo`
-> (`id`)
-> VALUES
-> (1),
-> (2),
-> (3),
-> -- (1),
-> (10),
-> (11),
-> (15),
-> (20),
-> (182),
-> (191);
Query OK, 9 rows affected (0.00 sec)
Records: 9 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE `userinfo`
-> CHANGE `id` `id` BIGINT(20) NOT NULL
-> PRIMARY KEY AUTO_INCREMENT;
Query OK, 9 rows affected (0.00 sec)
Records: 9 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE `userinfo` AUTO_INCREMENT = 201;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW CREATE TABLE `userinfo`\G
*************************** 1. row ***************************
Table: userinfo
Create Table: CREATE TABLE `userinfo` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)
In another case:
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.17 |
+-----------+
1 row in set (0.00 sec)
mysql> DROP TABLE IF EXISTS `userinfo`;
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE IF NOT EXISTS `userinfo` (
-> `id` INT(11) NOT NULL
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO `userinfo`
-> (`id`)
-> VALUES
-> (1),
-> (2),
-> (3),
-> (1), -- Duplicate
-> (10),
-> (11),
-> (15),
-> (20),
-> (182),
-> (191);
Query OK, 10 rows affected (0.00 sec)
Records: 10 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE `userinfo`
-> CHANGE `id` `id` BIGINT(20) NOT NULL
-> PRIMARY KEY AUTO_INCREMENT;
ERROR 1062 (23000): ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '1' for key 'PRIMARY'
Try resetting the column to primary key, like this:
ALTER TABLE `userinfo` CHANGE `id` `id` INT(10) PRIMARY KEY AUTO_INCREMENT;
You can just drop auto_increment then set auto_increment, do not need recreate primary key
ALTER TABLE `userinfo` CHANGE `id` `id` int NOT NULL;
ALTER TABLE `userinfo` AUTO_INCREMENT=9;
ALTER TABLE `userinfo` CHANGE `id` `id` int NOT NULL AUTO_INCREMENT;
I was having this same problem/error (MySql 5.7.28) with bulk inserts of data that was missing primary key/auto_increment values - even though there were no duplicates. I wanted to preserve the primary key and needed to preserve the original field values also as resequencing would have caused data-integrity problems in the system.
My solution was to remove the auto_increment and then primary key as suggested above:
ALTER TABLE `userinfo` CHANGE `id` `id` int NOT NULL;
ALTER TABLE `userinfo` DROP PRIMARY KEY;
Then run my bulk insert statement without resequencing problems.
Then restore the primary key and original values using the following steps:
renaming the original PK/auto-increment field which now has the desired values in it
creating a new PK/auto-increment field with the original field name
setting the auto-increment value to one more than the max value of the renamed field
copying the original values into the new PK/auto-increment field
dropping the renamed field (now you have the original schema with PK/auto-increment field with original gaps in the sequence as desired.
Here is the sql for the first two steps:
ALTER TABLE `userinfo` CHANGE `id` `orig_id` INT;
ALTER TAble `userinfo` add id int auto_increment not null primary key;
Then use the following query result:
select max(orig_id) + 1 from `userinfo`;
to set the new auto-increment, before updating the new PK/auto-increment values:
ALTER TABLE `userinfo` AUTO_INCREMENT=201;
UPDATE `userinfo` set id = orig_id;
ALTER TABLE `userinfo` drop column orig_id;
and now I've got the original table values copied exactly as wanted. I hope this helps others save time.

How to do unique constraint works with NULL value in MySQL

I am looking for how to implement unique constraints with NULL check.
MySQL shouldn't allow multiple null value.
Employee:
id | name
---|-----
1 | null
2 | null -> should give error during inserting 2nd row.
No, MySQL is doing the right thing, according to the SQL-99 specification.
https://mariadb.com/kb/en/sql-99/constraint_type-unique-constraint/
A UNIQUE Constraint makes it impossible to COMMIT any operation that
would cause the unique key to contain any non-null duplicate values.
(Multiple null values are allowed, since the null value is never equal
to anything, even another null value.)
If you use a UNIQUE constraint but don't want multiple rows with NULL, declare the columns as NOT NULL and prohibit any row from having NULL.
MySQL 5.7 does allow for a workaround:
mysql> CREATE TABLE `null_test` (
-> `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
-> `const` varchar(255) NOT NULL DEFAULT '',
-> `deleted_at` datetime DEFAULT NULL,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.03 sec)
With soft deletes, it would be nice if you could have just one row with a with a deleted_at = NULL per constraint.
mysql> ALTER TABLE `null_test` ADD `vconst` int(1) GENERATED ALWAYS AS (((NULL = `deleted_at`) or (NULL <=> `deleted_at`))) VIRTUAL;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
So I created a virtual column that will flip from 1 to null when deleted_at gets set.
mysql> ALTER TABLE `null_test` ADD UNIQUE KEY `nullable_index` (`const`,`vconst`);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
Instead of including deleted_at to the unique constraint add the virtual column, vconst.
mysql> INSERT INTO `null_test` SET `const` = 'Ghost';
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM `null_test` WHERE `const` = 'Ghost';
+--------+-------+------------+--------+
| id | const | deleted_at | vconst |
+--------+-------+------------+--------+
| 999901 | Ghost | NULL | 1 |
+--------+-------+------------+--------+
1 row in set (0.01 sec)
No need to insert the vconst (but you cannot, anyhow).
mysql> INSERT INTO `null_test` SET `const` = 'Ghost';
ERROR 1062 (23000): Duplicate entry 'Ghost-1' for key 'nullable_index'
Inserting it again throws the Duplicate entry error.
mysql> UPDATE `null_test` SET `deleted_at` = NOW() WHERE `const` = 'Ghost';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Same with setting delete_at, no need to touch vconst, it will flip automatically.
mysql> SELECT * FROM `null_test` WHERE `const` = 'Ghost';
+--------+-------+---------------------+--------+
| id | const | deleted_at | vconst |
+--------+-------+---------------------+--------+
| 999901 | Ghost | 2017-02-16 22:07:45 | NULL |
+--------+-------+---------------------+--------+
1 row in set (0.00 sec)
mysql> INSERT INTO `null_test` SET `const` = 'Ghost';
Query OK, 1 row affected (0.00 sec)
Now you are free to insert a new row with the same constraints!
mysql> SELECT * FROM `null_test` WHERE `const` = 'Ghost';
+--------+-------+---------------------+--------+
| id | const | deleted_at | vconst |
+--------+-------+---------------------+--------+
| 999901 | Ghost | 2017-02-16 22:07:45 | NULL |
| 999903 | Ghost | NULL | 1 |
+--------+-------+---------------------+--------+
2 rows in set (0.01 sec)
In this case, depending on how much you soft delete, setting deleted_at, you might want to include deleted_at to the index, or a new index with it, but I will let my load tests decide.
alter table yourtable add column `virtual_null` varchar(20) GENERATED ALWAYS AS (if(isnull(`your_nullable_column`),'null',`your_nullable_column`))) VIRTUAL;
alter table yourtable add constraint unique(virtual_null);
Make this and be happy, behind the scenes mysql's null is a hash value. Because that its impossible compare two null values...
Sorry by poor english, good luck

Automatically generating 6 digit unique number in MySQL

I have a table named 'users' with the follwing columns
id
email
password
pin
I want the 'pin' to be auto-filled with a unique 6 digit number every time a new record is added. It would be of a great help if anyone could suggest a good approach for this problem.
Define the PIN column as
pin MEDIUMINT NOT NULL AUTO_INCREMENT
Use the zerofill option for your int field. the Mysql fills it for your.
CREATE TABLE `int6` (
`id` int(6) unsigned zerofill NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
sample
MariaDB [l]> drop table int6;
Query OK, 0 rows affected (0.00 sec)
MariaDB [l]> CREATE TABLE `int6` (
-> `id` int(6) unsigned zerofill NOT NULL AUTO_INCREMENT,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.02 sec)
MariaDB [l]> insert into int6 VALUES (1),(2),(3);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
MariaDB [l]> select * from int6;
+--------+
| id |
+--------+
| 000001 |
| 000002 |
| 000003 |
+--------+
3 rows in set (0.00 sec)
MariaDB [l]>

MySql silently ignores "references" keyword on table creation

mysql> create database test;
Query OK, 1 row affected (0.01 sec)
mysql> use test;
Database changed
mysql> create table one (id int not null primary key);
Query OK, 0 rows affected (0.03 sec)
mysql> -- here is the problem
mysql> create table two (oneid int not null references one(id));
Query OK, 0 rows affected (0.02 sec)
mysql> -- here are the first signs of issues!!!!
mysql> show create table two;
+-------+----------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------------+
| two | CREATE TABLE `two` (
`oneid` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+----------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> -- here is the issue: an insert with no reference
mysql> insert into two values (-12);
Query OK, 1 row affected (0.01 sec)
mysql> select * from two;
+-------+
| oneid |
+-------+
| -12 |
+-------+
1 row in set (0.00 sec)
mysql> -- if you want to know:
mysql> SHOW Variables WHERE Variable_name='foreign_key_checks';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| foreign_key_checks | ON |
+--------------------+-------+
1 row in set (0.00 sec)
After my research, the only question here is: why MySql do not reject table two creation because of its not valid syntax and instead it silently create the table without the foreign key reference?
Just for completeness here is the correct syntax for MySql.
mysql> create table three(oneid int not null, CONSTRAINT whatEverName FOREIGN KEY (oneid) REFERENCES one(id));
Query OK, 0 rows affected (0.04 sec)
MySQL parses but ignores “inline REFERENCES specifications” (as defined in the SQL standard) where the references are defined as part of the column specification. MySQL accepts REFERENCES clauses only when specified as part of a separate FOREIGN KEY specification.
You can go here and read more yourself...

mysql: Duplicate entry '0' for key 'PRIMARY' and bizarre ID behavior

See log below. (Snipped just for brevity; unsnipped # http://pastebin.com/k9sCM6Ee)
In short: somehow rows are getting assigned ID 0. When this happens, it blocks inserts, even when those inserts aren't actually conflicting with ID 0 (although that really shouldn't happen in the first place).
Although it is heavily read and very heavily inserted (up to ~300k rows/min), this table is never updated. The only method that inserts is the one that results in the INSERT INTO queries like below. There are no foreign keys or the like.
a) WTF?
b) How do I fix it?
Thanks!
$ mysql --version
mysql Ver 14.14 Distrib 5.1.30, for apple-darwin9.4.0 (i386) using readline 5.1
$ mysql
mysql> SHOW CREATE TABLE visitations \G
*************************** 1. row ***************************
Table: visitations
Create Table: CREATE TABLE `visitations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`scraping_id` int(11) NOT NULL,
`site_id` int(11) NOT NULL,
`visited` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_visitations_on_scraping_id_and_site_id` (`scraping_id`,`site_id`),
KEY `index_visitations_on_site_id` (`site_id`)
) ENGINE=InnoDB AUTO_INCREMENT=23525407 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> show triggers;
Empty set (0.04 sec)
mysql> INSERT INTO `visitations` (`scraping_id`,`site_id`,`visited`) VALUES (647,196,0),(647,51679,0),(647,13689,0),(647,85739,1),(647,4388,0),(647,100346,0),(647,1245,0),[snip];
ERROR 1062 (23000): Duplicate entry '0' for key 'PRIMARY'
mysql> SELECT * FROM `visitations` WHERE (`scraping_id`,`site_id`,`visited`) IN ((647,196,0),(647,51679,0),(647,13689,0),(647,85739,1),(647,4388,0),(647,100346,0),(647,1245,0),[snip]);
Empty set (1 min 27.43 sec)
mysql> select * from visitations where id = 0;
+----+-------------+---------+---------+
| id | scraping_id | site_id | visited |
+----+-------------+---------+---------+
| 0 | 645 | 46177 | 0 |
+----+-------------+---------+---------+
1 row in set (0.00 sec)
mysql> delete from visitations where id < 363;
Query OK, 363 rows affected (0.11 sec)
mysql> select * from visitations where id = 0;
Empty set (0.00 sec)
mysql> INSERT INTO `visitations` (`scraping_id`,`site_id`,`visited`) VALUES (647,196,0),(647,51679,0),(647,13689,0),(647,85739,1),(647,4388,0),(647,100346,0),(647,1245,0),[snip];
Query OK, 500 rows affected (0.23 sec)
Records: 500 Duplicates: 0 Warnings: 0
mysql> select * from visitations where id = 0;
Empty set (0.00 sec)
mysql> INSERT INTO `visitations` (`scraping_id`,`site_id`,`visited`) VALUES (647,196,0),(647,51679,0),(647,13689,0),(647,85739,1),(647,4388,0),(647,100346,0),(647,1245,0),[snip];
ERROR 1062 (23000): Duplicate entry '647-196' for key 'index_visitations_on_scraping_id_and_site_id'
You have probably hit a bug like:
auto increment does not work properly with InnoDB after update
You need to track the change history from the release you are using to identify whether fixed bugs can affect you and whether you should upgrade.
MySQL 5.1 Change History