【问题标题】:Is specifying a UniqueConstraint on two fields expensive in Django?在 Django 中的两个字段上指定 UniqueConstraint 是否很昂贵?
【发布时间】:2020-10-07 18:47:13
【问题描述】:

根据documentations here,,Django 允许您方便地指定必须是唯一的字段列表。

我想对于数据库中插入的每一行,Django 都会扫描整个表以验证约束。在一个超过 100 万行的表中,我可以想象这是一场噩梦。

这确实是 UniqueConstraint 的运作方式吗? 真的这么贵吗?

【问题讨论】:

  • 通常会创建一个索引。

标签: mysql django django-models django-rest-framework


【解决方案1】:

简答:大多数数据库都制作索引以提高唯一性约束的检查。

构造它的查询将在数据库端创建一个索引。事实上,如果你创建一个UniqueConstraint [GitHub],那么它会调用使用create_sql method [GitHub] 来生成SQL 查询来创建这个约束:

    def create_sql(self, model, schema_editor):
        fields = [model._meta.get_field(field_name).column for field_name in self.fields]
        condition = self._get_condition_sql(model, schema_editor)
        return schema_editor._create_unique_sql(
            model, fields, self.name, condition=condition,
            deferrable=self.deferrable,
        )

如果数据库支持supports_partial_indexes 功能,则此_create_unique_sql function [GitHub] 将检查它是否必须建立索引:

    def _create_unique_sql(self, model, columns, name=None, condition=None, deferrable=None):
        if (
            deferrable and
            not self.connection.features.supports_deferrable_unique_constraints
        ):
            return None

        def create_unique_name(*args, **kwargs):
            return self.quote_name(self._create_index_name(*args, **kwargs))

        table = Table(model._meta.db_table, self.quote_name)
        if name is None:
            name = IndexName(model._meta.db_table, columns, '_uniq', create_unique_name)
        else:
            name = self.quote_name(name)
        columns = Columns(table, columns, self.quote_name)
        if condition:
            if not self.connection.features.supports_partial_indexes:
                return None
            sql = self.sql_create_unique_index
        else:
            sql = self.sql_create_unique
        return Statement(
            sql,
            table=table,
            name=name,
            columns=columns,
            condition=self._index_condition_sql(condition),
            deferrable=self._deferrable_constraint_sql(deferrable),
        )

这个sql_create_unique_index [GitHub] 的值为:

sql_create_unique_index = "CREATE UNIQUE INDEX %(name)s ON %(table)s (%(columns)s)%(condition)s"

因此,这将创建一个索引,并加快查找速度。

然而,并非所有数据库都需要明确创建索引。例如,对于 MySQL,它将创建一个如下所示的查询:

--
-- Create constraint ij_unique on model foo
--
ALTER TABLE `app_name_foo` ADD CONSTRAINT `ij_unique` UNIQUE (`i`, `j`);

但是正如我们在MySQL documentation on Creating table indexes keys 中看到的,这也会创建一个数据库索引。例如,如果我们检查索引,我们会看到:

mysql> show index from app_name_foo;
+--------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table        | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| app_name_foo |          0 | PRIMARY   |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| app_name_foo |          0 | ij_unique |            1 | i           | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| app_name_foo |          0 | ij_unique |            2 | j           | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+--------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-27
    • 1970-01-01
    • 2017-10-12
    • 1970-01-01
    • 2012-03-23
    • 1970-01-01
    • 2012-12-24
    相关资源
    最近更新 更多