Стоит ли указывать UniqueConstraint для двух полей в Django дорого? - PullRequest
0 голосов
/ 17 июня 2020

Согласно документации здесь, Django удобно позволяет вам указать список полей, которые должны быть уникальными вместе.

Я полагаю, что для каждой строки, вставленной в базу данных, Django просматривает всю таблицу, чтобы проверить ограничение. В таблице с 1 млн + строк я могу представить, что это кошмар.

Действительно ли так работает UniqueConstraint? Неужели это так дорого?

1 Ответ

1 голос
/ 17 июня 2020

Краткий ответ : большинство баз данных создают индексы, чтобы сделать проверку ограничений уникальности эффективной.

Запрос для его создания создаст индекс на стороне базы данных. Действительно, если вы создадите UniqueConstraint [GitHub] , тогда он вызовет метод create_sql [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.<b>_create_unique_sql(</b>
            model, fields, self.name, condition=condition,
            deferrable=self.deferrable,
        <b>)</b>

Эта _create_unique_sql функция [GitHub] будет, тогда также проверьте, нужно ли создавать индекс, учитывая, что база данных поддерживает функцию supports_partial_indexes:

    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.<b>features.supports_partial_indexes</b>:
                return None
            <b>sql = self.sql_create_unique_index</b>
        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` <b>UNIQUE</b> (`i`, `j`);

Но, как мы видим в документации MySQL по Создание ключей индексов таблиц , это также создаст индекс базы данных. Если мы, например, проверим индексы, мы увидим:

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      |         |               |
+--------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
...