2010-11-05 50 views
1

我正在開發使用symfony 1.4(和Doctrine),並有一個MySQL數據庫表在多列上的唯一索引。首先,到目前爲止表中的YAML定義如下:唯一的多列和NULL值

Campaign: 
    actAs: 
    Sluggable: 
     fields: [name] 
     canUpdate: true 
     uniqueBy: [merchant_id, deleted_at] 
    Timestampable: ~ 
    SoftDelete: ~ 
    columns: 
    merchant_id:  { type: integer, notnull: true } 
    name:    { type: string(255), notnull: true, notblank: true } 
    start_date:  { type: date, notnull: true, notblank: true } 
    end_date:   { type: date, notnull: true, notblank: true } 
    indexes: 
    unique_name: { fields: [name, merchant_id, deleted_at], type: unique } 
    relations: 
    Merchant: { local: merchant_id, foreign: id } 

正如您所看到的,我必須處理屬於商家的廣告系列。一個活動知道它的商人,並有一個名字(以及開始日期和結束日期)。廣告系列的名稱應該是唯一的 - 不是全局的,而是針對特定的商家。到此爲止,我需要一個關於廣告系列名稱和相應商家的唯一索引。但是,由於表格「充當SoftDelete」,用戶應該能夠創建一個名稱已經存在用於「軟刪除」活動的新活動,deleted_at列也必須是唯一索引的一部分。您看到,廣告系列名稱的唯一性僅涉及相應商家的已刪除廣告系列。

現在來到實際問題:由於列deleted_at NULL的唯一索引中的所有未刪除的廣告活動,並始終作爲唯一處理空值,所有的活動都不允許有非唯一名稱 - 真正的意義。我知道,這適用於MyISAM和InnoDB表,但不適用於BDB表。但是,如果你知道我的意思,切換到BDB不是我最喜歡的選擇。

現在來到實際問題:除了將MySQL引擎更改爲BDB以外,還有其他可能的選擇嗎?解決方法可以是重命名軟刪除的廣告系列,例如name = 'DELETED AT ' + deleted_at + ': ' + name。這一方面有一個好處,即所有軟刪除的廣告系列即使在它們被恢復的情況下也會具有唯一的名稱(將deleted_at重置爲NULL)。 deleted_at列不再是唯一索引的一部分,因此,所有的廣告系列(未刪除,軟刪除以及恢復一次)都將具有唯一的名稱 - 涉及相應的商家。但另一方面,我認爲這不是最優雅的解決方案。你對此有何看法和專業知識?

我非常感謝你,並對你的貢獻感到高興。
Flinsch。

回答

1

我認爲你可以保持你的基本結構,你只需要一種方法來使deleted_at NOT NULL。這意味着你需要給它一個默認值。使用的默認值是0或0000-00-00 00:00:00。

我的建議是添加一個新列來標記行是否被邏輯刪除。你可以稱之爲「IS_DELETED」。然後添加deleted_at的默認值並將其設置爲非空,並在唯一索引中包含is_deleted。

這裏的行動這種做法的一個很簡單的例子:

mysql> create table merchant(
    -> id int unsigned not null auto_increment, 
    -> name varchar(50) not null, 
    -> is_deleted tinyint not null default 0, 
    -> deleted_at datetime not null default 0, 
    -> primary key (id), 
    -> unique key name_and_deleted_at (name,is_deleted,deleted_at) 
    ->) ENGINE = InnoDB; 
Query OK, 0 rows affected (0.08 sec) 

mysql> 
mysql> -- successful inserts 
mysql> insert into merchant (name,is_deleted) values ('foo',0); 
Query OK, 1 row affected (0.00 sec) 

mysql> insert into merchant (name,is_deleted) values ('bar',0); 
Query OK, 1 row affected (0.00 sec) 

mysql> 
mysql> -- insert failure due to duplicate name 
mysql> insert into merchant (name,is_deleted) values ('foo',0); 
ERROR 1062 (23000): Duplicate entry 'foo-0-0000-00-00 00:00:00' for key 'name_and_deleted_at' 
mysql> -- logical delete 
mysql> update merchant set is_deleted = true, deleted_at = now() where name = 'foo'; 
Query OK, 1 row affected (0.00 sec) 
Rows matched: 1 Changed: 1 Warnings: 0 

mysql> -- now the insert succeeds 
mysql> insert into merchant (name,is_deleted) values ('foo',0); 
Query OK, 1 row affected (0.01 sec) 

mysql> 
mysql> -- show data 
mysql> select id,name,is_deleted,deleted_at 
    -> from merchant 
    -> order by id; 
+----+------+------------+---------------------+ 
| id | name | is_deleted | deleted_at   | 
+----+------+------------+---------------------+ 
| 1 | foo |   1 | 2010-11-05 13:54:17 | 
| 2 | bar |   0 | 0000-00-00 00:00:00 | 
| 4 | foo |   0 | 0000-00-00 00:00:00 | 
+----+------+------------+---------------------+ 
3 rows in set (0.00 sec) 
+1

艾克,感謝您的答覆。我也有一個非常相似的想法。我認爲附加標誌'is_deleted'並不是真的需要,因爲我可以檢查'deleted_at'是否爲0。不過,我之前避開它,因爲我不確定是否手動將'deleted_at'設置爲NOT NULL值會導致無法預料的後果,例如生成對象列表等 - 因爲'deleted_at'就像是一個「magic column 「symfony /主義」。但我想我會試試看... :) – Flinsch 2010-11-05 22:30:06

+0

MySQL 5.7可以幫助虛擬列!:http://stackoverflow.com/questions/42064759/how-to-do-unique-constraint-works-與無效值在MySQL的/ 42291845#42291845 – Nothus 2017-02-17 07:38:37