【问题标题】:mysql not to allow NULL on unique indexmysql不允许唯一索引上的NULL
【发布时间】:2013-03-12 16:57:19
【问题描述】:

我使用用户的 mysql 创建了一个表。 该表的列是序列、电子邮件、date_created 和 date_canceled。 主要是序列,我试图为电子邮件和 date_canceled 创建一个唯一索引 - 所以当电子邮件处于活动状态时(从未取消 - 意味着 date_canceled 为 NULL),不会再插入另一个活动电子邮件或发生这种情况。

我知道它可以使用 oracle db 来完成,但使用 mysql 唯一索引允许 NULL。

任何建议如何处理? 谢谢!

【问题讨论】:

  • 您是否尝试将NOT NULL 应用于您的索引?
  • 我想在 date_canceled 列上允许为空,因为这样我就知道它的活动...
  • @ficuscr - 不是骗子。 10009219 询问“条件”NOT NULL 约束,这与 OP 在这里要求的不同。

标签: php mysql sql oracle mysql-workbench


【解决方案1】:

我对您的问题的理解是,您希望每个用户在任何时候都有一封与其关联的活动电子邮件,同时保留用户以前邮件的历史记录。


解决方案 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())

【讨论】:

    【解决方案2】:

    BDB 存储引擎会将NULL 视为唯一值,只允许单个NULL 值。

    From the docs:

    如果允许NULL 值的列具有唯一索引,则只有一个 NULL 值是允许的。这与其他存储引擎不同, 允许在唯一索引中有多个 NULL 值。

    如果您不想更改存储引擎来获得此行为,您的其他选择是更改表结构以将活动电子邮件和已取消电子邮件存储在不同的表中...创建一个触发器以强制执行此行为...或为active 电子邮件分配一个神奇的日期值,并且不再允许此列中的NULL 值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-03
      • 1970-01-01
      相关资源
      最近更新 更多