Stop MySQL tolerating multiple NULLs in a UNIQUE c

2020-04-03 03:01发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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 ;


回答4:

BDB doesn't allow multiple NULL values using UNIQUE. But MySQL drop the BDB engine (http://dev.mysql.com/doc/relnotes/mysql/5.1/en/news-5-1-12.html).

So now : http://dev.mysql.com/doc/refman/5.5/en/create-index.html

For all engines, a UNIQUE index permits multiple NULL values for columns that can contain NULL. If you specify a prefix value for a column in a UNIQUE index, the column values must be unique within the prefix.



标签: mysql sql mysql5