假设我有以下型号
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
将不会,因为它将采用全表扫描?
我是正确的还是我在某处出错了?请记住,这些不是事实,而只是我从一般假设中得出的推论 - 因此我要求确认。
答案 0 :(得分:3)
假设您想要 AND
- 仅Alex White
而非Alex Chen
或Tony 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。但是,如果您还没有遇到性能问题,那么优化它还为时过早,因为您无法真正测试所获得的性能。