Django始于vs在MySQL上的表现

时间:2016-11-27 10:13:11

标签: mysql django indexing django-models django-queryset

假设我有以下型号

class Person(models.Model):
    name = models.CharField(max_length=20, primary_key=True)

所以我会在数据库中拥有像

这样的对象
Person.objects.create(name='alex white')
Person.objects.create(name='alex chen')
Person.objects.create(name='tony white')

然后我可以通过执行以下操作查询名字为alex或姓氏为white的所有用户

all_alex = Person.objects.filter(name__startswith='alex')
all_white = Person.objects.filter(name__endswith='white')

我不知道Django是如何在幕后实现的,但我猜它是用SQL LIKE 'alex%'LIKE '%white'

但是,因为根据MySQL index documentation,因为只有%出现在LIKE的末尾才能使用主键索引(例如,与全表扫描相反)查询。

这是否意味着,随着数据库的增长,startswith将可行 - 而endswith将不会,因为它将采用全表扫描?

我是正确的还是我在某处出错了?请记住,这些不是事实,而只是我从一般假设中得出的推论 - 因此我要求确认。

2 个答案:

答案 0 :(得分:3)

假设您想要 AND - 仅Alex White而非Alex ChenTony White,...

更好(假设有一个以name开头的索引)是

SELECT ...
    WHERE name LIKE 'Alex%White'

如果Django无法生成,那么它就会妨碍MySQL的有效使用。

此构造将扫描以alex开头的所有名称,并进一步过滤表达式的其余部分。

如果您确实需要 OR(和3个名字),那么您将无法使用

SELECT ...
    WHERE ( name LIKE 'Alex%'
         OR name LIKE '%White' )

除了扫描所有names之外别无选择。

在某些情况下,也许这一个, FULLTEXT 会更好:

FULLTEXT(name)  -- This index is needed for the following:

SELECT ...
    WHERE MATCH(name) AGAINST('Alex White' IN BOOLEAN MODE)  -- for OR

SELECT ...
    WHERE MATCH(name) AGAINST('+Alex +White' IN BOOLEAN MODE)  -- for AND

(同样,我不知道Django的功能。)

答案 1 :(得分:1)

是的,您的理解是正确的。

select *
from foo
where bar like 'text1%' and bar like '%text2'

不一定是最佳的。这可能是一种改进:

select *
from (select *
      from foo
      where foo.bar like 'text1%') t
where t.bar like '%text2'

您需要进行测量以检查这是否更好。如果是,原因是在内部查询中使用索引,而在外部查询中您不使用索引,但是该集由第一个查询预先过滤,因此您有一个小得多的查询集。

我根本不是Django专家,所以我的回答可能是错误的,但我相信如果filter实际执行查询,将filter链接起来会很有帮助。如果是这种情况,那么您可以使用上述优化。如果filter只准备一个查询并且链接过滤器将导致单个查询与上面的查询不同,那么我建议使用手写的MySQL。但是,如果您还没有遇到性能问题,那么优化它还为时过早,因为您无法真正测试所获得的性能。