简体   繁体   English

具有软删除、唯一键和外键约束的 MySQL

[英]MySQL with Soft-Deletion, Unique Key and Foreign Key Constraints

Say I have two tables, user and comment .假设我有两个表, usercomment They have table definitions that look like this:它们的表定义如下所示:

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;

This is great for enforcing data integrity and all that, but I want to be able to "delete" a user and keep all its comments (for reference's sake).这对于执行数据完整性和所有这些非常有用,但我希望能够“删除”用户并保留其所有评论(仅供参考)。

To this end, I've added deleted so that I can SET deleted = 1 on a record.为此,我添加了deleted ,以便我可以在记录上SET deleted = 1 By listing everything with deleted = 0 by default, I can hide away all the deleted records until I need them.通过默认列出所有deleted = 0的内容,我可以隐藏所有已删除的记录,直到我需要它们为止。

So far so good.到目前为止,一切都很好。

The problem comes when:问题出现在:

  • A user signs up with a username (say, "Sam"),用户使用用户名(例如“Sam”)注册,
  • I soft-delete that user (for unrelated reasons), and我软删除该用户(出于不相关的原因),并且
  • Someone else comes along to sign up as Sam, and suddenly we've violated the UNIQUE constraint on user .其他人来注册为 Sam,突然我们违反了对user的 UNIQUE 约束。

I want users to be able to edit their own usernames, so I shouldn't make username the primary key, and we'll still have the same problem when deleting users.我希望用户能够编辑自己的用户名,所以我不应该将username为主键,删除用户时我们仍然会遇到同样的问题。

Any thoughts?有什么想法吗?

Edit for clarification: Added following RedFilter's answer and comments below.编辑澄清:在下面添加了 RedFilter 的答案和评论。

I'm concerned with the case where the "deleted" users and comments are not visible to the public, but are visible only administrators, or are kept for the purpose of calculating statistics.我担心“已删除”的用户和评论对公众不可见,但只有管理员可见,或者为了计算统计数据而保留。

This question is a thought experiment, with the user and comment tables just being examples.这个问题是一个思想实验,用户和评论表只是示例。 Still, username wasn't the best one to use;尽管如此, username并不是最好的用户名。 RedFilter makes valid points about user identity, particularly when the records are presented in a public context. RedFilter 对用户身份提出了有效的观点,特别是当记录在公共上下文中呈现时。

Regarding " Why isn't username the primary key? ": this is just an example, but if I apply this to a real problem I'll be needing to work within the constraints of an existing system that assumes the existence of a surrogate primary key.关于“为什么用户名不是主键? ”:这只是一个示例,但是如果我将其应用于实际问题,我将需要在假设存在代理主键的现有系统的约束下工作钥匙。

Add unique constraint on fields(username, deleted) Change field type for 'deleted' to INTEGER.添加对字段的唯一约束(用户名,已删除)将“已删除”的字段类型更改为整数。

During delete operation (it can be done in trigger, or in part of code where you need actually delete user) copy value of id field to deleted field.在删除操作期间(可以在触发器中完成,也可以在需要实际删除用户的部分代码中完成)将 id 字段的值复制到已删除字段。

This approach allow you:这种方法允许您:

  • keep unique names for active users (deleted = 0)保留活动用户的唯一名称(已删除 = 0)
  • allow delete users with same username several times允许多次删除具有相同用户名的用户

Field 'Deleted' can't have only 2 value because the following scenario will not work:字段“已删除”不能只有 2 个值,因为以下情况不起作用:

  1. you create user 'Sam'您创建用户“山姆”
  2. User Sam is deleted用户 Sam 被删除
  3. You create new user witn userName 'Sam'您使用 userName 'Sam' 创建新用户
  4. You try delete user with userName 'Sam' - fail.您尝试删除用户名为“Sam”的用户 - 失败。 You already have record userName = 'Sam' and deleted = '1'您已经有记录 userName = 'Sam' 并已删除 = '1'

Just keep the unique index or contraint on username .只需在username上保留唯一索引或约束即可。 You do not want new users to be able to use the deleted name, as not only could there be general confusion about identity, but if you are still showing the old posts from the deleted user, then they will mistakenly be understood to be posted by the new user with the same name.您不希望新用户能够使用已删除的名称,因为这不仅会造成普遍的身份混淆,而且如果您仍然显示已删除用户的旧帖子,那么他们会被误认为是由具有相同名称的新用户。

When a new user registers, you would normally check to see if the name is in use before allowing registration to complete, so there should be no conflict here.当新用户注册时,您通常会在允许注册完成之前检查该名称是否在使用中,因此这里应该没有冲突。

. .

My practical solution for soft-delete is archiving by creating a new table with the following columns: original_id , table_name , payload , (and an optional primary key `id).我的软删除实用解决方案是通过创建一个包含以下列的新表进行归档: original_idtable_namepayload ,(和一个可选的主键 `id)。

Where original_id is the original id of deleted record, table_name is the table name of the deleted record ( "user" in your case), payload is JSON-stringified string from all columns of the deleted record.其中original_id是已删除记录的原始 id, table_name是已删除记录的表名(在您的情况下为"user" ), payload是来自已删除记录的所有列的 JSON 字符串化字符串。

I also suggest making an index on the column original_id for latter data retrievement.我还建议在original_id列上建立一个索引,以便以后检索数据。

By this way of archiving data.通过这种方式归档数据。 You will have these advantages你将拥有这些优势

  • Keep track of all data in history跟踪历史记录中的所有数据
  • Have only one place to archive records from any table, regardless of the deleted record's table structure无论已删除记录的表结构如何,都只有一个地方可以存档任何表中的记录
  • No worry of the unique index in the original table不用担心原表的唯一索引
  • No worry of checking foreign index in the original table不用担心检查原表中的外部索引

The is already a discussion here explaining why soft-deletion is not a good idea in practice.这里已经有一个讨论解释了为什么软删除在实践中不是一个好主意。 Soft-delete introduces some potential troubles in the future such as counting records, ...软删除会在未来引入一些潜在的麻烦,例如计数记录,...

You can do the following:您可以执行以下操作:

 CREATE UNIQUE INDEX YourIndexName 
 ON YourTableName (Column1, Column2, Column3)
 WHERE IsDeleted = 0

You can find the complete answer here:你可以在这里找到完整的答案:

enter link description here 在此处输入链接描述

We are using mysql generated column ( https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html ) to solve the problem. We are using mysql generated column ( https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html ) to solve the problem.

ALTER TABLE `dummy` ADD `not_deleted` tinyint AS (if(`deleted_at` is null,1,NULL)) STORED;
CREATE UNIQUE INDEX `IDX_4b8d12e176ac3d69d21ebebd5a` ON `dummy` (`code`, `not_deleted`);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM