Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop MySQL tolerating multiple NULLs in a UNIQUE constraint

Tags:

sql

mysql

mysql5

My SQL schema is

CREATE TABLE Foo (
 `bar` INT NULL ,
 `name` VARCHAR (59) NOT NULL ,
 UNIQUE ( `name`, `bar` )
) ENGINE = INNODB;

MySQL is allowing the following statement to be repeated, resulting in duplicates.

INSERT INTO Foo (`bar`, `name`) VALUES (NULL, 'abc');

despite having

UNIQUE ( `name`, `bar` )

Why is this tolerated and how do I stop it?

like image 701
spraff Avatar asked Feb 14 '12 16:02

spraff


People also ask

Can Unique Key have multiple null values in MySQL?

Unique key constraints can accept only one NULL value for column. Unique constraints are also referenced by the foreign key of another table.

Can unique constraint have multiple null values?

You can insert NULL values into columns with the UNIQUE constraint because NULL is the absence of a value, so it is never equal to other NULL values and not considered a duplicate value. This means that it's possible to insert rows that appear to be duplicates if one of the values is NULL .

How many null values are allowed in unique constraint?

Another difference is that the UNIQUE constraint allows for one NULL value, but the PRIMARY KEY does not allow NULL values.


3 Answers

Warning: This answer is outdated. As of MySQL 5.1, BDB is not supported.

It depends on MySQL Engine Type. BDB doesn't allow multiple NULL values using UNIQUE but MyISAM and InnoDB allows multiple NULLs even with UNIQUE.

like image 104
Emir Akaydın Avatar answered Nov 14 '22 20:11

Emir Akaydın


In general, depending on the storage engine, NULL may or may not be seen as a unique value. You must be using a storage engine which doesn't recognise NULL as a unique value, eg. InnoDB or MyISAM.

To get around this you can create a "null value", such as 99999999, which you can recognise as NULL as there is no way to change how your storage engine decides to deal with nulls in unique keys.

like image 24
anothershrubery Avatar answered Nov 14 '22 20:11

anothershrubery


UPDATE: You should use the idea suggested by @greenoldman in the comment below instead. Create a boolean field with triggers to set the value based on whether your nullable field is NULL or not and then combine the boolean field in a unique constraint with the other fields that define uniqueness.


I found a way around this problem if you must enforce the unique constraint but also need to have a foreign key on the column, thus requiring it to be nullable. My solution was derived from this and will require a little extra space. This is an example with a numeric id field.

The basic concept is that you have to create another non-nullable field that will have the value of your nullable field with the foreign key duplicated into it with a trigger. The unique constraint will then be enforced on the non-nullable duplicate field. To do this you need to define a non-nullable field with a default value of 0 similar to this:

ALTER TABLE `my_table` ADD  `uniq_foo` int(10) UNSIGNED NOT NULL DEFAULT '0';

Then you just have to define some triggers like this:

DROP TRIGGER IF EXISTS `my_table_before_insert`;
DELIMITER ;;
CREATE TRIGGER `my_table_before_insert` BEFORE INSERT ON `my_table`
FOR EACH ROW
BEGIN
    SET NEW.uniq_foo = IFNULL(NEW.foo_id, 0);
END;;
DELIMITER ;

DROP TRIGGER IF EXISTS `my_table_before_update`;
DELIMITER ;;
CREATE TRIGGER `my_table_before_update` BEFORE UPDATE ON `my_table`
FOR EACH ROW
BEGIN
    SET NEW.uniq_foo = IFNULL(NEW.foo_id, 0);
END;;
DELIMITER ;
like image 39
Justin Warkentin Avatar answered Nov 14 '22 21:11

Justin Warkentin