Self referencing foreign key - mysql not set to null - mysql

Indexes :
Keyname Type Unique Packed Column Cardinality Collation Null
parent_id BTREE No No parent_id 1 A YES
Table : (comments)
Column Type Null Default Extra
id int(11) No None AUTO_INCREMENT
parent_id int(11) Yes NULL
Relation view:
Column Foreign key constraint (INNODB)
parent_id 'test_site'.'comments'.'id' ON DELETE CASCADE ON UPDATE NO ACTION
Is it possible to have parent_id not set to NULL. I have tried setting the default value to '0' and inserting the value '0' but I get the following error.
Error:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update
a child row: a foreign key constraint fails (`test_site`.`comments`,
CONSTRAINT `comments_ibfk_2` FOREIGN KEY (`parent_id`) REFERENCES `comments`
(`id`) ON DELETE CASCADE ON UPDATE NO ACTION)
Any help on this would be much appreciated, Thank you.

Yes, it is possible, although you have to circumvent the foreign key constraint just once to insert a dummy record for the default value. Here's my workflow:
Here's the table creation:
root#localhost:playground > create table comments(id int auto_increment primary key, parent_id int not null default 0, constraint fk_parent_id foreign key (parent_id) references comments(id) on delete cascade on update cascade)engine=innodb;
Query OK, 0 rows affected (0.01 sec)
root#localhost:playground > show create table comments\G
*************************** 1. row ***************************
Table: comments
Create Table: CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `fk_parent_id` (`parent_id`),
CONSTRAINT `fk_parent_id` FOREIGN KEY (`parent_id`) REFERENCES `comments` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
Now circumvent foreign key and insert dummy record.
root#localhost:playground > set session foreign_key_checks=0;
Query OK, 0 rows affected (0.00 sec)
root#localhost:playground > insert into comments (id) values (null); Query OK, 1 row affected (0.00 sec)
root#localhost:playground > set session foreign_key_checks=1;
Query OK, 0 rows affected (0.00 sec)
root#localhost:playground > select * from comments;
+----+-----------+
| id | parent_id |
+----+-----------+
| 1 | 0 |
+----+-----------+
1 row in set (0.00 sec)
root#localhost:playground > update comments set id = 0 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
root#localhost:playground > select * from comments;
+----+-----------+
| id | parent_id |
+----+-----------+
| 0 | 0 |
+----+-----------+
1 row in set (0.00 sec)
To make things neat and tidy I reset auto_increment (this is not necessary):
root#localhost:playground > alter table comments auto_increment=0;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
And from now on your foreign key constraint is working properly and your column is no longer nullable and has a default value:
root#localhost:playground > insert into comments (id) values (null);
Query OK, 1 row affected (0.00 sec)
root#localhost:playground > select * from comments;
+----+-----------+
| id | parent_id |
+----+-----------+
| 0 | 0 |
| 1 | 0 |
+----+-----------+
2 rows in set (0.00 sec)

Related

MySQL ENUM to VARCHAR conversion issue

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`)
)

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]>

Foreign key and the primary key in the same table

I need to create a relationship between two attributes in the same table. So both primary key and the foreign keys are in the same table. Here I have a table called User_Type. Primary key is User_ID. It should be the foreign key of Parent_ID.
Ex:
User_Type
User_ID
User_Name
Parent_ID
User_Type_Division
But when I'm creating the relationship I get an error like this.
Cannot add or update a child row: a foreign key constraint fails
(mydb.user_type, CONSTRAINT Parent_User_Type FOREIGN KEY
(Parent_ID) REFERENCES user_type (User_ID) ON DELETE NO ACTION
ON UPDATE NO ACTION)").
Is there any way available to avoid this error. Please someone let me know.
And Here I have given the Query of the table.
CREATE TABLE IF NOT EXISTS `user_type` (
`User_ID` int(11) NOT NULL AUTO_INCREMENT,
`User_Name` varchar(45) NOT NULL,
`Parent_ID` int(11) DEFAULT NULL,
`User_Type_Division` varchar(45) DEFAULT NULL,
`User_ID_Format` varchar(45) DEFAULT NULL,
`Data_Entered_Person` varchar(45) DEFAULT NULL,
`Entered_Time` varchar(45) DEFAULT NULL,
PRIMARY KEY (`User_ID`),
UNIQUE KEY `User_Name_UNIQUE` (`User_Name`),
KEY `ParentUserType` (`Parent_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
There is a way to add and enforce foreign key on existing data.
Step 1: You need to stop foreign key checks for the current session.
SET FOREIGN_KEY_CHECKS=0;
Step 2: Add foreign key to your table.
ALTER TABLE myTable
ADD CONSTRAINT fk_name FOREIGN KEY ( columnName ) REFERENCES ...
Step 3: Enable foreign key checks.
SET FOREIGN_KEY_CHECKS=1;
Working Example:
mysql> create table fkchk( i int not null primary key auto_increment, n int );
Query OK, 0 rows affected (0.26 sec)
mysql> insert into fkchk(n) values ( 0 );
Query OK, 1 row affected (0.10 sec)
mysql> show variables like '%fore%';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| foreign_key_checks | ON |
+--------------------+-------+
1 row in set (0.00 sec)
mysql> alter table fkchk
> add constraint fk_n foreign key (n) references fkchk(i)
> on delete no action;
ERROR 1452 (23000): Cannot add or update a child row:
a foreign key constraint fails
(`test`.<result 2 when explaining filename '#sql-6fc_14'>,
CONSTRAINT `fk_n` FOREIGN KEY (`n`)
REFERENCES `fkchk` (`i`) ON DELETE NO ACTION)
mysql> SET FOREIGN_KEY_CHECKS=0;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%fore%';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| foreign_key_checks | OFF |
+--------------------+-------+
1 row in set (0.00 sec)
mysql> alter table fkchk
> add constraint fk_n foreign key (n) references fkchk(i)
> on delete no action;
Query OK, 0 rows affected (0.50 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> insert into fkchk(n) values ( 0 );
Query OK, 1 row affected (0.32 sec)
mysql> select * from fkchk;
+---+------+
| i | n |
+---+------+
| 1 | 0 |
| 2 | 0 |
+---+------+
2 rows in set (0.00 sec)
mysql> SET FOREIGN_KEY_CHECKS=1;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%fore%';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| foreign_key_checks | ON |
+--------------------+-------+
1 row in set (0.00 sec)
mysql> insert into fkchk(n) values ( 1 );
Query OK, 1 row affected (0.12 sec)
mysql> insert into fkchk(n) values ( 0 );
ERROR 1452 (23000): Cannot add or update a child row:
a foreign key constraint fails
(`test`.`fkchk`,
CONSTRAINT `fk_n` FOREIGN KEY (`n`)
REFERENCES `fkchk` (`i`) ON DELETE NO ACTION)
mysql>
mysql> select * from fkchk;
+---+------+
| i | n |
+---+------+
| 1 | 0 |
| 2 | 0 |
| 3 | 1 |
+---+------+
3 rows in set (0.00 sec)
mysql>
Double check that the User_ID column and the Parent_ID column are of the same datatype.
Also, your User_ID key column is set to NOT NULL, but your Parent_ID column is set to null. that could be the problem. Set Parent_ID to NOT NULL and then try creating the FK relationship again. This is one of the options I was talking about. However, if you already have data in your table, this could be an issue too.

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