2015-08-08 105 views
6

我有一個事務表,其中幾行可以存儲三個屬性(foo_id,bar_id),最後一個屬性是deleted_at,這裏的想法是確保我只有一個活動行一次(deleted_at ='0000-00-00 00:00:00')。MySQL - 具有日期時間的複合唯一鍵

但我收到以下錯誤:

mysql> UPDATE epic_table SET deleted_at = NOW() WHERE (foo_id = '1' AND bar_id IN ('18')); 
ERROR 1062 (23000): Duplicate entry '18-1-2015-08-08 16:35:46' for key 'epic_table_ibuk_1' 

這裏是架構:

CREATE TABLE `epic_table` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `foo_id` bigint(20) unsigned NOT NULL, 
    `bar_id` bigint(20) unsigned NOT NULL, 
    `created_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 
    `deleted_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `epic_table_ibuk_1` (`foo_id`,`bar_id`,`deleted_at`), 
    KEY `epic_table_ibfk_1` (`foo_id`), 
    KEY `epic_table_ibfk_2` (`bar_id`), 
    CONSTRAINT `epic_table_ibfk_1` FOREIGN KEY (`foo_id`) REFERENCES `foo` (`id`), 
    CONSTRAINT `epic_table_ibfk_2` FOREIGN KEY (`bar_id`) REFERENCES `bar` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 

立即站出來的唯一的事情是MySQL可能沒有考慮的時間部分日期時間值,因此抱怨18-1-2015-08-08的另一行。

任何想法如何保持多行靈活性,同時能夠強制在任何給定時間只有一行是「活動的」?

+2

不'(foo_id = '1' AND IN bar_id( '18'))'完全匹配_一行?如果它實際上匹配多個,則新值NOW()會導致違反密鑰約束。如果有多於一個,但是你想選擇一個具有LIMIT(這將避免違反唯一性)的條件,那麼你會根據什麼標準對它們進行排序? –

+0

不,可能有X行的foo_id = 1,bar_id = 18,它們之間的唯一差異將是deleted_at值,只有其中一個具有deleted_at ='0000-00-00 00:00:00 ' –

+2

@MikePurcell如果你有很多行,foo_id = 1和bar_id = 18,你的update語句要求所有這些行都用deleted_at = NOW()更新,因此導致非唯一性。你能改變你的where子句來使用'和deleted_at ='0000-00-00 00:00:00''嗎? – zedfoxus

回答

7

從一個答案評論更改...

聲明:

UPDATE epic_table SET deleted_at = NOW() 
WHERE (foo_id = '1' AND bar_id IN ('18')); 

嘗試設置deleted_atfoo_id = 1bar_id = 18所有行。由於foo_id,bar_id和deleted_at組合被標記爲唯一,所以這種改變最終違反了唯一約束。

只有可能需要略微改變,像這樣的聲明,知道只有一條記錄可能有deleted_at = '0000-00-00 00:00:00'

UPDATE epic_table SET deleted_at = NOW() 
WHERE (foo_id = '1' AND bar_id IN ('18') and deleted_at = '0000-00-00 00:00:00');