Django startswith 与 endswith 性能对比 MySQL
Django startswith vs endswith performance on MySQL
假设我有以下模型
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,如果 %
出现在末尾,则只能使用主键索引(例如,与完整 table 扫描相反) LIKE
查询。
这是否意味着,随着数据库的增长,startswith
将可行 - 而 endswith
将不可行,因为它将求助于完整的 table 扫描?
我是正确的还是哪里出错了?请记住,这些不是事实,而只是我从一般假设中得出的推论——因此我要求确认。
是的,你的理解是正确的。
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。但是,如果您还没有遇到性能问题,那么优化它还为时过早,因为您无法真正测试获得的性能量。
假设您想要 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 的功能。)
假设我有以下模型
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,如果 %
出现在末尾,则只能使用主键索引(例如,与完整 table 扫描相反) LIKE
查询。
这是否意味着,随着数据库的增长,startswith
将可行 - 而 endswith
将不可行,因为它将求助于完整的 table 扫描?
我是正确的还是哪里出错了?请记住,这些不是事实,而只是我从一般假设中得出的推论——因此我要求确认。
是的,你的理解是正确的。
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。但是,如果您还没有遇到性能问题,那么优化它还为时过早,因为您无法真正测试获得的性能量。
假设您想要 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 的功能。)