与删除时MySQL外键中的限制相反?

与删除时MySQL外键中的限制相反?,mysql,database-design,foreign-keys,Mysql,Database Design,Foreign Keys,我正在重新设计一些应用程序安全日志表(比如用户登录、访问不同文件等),以满足一些不断变化的需求。它们最初是用MyISAM制作的,但并不经常被访问,切换到InnoDB并添加一组外键以实现数据完整性将更为有益。因为我无论如何都要重新制作桌子,所以我认为现在是进行转换的最佳时机 在大多数情况下,所有内容都是简单的外键,并按预期工作。我正在尝试一些奇怪的事情,但遇到的唯一问题是用户ID。这些日志表中的每条记录都与一个用户标识相关联,我想确保插入记录时给定的用户标识存在。添加一个引用用户表的外键可以解决这

我正在重新设计一些应用程序安全日志表(比如用户登录、访问不同文件等),以满足一些不断变化的需求。它们最初是用
MyISAM
制作的,但并不经常被访问,切换到
InnoDB
并添加一组外键以实现数据完整性将更为有益。因为我无论如何都要重新制作桌子,所以我认为现在是进行转换的最佳时机

在大多数情况下,所有内容都是简单的外键,并按预期工作。我正在尝试一些奇怪的事情,但遇到的唯一问题是用户ID。这些日志表中的每条记录都与一个用户标识相关联,我想确保插入记录时给定的用户标识存在。添加一个引用用户表的外键可以解决这个问题——这很简单。以下是一些简明、有代表性的表格:

用户表

CREATE TABLE tbl_user (
  id INT(10) NOT NULL AUTO_INCREMENT,
  first_name VARCHAR(50),
  PRIMARY KEY(id)
) ENGINE=InnoDB; 
CREATE TABLE tbl_login_time (
  id INT(10) NOT NULL AUTO_INCREMENT,
  user_id INT(10) NOT NULL,
  login_at TIMESTAMP NOT NULL,
  PRIMARY KEY(id),
  CONSTRAINT 'tbl_login_time_fk_1` FOREIGN KEY (user_id) REFERENCES tbl_user
      ON UPDATE CASCADE ON DELETE ???
) ENGINE=InnoDB; 
示例日志表

CREATE TABLE tbl_user (
  id INT(10) NOT NULL AUTO_INCREMENT,
  first_name VARCHAR(50),
  PRIMARY KEY(id)
) ENGINE=InnoDB; 
CREATE TABLE tbl_login_time (
  id INT(10) NOT NULL AUTO_INCREMENT,
  user_id INT(10) NOT NULL,
  login_at TIMESTAMP NOT NULL,
  PRIMARY KEY(id),
  CONSTRAINT 'tbl_login_time_fk_1` FOREIGN KEY (user_id) REFERENCES tbl_user
      ON UPDATE CASCADE ON DELETE ???
) ENGINE=InnoDB; 
我的问题是,我希望强制执行外键以进行插入、级联更新,但删除tbl_用户中的记录不会影响tbl_登录时间。通常情况下,用户会被标记为不活动,但每隔一段时间,就会有一个用户被完全删除,但需要维护日志

删除时列出了6个
选项,但没有一个听起来合适:

  • 限制:将阻止tbl\U用户中的删除
  • 无操作:像限制一样进行计算
  • CASCADE:会像我希望的那样在tbl_用户中删除,但也会在tbl_登录时删除
  • 设置NULL:将在tbl_用户中删除,并在tbl_登录时保留该行,但将数据置空。接近但没有雪茄
  • 设置默认值:MySQL识别它,但拒绝它
  • 删除时忽略:相当于限制
  • 我以前从未使用过像这样的外键(强制执行
    插入
    更新
    ,但不执行
    删除
    ),而且在阅读了许多其他问题后,似乎也不像其他人那样使用外键。这可能会告诉我这是一个错误的方法,但它能以某种方式工作吗

    我的问题是我希望为插入强制执行外键, 要级联的更新,但删除tbl_用户中的记录不会影响 登录时间不限

    使用外键约束无法实现这一点

    在某些应用程序中,ON DELETE SET NULL是有意义的。但您的应用程序本质上是存储在SQL数据库中的日志文件。您有一个严重的问题,您希望删除标识信息(用户),但在某些情况下保留其ID号。坦率地说,我不明白为什么您愿意保留用户23332在今天18:40登录的事实,而不关心您是否能够识别用户23332

    你有几个选择

    • 删除日志文件表,并将日志文件数据存储在文件系统中的文件中,而不是数据库中。如果我处在你的地位,我会首先考虑这个问题。如果我们讨论的是一个可以通过web访问的数据库,请确保日志文件存储在web根目录之外。(我将它与所有其他日志文件一起存储在/var/log下。)
    • 使用外键约束,切勿删除用户
    • 使用外键约束,并受ON DELETE SET NULL或ON DELETE SET DEFAULT的影响。在这个特定的应用程序中,ON DELETE SET NULL和ON DELETE SET DEFAULT在语义上是等价的。两者都用不识别用户的数据替换好数据。如果您无法识别用户23332,谁会在乎您是否知道她在今天18:40登录
    • 删除外键约束,并使用触发器执行任何您喜欢的操作

    我敢肯定,我们都同意最明显的选择——使用外键和ON DELETE CASCADE——对于您的应用程序来说是错误的。

    您能使用触发器吗?@njk如果需要,我可以向用户表添加触发器,但通常我会尽量避免使用它们。如果你不能使用外键,并且必须使用触发器,这将是一个可以接受的答案。删除用户记录,同时保留ID引用的日志似乎不太实际。在我看来,这是一个设计缺陷。如果你需要保留日志,为什么不使用一些停用标志/摆脱用户日志依赖性?@rosipov在做了一些工作并看到这里的前两条评论后,我认为这可能不是正确的方法。使用触发器确保只输入有效数据可能是一种方法,但我仍然很好奇,你能用外键实现这一点吗?外键管理依赖项(A必须依赖于B),你试图忽略这一事实(A必须依赖于B,除非没有B)。我将把以这种方式使用外键的技术方面留给专家。但是:我真的不喜欢在logs表中没有用户的情况下使用用户ID的想法——一旦用户被删除,它就会变成垃圾数据。底线-您很少希望完全删除生产应用程序中的数据。
    使用外键约束无法完成此操作。
    -明白了。你让我相信你是对的,我想做的没有意义。我开始向那个方向倾斜,这就是我提出问题的原因,但这是很好的反馈/建议,谢谢。想象一下,就像一个账单表,保存着所有财务记录。它有两个参考:1)一个用户,他改变了ballance;2). 存入客户账户;我想保留所有这些记录,即使我删除了经理的用户帐户或客户帐户。使用日志文件在这里不是一个选项…@pilat:优化您的需求。在英语中,“删除行但保留所有行”的要求是毫无意义的。(英语不是你的第一语言,对吗?)