2013-03-22 93 views
2

我使用mysql的用戶創建了一個表。表格的 列是序列,電子郵件,date_created和date_canceled。 主要是序列和im試圖爲電子郵件和date_canceled做一個唯一的索引 - 所以當電子郵件處於活動狀態(從未取消 - 意味着date_canceled爲NULL)不會被插入另一個活動電子郵件或發生這種情況。mysql不允許在唯一索引處爲NULL

我知道它可以與Oracle數據庫,但與MySQL獨特的指數法允許NULL來完成。

任何建議如何處理這個? 謝謝!

+0

你嘗試過將'不NULL'你的指數? – 2013-03-22 18:07:30

+0

笨蛋? http://stackoverflow.com/questions/10009219/oracle-constraint-to-allow-null-value-如 – ficuscr 2013-03-22 18:07:53

+0

我想在date_canceled列允許null,因爲這樣我就知道它的活躍... – user2062262 2013-03-22 18:10:13

回答

0

BDB存儲引擎將把NULL作爲一個獨特的價值,只允許單個NULL值。

From the docs

如果允許NULL值的列都有一個唯一的索引,只有單一 NULL值是允許的。這與其他存儲引擎 不同,後者允許在唯一索引中使用多個NULL值。

如果你不想改變你的存儲引擎來獲得這種行爲,你的其他選擇是改變你的表結構來存儲活動的電子郵件,並取消在不同的表電子郵件...創建執行此行爲的觸發器...或爲此列分配active電子郵件的魔術日期值,並且不再允許NULL值。

0

我你的問題的理解是,你希望每個用戶與他們在任何時間關聯的一個有效的電子郵件,也保持用戶的郵件以往的歷史。


解決方案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允許在一個唯一約束中存在多個空值,其中記錄被取消,所以generate列的值爲空;所以你可以擁有儘可能多的記錄。但是,如果記錄未被取消,則生成的列將返回用戶的ID;這受限於唯一約束,所以如果存在具有相同用戶標識的另一個活動記錄,則唯一約束將引發異常。


解決方案2

的Sql小提琴:http://sqlfiddle.com/#!9/b54dce/2

但是這也適用於早期版本的更清潔的方法是簡單地阻止可空date_cancelled;相反,在未來任何未取消的項目中將其設置爲固定值,然後將user_id和date_cancelled的組合設置爲null;例如這裏

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日期和過濾器來解決上述問題;但是您需要添加更多檢查以確保同一用戶的活動窗口不會重疊;這使事情進一步複雜化。
  • 添加檢查約束可以防止將來的值;但令人遺憾的是,它們目前不在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())