Django ORM 使用 'IN' 语句过滤多个字段
Django ORM filter multiple fields using 'IN' statement
所以我在 Django 中有以下模型:
class MemberLoyalty(models.Model):
date_time = models.DateField(primary_key=True)
member = models.ForeignKey(Member, models.DO_NOTHING)
loyalty_value = models.IntegerField()
我的目标是让所有元组按最近日期的成员分组。有很多方法可以做到这一点,其中之一是使用一个子查询,该子查询按最大 date_time 的成员分组,并使用其结果过滤 member_loyalty。此解决方案的工作 sql 如下:
SELECT
*
FROM
member_loyalty
WHERE
(date_time , member_id) IN (SELECT
max(date_time), member_id
FROM
member_loyalty
GROUP BY member_id);
另一种方法是加入子查询。
我如何在 Django 查询中翻译它?我找不到使用 IN 过滤两个字段的方法,也找不到使用特定 ON 语句连接子查询的方法。
我试过:
cls.objects.values('member_id', 'loyalty_value').annotate(latest_date=Max('date_time'))
但它开始按 loyalty_value 分组。
也尝试构建子查询,但找不到如何加入它或在过滤器上使用它:
subquery = cls.objects.values('member_id').annotate(max_date=Max('date_time'))
此外,我正在使用 Mysql,所以我无法使用 .distinct('param') 方法。
这是典型的greatest-per-group query. Stack-overflow even has a tag。
我认为使用最新版本的 Django 最有效的方法是通过 window query。沿线的东西应该可以解决问题。
MemberLoyalty.objects.all().annotate(my_max=Window(
expression=Max('date_time'),
partition_by=F('member')
)).filter(my_max=F('date_time'))
Update:这实际上是行不通的,因为Window
注解不是filterable
。我认为为了过滤 window 注释,你需要将它包装在 Subquery
中,但是对于 Subquery
你实际上没有义务使用 Window
函数,有另一种方法,这是我的下一个例子。
如果 MySQL 或 Django 不支持 window 查询,则 Subquery 发挥作用。
MemberLoyalty.objects.filter(
date_time=Subquery(
(MemberLoyalty.objects
.filter(member=OuterRef('member'))
.values('member')
.annotate(max_date=Max('date_time'))
.values('max_date')[:1]
)
)
)
如果事件 Subqueries
不可用(Django 1.11 之前)那么这也应该有效:
MemberLoyalty.objects.annotate(
max_date=Max('member__memberloyalty_set__date_time')
).filter(max_date=F('date_time'))
所以我在 Django 中有以下模型:
class MemberLoyalty(models.Model):
date_time = models.DateField(primary_key=True)
member = models.ForeignKey(Member, models.DO_NOTHING)
loyalty_value = models.IntegerField()
我的目标是让所有元组按最近日期的成员分组。有很多方法可以做到这一点,其中之一是使用一个子查询,该子查询按最大 date_time 的成员分组,并使用其结果过滤 member_loyalty。此解决方案的工作 sql 如下:
SELECT
*
FROM
member_loyalty
WHERE
(date_time , member_id) IN (SELECT
max(date_time), member_id
FROM
member_loyalty
GROUP BY member_id);
另一种方法是加入子查询。
我如何在 Django 查询中翻译它?我找不到使用 IN 过滤两个字段的方法,也找不到使用特定 ON 语句连接子查询的方法。
我试过:
cls.objects.values('member_id', 'loyalty_value').annotate(latest_date=Max('date_time'))
但它开始按 loyalty_value 分组。
也尝试构建子查询,但找不到如何加入它或在过滤器上使用它:
subquery = cls.objects.values('member_id').annotate(max_date=Max('date_time'))
此外,我正在使用 Mysql,所以我无法使用 .distinct('param') 方法。
这是典型的greatest-per-group query. Stack-overflow even has a tag。
我认为使用最新版本的 Django 最有效的方法是通过 window query。沿线的东西应该可以解决问题。
MemberLoyalty.objects.all().annotate(my_max=Window(
expression=Max('date_time'),
partition_by=F('member')
)).filter(my_max=F('date_time'))
Update:这实际上是行不通的,因为Window
注解不是filterable
。我认为为了过滤 window 注释,你需要将它包装在 Subquery
中,但是对于 Subquery
你实际上没有义务使用 Window
函数,有另一种方法,这是我的下一个例子。
如果 MySQL 或 Django 不支持 window 查询,则 Subquery 发挥作用。
MemberLoyalty.objects.filter(
date_time=Subquery(
(MemberLoyalty.objects
.filter(member=OuterRef('member'))
.values('member')
.annotate(max_date=Max('date_time'))
.values('max_date')[:1]
)
)
)
如果事件 Subqueries
不可用(Django 1.11 之前)那么这也应该有效:
MemberLoyalty.objects.annotate(
max_date=Max('member__memberloyalty_set__date_time')
).filter(max_date=F('date_time'))