如何在 Django 中创建不区分大小写的数据库索引?

Posted

技术标签:

【中文标题】如何在 Django 中创建不区分大小写的数据库索引?【英文标题】:How can I create a case-insensitive database index in Django? 【发布时间】:2013-09-07 00:35:36 【问题描述】:

我正在使用 Django 创建一些数据库表,如下所示:

class MetadataTerms(models.Model):
    term = models.CharField(max_length=200)
    size = models.IntegerField(default=0)
    validity = models.IntegerField(default=0, choices=TERM_VALIDITY_CHOICES)

然后我正在运行查找查询以查找具有正确term 的适当行,以不区分大小写的方式匹配。例如:

MetadataTerms.objects.filter(term__iexact=search_string, size=3)

此查找子句在 SQL 中转换为类似的内容:

 SELECT "app_metadataterms"."id", "app_metadataterms"."term", "app_metadataterms"."size" FROM "app_metadataterms" WHERE (UPPER("app_metadataterms"."term"::text) = UPPER('Jack Nicklaus survives')  AND "app_metadataterms"."size" = 3 );

在 Postgres 上,我可以在上面执行EXPLAIN 查询,我得到这个查询计划:

                                    QUERY PLAN
-----------------------------------------------------------------------------------
 Seq Scan on app_metadataterms  (cost=0.00..1233.01 rows=118 width=21)
   Filter: ((size = 3) AND (upper((term)::text) = 'JACK NICKLAUS SURVIVES'::text))

因为term字段没有被索引,也没有以大小写规范化的方式被索引,所以上面的查询需要对所有数据库行执行一个慢速的Seq[uential] Scan操作。

然后我插入一个简单的大小写标准化索引,例如:

 CREATE INDEX size_term_insisitive_idx ON app_metadataterms (upper(term), size);

上述查询现在的运行速度提高了大约 6 倍:

                                         QUERY PLAN
---------------------------------------------------------------------------------------------
 Bitmap Heap Scan on app_metadataterms  (cost=5.54..265.15 rows=125 width=21)
   Recheck Cond: ((upper((term)::text) = 'JACK NICKLAUS SURVIVES'::text) AND (size = 3))
   ->  Bitmap Index Scan on size_term_insisitive_idx  (cost=0.00..5.51 rows=125 width=0)
         Index Cond: ((upper((term)::text) = 'JACK NICKLAUS SURVIVES'::text) AND (size = 3))

我的问题是:如何将高级数据库索引的创建注入到 Django 模型管理命令中?

【问题讨论】:

另见***.com/questions/44820345/… 【参考方案1】:

Django 1.11(2.0 应该也可以)+ PostgreSQL:

    首先,创建一个空迁移:

    python3 manage.py makemigrations appName --empty
    

    Django 使用UPPER 进行不精确的查找。因此,创建一个迁移以添加 UPPER(yourField) 索引:

    # -*- coding: utf-8 -*-
    # Generated by Django 1.11.7 on 2017-12-14 23:11
    from __future__ import unicode_literals
    
    
    from django.db import migrations
    
    class Migration(migrations.Migration):
    
        dependencies = [
            ('stats', '0027_remove_siteuser_is_admin'),
        ]
    
        operations = [
            migrations.RunSQL(
                sql=r'CREATE INDEX "stats_siteuser_upper_idx" ON "stats_siteuser" (UPPER("email"));',
                reverse_sql=r'DROP INDEX "stats_siteuser_upper_idx";'
            ),
        ]
    

【讨论】:

也许,您需要在创建索引的地方添加 UNIQUE。 对于icontains,您需要一个 GIN trigram 索引:'CREATE INDEX "stats_siteuser_upper_idx" ON "stats_siteuser" USING gin (UPPER("email") gin_trgm_ops);' 您需要 TrigramExtension 来完成这项工作。【参考方案2】:

从3.2 开始,您可以将*expressions 添加到Index

如果你想创建

 CREATE INDEX size_term_insisitive_idx ON app_metadataterms (upper(term), size);

类似的东西应该可以工作。

class MetadataTerms(models.Model):
    term = models.CharField(max_length=200)
    size = models.IntegerField(default=0)
    validity = models.IntegerField(default=0, choices=TERM_VALIDITY_CHOICES)
    
    class Meta:
        indexes = [
            Index(
                Upper('term'), 'size',
                name='size_term_insisitive_idx',
            ),
        ]

【讨论】:

【参考方案3】:

在 Django 1.9(尚未发布)之前,您可以使用 sqlcustom 命令,但如果您查看即将发布的 1.9 的开发文档,您会发现该命令明显缺失。

所以:

在 在 >= 1.9 中,您需要在迁移过程中开始使用新的 RunSQL function。如果您愿意,也可以在 1.7 或 1.8 中执行此操作。

【讨论】:

【参考方案4】:

要将自定义 sql 注入 django 模型管理命令,请查看django-admin.py sqlcustom

你可以在<app_name>/sql/<model_name>.sql中放置一个包含你创建索引的sql文件

从文档中了解它们何时应用:

SQL 文件在所有 模型的建表语句已经执行。使用此 SQL 挂钩以进行任何表修改,或将任何 SQL 函数插入 数据库。

您可以通过运行manage.py sqlcustom <app_name>查看每个应用程序的自定义sql

【讨论】:

以上是关于如何在 Django 中创建不区分大小写的数据库索引?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 laravel 路由中创建不包括某些 slug 的 slug 路由?

如何在Controller中创建不可变的BusinessLayer而不更改其属性?

如何在 Spring Boot 客户端应用程序中创建不共享底层网络连接的套接字

在组中创建不透明数据集

如何使用 Django 模型进行不区分大小写的查询

如何实现 Django 不区分大小写的模型字段?