Mar*_*łek 18 mysql sql database unique-constraint
我有简单的类别表.类别可以具有父类别(par_cat列),如果它是主类别,则为null,并且具有相同的父类别,不应该有2个或更多具有相同名称或URL的类别.
该表的代码:
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`);
Run Code Online (Sandbox Code Playgroud)
问题是,即使我有唯一的密钥,它也不起作用.如果我尝试在数据库2中插入已par_cat设置为null相同名称和URL的类别,则可以将这两个类别插入到数据库中而不会出现问题(并且它们不应该).但是,如果我选择其他类别par_cat(例如1假设存在id为1的类别),则只插入第一条记录(这是所需的行为).
问题 - 如何处理这种情况?我读到了:
UNIQUE索引创建一个约束,使索引中的所有值必须是不同的.如果您尝试添加具有与现有行匹配的键值的新行,则会发生错误.此约束不适用于除BDB存储引擎之外的NULL值.对于其他引擎,UNIQUE索引允许包含NULL的列的多个NULL值.如果为UNIQUE索引中的列指定前缀值,则列值在前缀中必须是唯一的.
但是如果我有多个列我预期的唯一它不是的情况下(仅par_cat可以为空,name且url不能为null).因为par_cat对id同一个表的引用但某些类别没有父类别,所以它应该允许null值.
Tho*_*ner 29
这符合SQL标准的定义.NULL表示未知.如果您有两个par_cat = NULL和name ='X'的记录,那么这两个NULL不会被视为保持相同的值.因此,它们不违反唯一键约束.(好吧,有人可能会争辩说NULL仍然可能意味着相同的值,但是应用这个规则会使得使用唯一索引和可空字段几乎不可能,因为NULL也可以意味着1,2或其他任何值.所以他们做得很好如他们所做的那样定义它.)
由于MySQL不支持您可以使用索引的功能索引,因此ISNULL(par_cat,-1), name您唯一的选择是使par_cat为0或-1的NOT NULL列或"无父"的任何内容,如果您希望约束起作用.
Yos*_*eph 12
我看到这是在 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);
Run Code Online (Sandbox Code Playgroud)
generate_par_cat 是一个虚拟的生成列,所以它没有存储空间。当用户插入(或更新)时,唯一索引会导致生成的 generate_par_cat 的值即时生成,这是一个非常快速的操作。
以防万一你来自 Laravel...
这是 Laravel 的 Virtual Column 迁移版本,用于解决UNIQUE其中一列NULL有价值时的问题
$table->integer('generated_par_cat')->virtualAs('ifNull(par_cat, 0)');
$table->unique(['name', 'generated_par_cat'], 'name_par_cat_unique');
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11314 次 |
| 最近记录: |