【问题标题】:Necessity of foreign key in this case(innoDB/mysql)这种情况下外键的必要性(innoDB/mysql)
【发布时间】:2009-11-27 16:27:33
【问题描述】:

我最近将我的整个数据库从 myisam 迁移到了 innodb。我对这一切都很陌生,所以我对外键的使用有疑问。

假设我有两个表:用户、消息。

用户

id (pr_key)
name

消息

id (pr_key)
user_id
message

id 字段都是自动递增的。

所以现在在我的查询中,我已经加入了这两个表。是否还需要在这里放置一个外键,我实际上没有看到一点。它是否具有性能优势。

如果我选择在此处放置外键,我假设我必须将消息的 pr_key 设置为 id 和 user_id。

添加其他 prim_key 不仅会占用更多空间,还会导致性能下降。

现在,如果表有两个 pr_key,而我只查询其中一个,我是否仍能获得相同的性能优势。还是我需要明确使用这两个键。

我知道在这个例子中我将在 user_id 上搜索,所以无论如何索引它可能是明智的。但是,如果该字段是没有搜索的字段怎么办。在这个字段上放置多个主键是否仍然很好,只是为了外来关系。

谢谢!

【问题讨论】:

    标签: performance foreign-keys primary-key innodb


    【解决方案1】:

    它具有数据完整性优点。

    当用户表中没有 id = 1234 的用户时,它将阻止应用程序代码在消息表中输入 1234 的 user_id。当用户在消息表中有任何消息时,它将阻止应用程序删除用户。

    PK 的名称不必与引用子表中的 FK 的名称匹配。

    “如果我选择在此处放置外键,我假设我必须同时将消息的 pr_key 设置为 id 和 user_id。”

    没有。您无需更改消息表中的键,只需在消息表中的 User_Id 列上建立外键 (FK) 约束,该约束引用(指向)用户表上的 id 列。消息中不需要额外的 PK,并且由于 PK 已经是唯一的(您不能有两条具有相同 messageId 的消息),因此 id 和 user_id 上的 PK 不能再添加任何唯一性。)

    这里的底线是,您可能对主键和外键实际上是什么有一个基本的误解。

    • PK 是对行唯一性的约束,它需要一个索引。
    • FK 是对列值的约束(它要求该值作为 PK 值存在于其他一些引用表中)它确实需要索引。

    @Noah,看看这个Other SO question

    【讨论】:

    • 绝对! FK 不必是唯一的。 FK 列引用或指向的另一个表中的 PK 列 - 必须是唯一的(当然是 PK),但 FK 不必是唯一的。
    【解决方案2】:

    无论你是否在消息(user_id)上建立正式的 FOREIGN KEY 规则,简单的事实是这是一个外键关系。

    首先,请记住,表中正确的主键应该足以唯一标识表中的每条记录。考虑到每条消息都已经带有 id 字段,您不需要也不想将 user_id 包装到消息表的主键中。

    其次,正如 Charles 已经说过的,在消息 (user_id) 上声明外键,REFERENCES users(id) 将允许您确保数据的完整性。使用外键约束,您可以指定当删除用户表中的一条记录时要执行的操作,该记录在消息表中具有相应的记录。您的选择是 ON DELETE CASCADE(删除此用户记录的所有子记录)、ON DELETE RESTRICT(不允许删除在 messages 表中有记录的用户)、ON DELETE NO ACTION(忽略删除操作) , ON DELETE SET NULL(保留子记录但将 user_id 字段设置为 NULL)。

    根据具体情况,这些选项中的每一个都是合适的,但这里最重要的是,您可以防止从子表到父表的意外空指针。

    第三,建立 FOREIGN KEY 关系将提供性能提升,因为这将在 messages(user_id) 上生成与 users 表上的 PRIMARY KEY 对应的索引。在执行连接这些字段的任何查询时,您将看到与未建立 FOREIGN KEY 相比,必须查询以返回子记录的记录数大大减少。

    @Charles Bretana - 您链接到的问题特定于 Microsoft SQL Server。这里的问题是关于 MySQL / InnoDB。

    我建议您查看InnoDB Foreign Key Constraints 的文档。

    InnoDB 需要外部索引 键和引用的键,以便 外键检查可以快速而不是 需要表扫描。在里面 引用表,必须有一个 外键列的索引 被列为第一列 相同的顺序。创建了这样的索引 自动在参考表上 如果它不存在。 (这是在 与一些旧版本相比,在 必须创建哪些索引 明确或创建外国 关键约束将失败。) index_name,如果给定,用作 前面已经介绍过了。

    【讨论】:

    • 您好,谢谢您的回答。我现在更好地理解了外键的必要性。我想知道一件事,您说:您不需要也不特别想将 user_id 包装到主键中。我尝试在没有 user_id 的情况下向这两个字段添加一个外键,但它返回了一个错误,指出该字段必须是唯一的。在这种情况下,该字段不是唯一的,用户可以拥有多条消息。
    • @Noah,E 建立 FK 不会在 FK 列上创建索引。如果需要,必须将此类索引显式创建为单独的操作。
    • @Saif,您希望消息中的 FK 仅位于 user_id 列上。不在 id 和 user_id 列上。 FK 的作用是防止您在消息表中输入错误的(不存在的)User_Ids。
    • @Charles Bretana - 检查 InnoDb 外键 (dev.mysql.com/doc/refman/5.1/en/…) 的文档。索引是必需的,自 Mysql 5.0 起将自动创建
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-18
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-19
    • 2018-06-24
    相关资源
    最近更新 更多