如何在 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而不更改其属性?