假设我有两个表,user
和comment
。它们的表定义如下所示:
CREATE TABLE `user` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) NOT NULL,
`deleted` TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`username`)
) ENGINE=InnoDB;
CREATE TABLE `comment` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`user_id` INTEGER NOT NULL,
`comment` TEXT,
`deleted` TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
CONSTRAINT `fk_comment_user_id` FOREIGN KEY (`user_id`)
REFERENCES `user` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;
这对于强制执行数据完整性非常有用,但我希望能够“删除”用户并保留所有注释(供参考)。
为此,我添加了deleted
,以便我可以SET deleted = 1
记录。通过默认列出deleted = 0
的所有内容,我可以隐藏所有已删除的记录,直到我需要它们为止。
到目前为止一切顺利。
问题出现在:
user
上的UNIQUE约束。我希望用户能够编辑自己的用户名,因此我不应将username
作为主键,删除用户时仍会遇到同样的问题。
有什么想法吗?
编辑以澄清:在下面添加了以下RedFilter的回答和评论。
我担心“已删除”的用户和评论对公众不可见,但仅对管理员可见,或者为了计算统计信息而保留。
这个问题是一个思想实验,用户和评论表只是一个例子。尽管如此,username
并不是最好用的; RedFilter提供有关用户身份的有效点,特别是当记录在公共环境中呈现时。
关于“为什么用户名不是主键?”:这只是一个例子,但是如果我将其应用于一个真正的问题,我将需要在一个约束下工作假定存在代理主键的现有系统。
答案 0 :(得分:26)
在字段上添加唯一约束(用户名,已删除) 将“已删除”的字段类型更改为INTEGER。
在删除操作期间(可以在触发器中或在您需要实际删除用户的部分代码中完成)将id字段的值复制到已删除的字段。
这种方法允许你:
字段'已删除'不能只有2个值,因为以下方案不起作用:
答案 1 :(得分:4)
只需在username
上保留唯一索引或约束。您不希望新用户能够使用已删除的名称,因为不仅可能存在关于身份的一般混淆,而且如果您仍然显示已删除用户的旧帖子,则会错误地将其发布为具有相同名称的新用户。
当新用户注册时,您通常会在允许注册完成之前检查该名称是否正在使用,因此此处不应存在冲突。
答案 2 :(得分:1)
我对软删除的实用解决方案是通过创建带有以下列的新表进行归档:original_id
,table_name
,payload
(以及可选的主键`id)。< / p>
其中original_id
是已删除记录的原始ID,table_name
是已删除记录的表名(在您的情况下为"user"
),payload
是JSON字符串化的删除记录的所有列中的字符串。
我还建议在original_id
列上建立索引以供以后的数据检索。
通过这种方式存档数据。您将拥有这些优势
这已经是讨论here,它解释了为什么软删除在实践中不是一个好主意。软删除会在将来引入一些潜在的麻烦,例如计数记录,...