我有简单的类别表。类别可以包含父类别(par_cat
列),如果它是主类别,则为null,并且具有相同的父类别,不应该是具有相同名称或网址的2个或更多类别。
此表的代码:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(10) unsigned NOT NULL,
`par_cat` int(10) unsigned DEFAULT NULL,
`lang` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'pl',
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`url` varchar(120) COLLATE utf8_unicode_ci NOT NULL,
`active` tinyint(3) unsigned NOT NULL DEFAULT '1',
`accepted` tinyint(3) unsigned NOT NULL DEFAULT '1',
`priority` int(10) unsigned NOT NULL DEFAULT '1000',
`entries` int(10) unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3 ;
ALTER TABLE `categories`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `categories_name_par_cat_unique` (`name`,`par_cat`),
ADD UNIQUE KEY `categories_url_par_cat_unique` (`url`,`par_cat`),
ADD KEY `categories_par_cat_foreign` (`par_cat`);
ALTER TABLE `categories`
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3;
ALTER TABLE `categories`ADD CONSTRAINT `categories_par_cat_foreign`
FOREIGN KEY (`par_cat`) REFERENCES `categories` (`id`);
问题是,即使我有独特的键,它也不起作用。如果我尝试将par_cat
设置为null
且名称和网址相同的数据库2插入数据库中,则可以将这两个类别插入到数据库中而不会出现问题(并且他们不应该这样做)。但是,如果我选择其他类别par_cat
(例如1假设存在id为1的类别),则只会插入第一条记录(以及所需的行为)。
问题 - 如何处理此案件?我读到了:
UNIQUE索引创建一个约束,以便索引中的所有值 必须是截然不同的如果您尝试使用a添加新行,则会发生错误 与现有行匹配的键值。此约束不适用 除了BDB存储引擎之外的NULL值。对于其他引擎,a UNIQUE索引允许可以包含的列的多个NULL值 空值。如果为UNIQUE索引中的列指定前缀值, 列值在前缀中必须是唯一的。
但是,如果我在多个列上具有唯一性,我预计情况并非如此(只有par_cat
可以为空,name
和url
不能为空)。由于par_cat
引用了同一个表中的id
,但某些类别没有父类别,因此应该允许null
个值。
答案 0 :(得分:27)
这适用于SQL标准的定义。 NULL表示未知。如果您有两个par_cat = NULL和name =' X'的记录,那么这两个NULL不会被视为保持相同的值。因此,他们不会违反唯一的密钥约束。 (好吧,有人可能会认为NULL仍然可能意味着相同的值,但应用此规则将使得使用唯一索引和可空字段几乎是不可能的,因为NULL也可能意味着1,2或其他其他价值。所以他们很好地定义了它,就像我认为的那样。)
由于MySQL不支持您可以在ISNULL(par_cat,-1), name
上创建索引的功能索引,因此您唯一的选择是使par_cat为0或-1的NOT NULL列或者#34;没有父级&#34 ;,如果你想让你的约束起作用。
答案 1 :(得分:0)
我看到这是在2014年提出的。 但是,通常是从MySQL请求的:例如https://bugs.mysql.com/bug.php?id=8173和https://bugs.mysql.com/bug.php?id=17825。 人们可以点击影响我尝试从MySQL获得关注。
从MySQL 5.7开始,我们现在可以使用以下解决方法:
ALTER TABLE categories
ADD generated_par_cat INT UNSIGNED AS (ifNull(par_cat, 0)) NOT NULL,
ADD UNIQUE INDEX categories_name_generated_par_cat (name, generated_par_cat),
ADD UNIQUE INDEX categories_url_generated_par_cat (url, generated_par_cat);
generate_par_cat是一个虚拟的生成列,因此它没有存储空间。当用户插入(或更新)时,唯一索引会导致动态生成generate_par_cat的值,这是一个非常快速的操作。