在我的 Django 应用程序中,我定义了一个模型,如下所示:
class NamedContainer(models.Model):
name = models.CharField(max_length=50)
capacity_ml = models.PositiveIntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['name', 'capacity_ml']
随着时间的推移,该表已经变得足够大,开始导致一些查询性能问题,特别是在依赖限制/偏移量进行切片时。其他切片方法在设计上可能具有更好的性能,但不幸的是,现在我仍然坚持限制/偏移。
然而,MySQL has a technique called "late row lookups"这对我的问题有很大帮助,简而言之,使用这种技术的原始 MySQL 查询看起来像
SELECT t2.* FROM (
SELECT *
FROM `core_namedcontainer`
WHERE `updated_at` >= '2019-01-01 05:00:00.000Z'
ORDER BY id ASC
LIMIT 500 OFFSET 10000
) AS t1
JOIN `core_namedcontainer` AS t2
ON t1.id = t2.id
ORDER BY `name` ASC, `capacity_ml` ASC
我只设法破坏了 ORM 查询以生成如下查询
SELECT *
FROM `core_namedcontainer`
WHERE (
`core_namedcontainer`.`id` IN (
SELECT U0.`id`
FROM `core_namedcontainer` U0
WHERE (
U0.`updated_at` >= 2019-01-01 05:00:00
)
ORDER BY U0.`name` ASC, U0.`capacity_ml` ASC
LIMIT 500 OFFSET 10000
)
)
ORDER BY `core_namedcontainer`.`name` ASC, `core_namedcontainer`.`capacity_mL` ASC
它只是使用子查询而不是自身的连接,并且在评估查询时,MySQL 会提示
NotSupportedError: (1235, "This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'")
django 应用程序使用的是 MySQL 5.6,由于各种原因,它需要很长时间才能升级到任何更新的版本。
我知道我可以按照我需要的方式发出原始 SQL 查询,但我希望有一种方法可以将此技术转换为 Django ORM 语法,以便我可以在我的模型上利用此技术作为基础查询集:
class NamedContainerManager(models.Manager):
def get_queryset(self):
queryset = super().get_queryset()
# do some ORM magic here to implement mysql late row lookup in all queries
return queryset
class NamedContainer(models.Model):
...
objects = NamedContainerManager()
非常感谢所有帮助!
最佳答案
OFFSET
会导致跳过或重复行。当心。
OFFSET
是 O(N*N)。这是因为它必须跨过所有“偏移”行。
更好的解决方案是“记住您离开的地方”而不是使用 OFFSET
。参见 http://mysql.rjweb.org/doc.php/pagination
你说“现在我被限制/偏移所困”。我不接受。在 ORM 中应该可以重新制定以记住您离开的地方。
关于mysql - 如何进行利用 MySQL 后期行查找的 ORM 查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58003070/