我对您的问题的理解是,您希望每个用户在任何时候都有一封与其关联的活动电子邮件,同时保留用户以前邮件的历史记录。
解决方案 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 小提琴: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 视为您有空值;即具有该值的任何记录都是活动的。
- 您还可以通过添加
active_from 日期并过滤where now() between date_active and date_cancelled 来解决上述问题;但是,您需要添加更多检查以确保同一用户的活动窗口不会重叠;这使事情变得更加复杂。
- 添加检查约束可以防止未来的值;但遗憾的是它们目前没有在 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())