【问题标题】:Why MySQL creating indexes for Foreign key during table updation为什么 MySQL 在表更新期间为外键创建索引
【发布时间】:2020-04-16 20:05:34
【问题描述】:

在文档中给出,当你使用 alter table 命令添加外键时,MySQL 不会为外键创建索引,你需要手动创建它。

https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html

外键可以是自引用的(引用同一个表)。当您使用 ALTER TABLE 向表添加外键约束时,请记住首先创建所需的索引。

但是我使用 alter table 命令在表中添加了一个外键,它正在创建索引和约束。

ALTER TABLE `pharmaceuticals_dosage` ADD CONSTRAINT `pharmaceuticals_dosa_as_needed_reason_cod_2ac978bf_fk_human_api` FOREIGN KEY (`as_needed_reason_code_id`) REFERENCES `human_api_code` (`id`);

我认为MySQL官方文档应该没有错,那么它创建索引的原因是什么?

PS:上面的查询是由Django迁移创建的

【问题讨论】:

  • 如果不存在引用字段为前缀的另一个索引,则 MySQL 在子项中添加索引。 MySQL 需要外键和引用键上的索引,以便外键检查可以快速且不需要表扫描。在引用表中,必须有一个索引,其中外键列按相同顺序列为第一列。如果引用表不存在,则自动在引用表上创建这样的索引但它没有在父表中创建索引,即使它是自引用FK。

标签: mysql django django-orm


【解决方案1】:

您参考的文档是正确的,但不够清楚。

假设您有一张员工表。您希望每一行都有一个列manager_id,它是一个外键,引用另一行是员工的经理。但是由于某种原因,您没有使用employee_id 作为表的主键——您还有另一个主键。

mysql> create table employees (
  id serial primary key, 
  employee_id int, 
  manager_id int
);

您想在manager_id 上添加一个外键,引用employee_id

mysql> alter table employees add foreign key (manager_id) references employees(employee_id);
ERROR 1215 (HY000): Cannot add foreign key constraint

怎么了? employee_id 上没有索引,但外键只能引用具有索引的列。如果我们在innodb状态中获得有关外键错误的详细信息,这会更清楚一点:

mysql> show engine innodb status\G
...
------------------------
LATEST FOREIGN KEY ERROR
------------------------
2019-12-26 10:25:52 0x700005bfb000 Error in foreign key constraint of table test/#sql-159d_8c9:
foreign key (manager_id) references employees(employee_id):
Cannot find an index in the referenced table where the
referenced columns appear as the first columns...
...

你需要先在employee_id上创建一个索引:

mysql> alter table employees add unique key (employee_id);
Query OK, 0 rows affected (0.01 sec)

mysql> alter table employees add foreign key (manager_id) references employees(employee_id);
Query OK, 0 rows affected (0.02 sec)

所以文档说“记得先创建所需的索引”是正确的,但不清楚所需的索引是否在 referenced 列 (employee_id) 上。您假设文档正在讨论外键列 (manager_id) 上的必需索引。

实际上,创建外键会自动在外键列上创建索引,除非该列上的索引已经存在。

【讨论】:

【解决方案2】:

由于您使用 django 创建了外键,它会自动创建数据库索引。 ForeignKey 的文档中提到了它。 来自文档:

数据库索引是在 ForeignKey 上自动创建的

您可以通过将db_index=False 添加到该字段来禁用外键。 例如。

class SomeModel(models.Model):
    refrence_to_table = models.ForeignKey(SomeTable, db_index=False)
    # other fields

附加文档中的文本:

在 ForeignKey 上自动创建一个数据库索引。你可以 通过将 db_index 设置为 False 来禁用它。你可能想避免 如果您正在创建外键以保持一致性,则索引的开销 而不是连接,或者如果您将创建一个替代索引 比如部分或多列索引。

我猜 MySQL 文档指的是当您直接创建外键(从 shell)到其他表时。这里Django会在创建的时候生成一个自定义的sql。

您可以查看Django在不同DB操作中使用的Schema

【讨论】:

  • 我检查了 Django 使用 sqlmigrate 生成的查询,它只创建了 Alter table add foreign key 查询。您能否再详细说明一下,我可以通过它来推断创建 Django 生成的索引的查询。
猜你喜欢
  • 2020-04-16
  • 2021-11-18
  • 1970-01-01
  • 2011-08-22
  • 2017-04-27
  • 1970-01-01
  • 2017-04-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多