我用mysql创建了一个表。表的列是sequence, email, date_created和date_canceled。primary是序列,我试图为email和date_canceled创建一个唯一的索引——所以当email处于活动状态时(从未被取消过——意味着date_cancelled为NULL),就不会有另一个活动的email被插入或出现这种情况。
我知道它可以用oracle db完成,但mysql的唯一索引允许NULL。
有什么建议吗?谢谢!
我对你的问题的理解是,你希望每个用户在任何时候都有一个与他们相关联的活动电子邮件,同时保持用户以前邮件的历史记录。
解决方案1
从MySQL 5.7.5开始,你可以创建一个生成的列,然后在上面设置一个唯一的约束。http://mysqlserverteam.com/generated-columns-in-mysql-5-7-5/。例如
create table UniqueActiveEmailDemo
(
sequence bigint not null auto_increment primary key
, userId bigint not null
, email nvarchar(1024) not null
, date_created timestamp default now()
, date_cancelled datetime
, single_active_mail_per_user bigint as (if(date_cancelled is null, userId, null))
);
alter table UniqueActiveEmailDemo
add unique UK_UniqueActiveEmailDemo_SingleActiveMailPerUser
(single_active_mail_per_user);
因为MySQL允许在一个唯一的约束中有多个空,当记录被取消时,生成列有一个空值;你想要多少记录就有多少记录。但是,如果记录没有被取消,生成的列返回用户的id;这受制于唯一约束,所以如果有另一个具有相同用户id的活动记录,唯一约束将抛出异常。
解决方案2
Sql Fiddle: http://sqlfiddle.com/#!9/b54dce/2
然而,一个更干净的方法,也适用于早期版本,将简单地防止date_cancelled为空;而不是将它设置为一个固定的值,在遥远的将来,任何项目尚未取消,然后有user_id和date_cancelled的组合为空;例如
create table UniqueActiveEmailDemo
(
sequence bigint not null auto_increment primary key
, userId bigint not null
, email nvarchar(1024) not null
, date_created timestamp default now()
, date_cancelled datetime not null default '9999-12-31 23:59:59'
);
ALTER TABLE UniqueActiveEmailDemo
ADD UNIQUE UK_UniqueActiveEmailDemo_SingleActiveMailPerUser
(userId, date_cancelled);
- 这里的一个区别是你不能在同一天取消同一用户的两个记录;但在现实中,我认为你永远不会得到它。
- 这也意味着你可以有记录,在未来得到。为了避免这个问题,你可以将
9999-12-31 23:59:59
视为空值;也就是说,任何具有该值的记录都是活动的。 - 您还可以通过在
where now() between date_active and date_cancelled
上添加active_from
日期和过滤器来解决上述问题;然而,你需要添加更多的检查,以确保同一用户的活动Windows不会重叠; - 添加检查约束将防止将来的值;但遗憾的是,它们目前没有在MySQL中使用(尽管是有效的语句)。http://dev.mysql.com/doc/refman/5.7/en/create-table.html
, date_cancelled datetime not null default '9999-12-31 23:59:59'
check (date_cancelled = '9999-12-31 23:59:59' or date_cancelled <= now())
BDB存储引擎将NULL
视为唯一值,只允许一个NULL
值。
From the docs:
如果允许
NULL
值的列有唯一索引,则只有一个允许设置NULL
的值。这与其他存储引擎不同,允许多个NULL
值在唯一索引中。
如果你不想改变你的存储引擎来获得这种行为,你的其他选择是改变你的表结构,在不同的表中存储活动的电子邮件和取消的电子邮件…创建一个触发器来强制执行此行为…或者为active
电子邮件指定一个神奇的日期值,并且不再允许NULL
值在此列中。