2012-02-14 18 views
10

マイSQLスキーマは、MySQLが次の文は、重複が生じ、繰り返すことができるようにされてが停止MySQLがUNIQUE制約で複数のNULLを許容

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

です。

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

、これは許容され、私はそれをどのように停止するのはなぜ

UNIQUE (`name`, `bar`) 

を有しているにもかかわらず?

+2

すると、その主くださいキー。いずれのフィールドでもNULLを許可しません。または、フィールドをnullにしないでください。 mysql docs:http://dev.mysql.com/doc/refman/5.1/en/create-index.html「すべてのエンジンでは、UNIQUEインデックスはNULLを含むことができる列に対して複数のNULL値を許可します」。 –

+0

'NULL'は値ではありません。 @MarcBによれば、このためにNULLを許可しないことが必要です – JNK

+0

それはバグのようです:http://bugs.mysql.com/bug.php?id=8173。 (バグレポートの少なくとも一部の人はそうだと思う) –

答えて

12

警告:この回答は古くなっています。 MySQL 5.1以降、BDBはサポートされていません。

これはMySQL Engine Typeに依存します。 BDBは、UNIQUEを使用して複数のNULL値を許可しませんが、MyISAMInnoDBは、UNIQUEでさえ複数のNULLを許可します。

2

通常、ストレージエンジンに応じて、NULLは一意の値と見なされる場合とされない場合があります。固有の値としてNULLを認識しないストレージエンジンを使用している必要があります。 InnoDBまたはMyISAMです。

これを回避するには、99999999などの「ヌル値」を作成できます。これは、NULLとして認識できます。ストレージエンジンがユニークキーでヌルを処理する方法を変更する方法がないためです。

+4

-1は、魔法の数の解を示唆しています。実際の答えは 'NULL'を理解し、適切に使用することです。 – JNK

+0

OPは「NULL」は確かに魔法の価値だと思っているので、私は-1を与えません。概念の理解を深めてはいけません。 –

0

BDBでは、UNIQUEを使用して複数のNULL値を許可しません。 しかし、MySQLはBDBエンジン(http://dev.mysql.com/doc/relnotes/mysql/5.1/en/news-5-1-12.html)を削除します。だから今

:すべてのエンジンのためにhttp://dev.mysql.com/doc/refman/5.5/en/create-index.html

は、UNIQUEインデックスはNULLを含めることができるカラムの複数NULL値を許可します。 UNIQUE索引の列に接頭部の値を指定する場合、列の値は接頭部内で一意でなければなりません。

1

更新日:@ greenoldmanが提案したアイデアを下のコメントに代わりに使用してください。トリガを使用してブール値フィールドを作成して、null可能フィールドがNULLかどうかに基づいて値を設定し、一意性制約のブール値フィールドと一意性を定義する他のフィールドを結合します。


私はあなたがユニーク制約を施行しなければならない場合は、この問題を回避する方法を発見しただけでなく、列に外部キーを持っている必要があり、これNULL可能であることを、それを必要とします。私の解決策はthisから派生したもので、少し余分なスペースが必要です。これは、数値のidフィールドを持つ例です。

基本的な考え方は、トリガーで外部キーが複製されたnullableフィールドの値を持つ別の非null可能フィールドを作成する必要があるということです。重複しない重複フィールドに一意の制約が適用されます。

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

その後、あなたはこのようないくつかのトリガを定義する必要があります:あなたはこれに似た0のデフォルト値と非NULL可能フィールドを定義する必要があり、このためには

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 ; 
+0

ありがとうございます!コンセプトは素晴らしいですが、実行は使いにくいです。しかし、この余分な列はnullの場合は '0'、not-nullの場合は' 1'を保持する 'TINYINT(1)'(意味は-bool)でなければなりません(またはその逆ではありません)。 null可能フィールド。ユニークなインデックスでは、この余分な列**を追加で**使用する必要があります。そしてそれだけです - 魔法の価値のためにあなたが葛藤することはありません。 – greenoldman

+0

@greenoldman素晴らしいアイデア! FWIWでは、非常に大規模でアクティブなデータセットを使用してこのテクニックを使用しても問題はありませんでしたが、私はあなたのアイデアがさらに好きです。 –

関連する問題