【问题标题】:Django — improve `startswith` performanceDjango — 提高 `startswith` 的性能
【发布时间】:2018-09-15 21:22:30
【问题描述】:

如何提高以下查询的性能?

class Worker(models.Model):
    name = models.CharField(max_length=32, db_index=True)

# This line is slow:
Worker.objects.filter(name__startswith='John')

我已经为模型添加了一个索引,但是......它根本没有被使用。 然而,当我在没有startswith 的情况下进行普通过滤时,索引确实会启动:

# This line is fast:
Worker.objects.filter(name='John')

为什么startswith 不使用索引?

【问题讨论】:

    标签: python sql django postgresql django-orm


    【解决方案1】:

    问题在于startswith 表达式转换为包含LIKE 运算符的SQL 查询,它没有利用默认索引。

    解决方案:添加一个带有特殊operator class的附加索引:

    CREATE INDEX "appname_model_field_like_idx" 
    ON "appname_model" ("fieldname" varchar_pattern_ops);
    

    一步一步:

    1. 首先,创建一个空迁移:

      python3 manage.py makemigrations appName --empty
      
    2. 添加自定义 RunSQL 命令:

      class Migration(migrations.Migration):
      
          dependencies = [
              ('stats', '0002_auto_2010213_0159.py'),
          ]
      
          operations = [
              migrations.RunSQL(
                  sql=r'''CREATE INDEX "appname_model_field_like_idx" 
                          ON "appname_model" ("fieldname" varchar_pattern_ops);''',
                  reverse_sql=r'DROP INDEX "appname_model_field_like_idx";'
              ),
          ]
      

    【讨论】:

    • reverse_sql 的附加样式点。丢失时总是令人沮丧。
    • 似乎 Django 已经自动添加了这个索引(可能只有当引用字段上已经有索引时 - 我不确定)。
    【解决方案2】:

    如果您的后端是 MySQL,请尝试使用不区分大小写的 istartswith 来使用索引:

    Worker.objects.filter(name__istartswith='John')

    【讨论】:

      猜你喜欢
      • 2012-01-28
      • 2017-11-23
      • 2015-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-03
      • 1970-01-01
      相关资源
      最近更新 更多