是否可以创建 Django 查询集来执行此复杂查询?
Can a Django Queryset be created to do this complex query?
假设我有以下 Django class:
class MyModel(models.Model):
a = models.IntegerField()
created_ts = models.DateTimeField(default=datetime.utcnow, editable=False)
def __str__(self):
return "<%s %s>" % (
self.__class__.__name__,
"; ".join(
[
"ID: %s" % self.pk,
"a: %s" % self.a,
"created_ts: %s" % self.created_ts,
]
)
)
我想为 a
的每个不同值找到具有最新 created_ts
的 MyModel
实例。我可以使用单个 QuerySet 这样做吗?如果是这样,那个 QuerySet 是什么?如果不是,获得该结果的最有效方法是什么?
最后,我想要Integer/MyModel-Instance对。答案应该大致如下所示:
{
1: <MyModel ID: 1; a: 1; created_ts: 2004-11-08 06:01:00>,
5: <MyModel ID: 2; a: 5; created_ts: 2004-11-05 08:01:32>,
3: <MyModel ID: 3; a: 3; created_ts: 2004-11-04 11:01:42>,
0: <MyModel ID: 4; a: 0; created_ts: 2004-11-03 06:12:10>,
}
不确定如何使用单个查询集执行此操作,但如果您愿意以不使用 raw 为代价执行更多查询,则可以执行此操作
from django.db.models import Max
max_ts_queryset = MyModel.objects.values('a').order_by('a').annotate(max_ts=Max('created_ts'))
获取每个 a 值的最大 ts,然后用
之类的东西循环它
final_list = {}
for obj in max_ts_queryset:
final_list[obj['a']] = MyModel.objects.get(a=obj['a'], created_ts=obj['max_ts']
return final_list
不是最直接的答案,但希望对您有所帮助:
那你可以用raw queries
MyModel.objects.raw("SELECT * FROM mymodel main INNER JOIN (SELECT max(created_ts) as max_ts, a FROM mymodel GROUP BY a) sub on main.a = sub.a AND main.created_ts = sub.max_ts")
我自己没有运行查询,所以它可能有语法错误,但你明白了。
如果您使用的是 Postgres,这将有效。如果您使用的是 MySQL 或 sqlite,则无法使用。
MyModel.objects.order_by('a', '-created_ts').distinct('a')
编辑: 哎呀刚刚看到你在 MySQL.
这对你有帮助
from django.db.models import Count, Max
MyModel.objects.values('a').annotate(count=Count("a"),latest_date=Max('created_ts'))
数据在table
a created_ts
- -----------
1 2015-09-08 20:05:51.144321+00:00
1 2015-09-08 20:08:40.687936+00:00
3 2015-09-08 20:08:58.472077+00:00
2 2015-09-08 20:09:08.493748+00:00
2 2015-09-08 20:10:20.906069+00:00
输出
[
{'count': 2, 'latest_date': datetime.datetime(2015, 9, 8, 20, 8, 40, 687936, tzinfo=<UTC>), 'a': 1},
{'count': 2, 'latest_date': datetime.datetime(2015, 9, 8, 20, 10, 20, 906069, tzinfo=<UTC>), 'a': 2},
{'count': 1, 'latest_date': datetime.datetime(2015, 9, 8, 20, 8, 58, 472077, tzinfo=<UTC>), 'a': 3}
]
从 https://docs.djangoproject.com/en/1.8/ref/models/querysets/#queryset-api 开始,由于查询集的结果是一个查询集,您应该能够像这样链接:
MyModel.objects.order_by('created_ts').distinct('a')
您的问题与您之前的问题几乎完全相同:How to make Django Queryset that selects records with max value within a group
因此,我将使用我以前 proposed solution 的一部分来回答:
MyClass.objects.filter(
created_ts__in=MyClass.objects.values(
"a"
).annotate(
created_ts=models.Max(
"created_ts"
)
).values_list("created_ts", flat=True)
)
请注意,这只需要一个 SQL 请求,正如您在查询前后打印 len(django.db.connection.queries) 所见。
但是,请注意,后一种解决方案仅在您的 created_ts 属性保证唯一时才有效,这可能不是您的情况。
假设我有以下 Django class:
class MyModel(models.Model):
a = models.IntegerField()
created_ts = models.DateTimeField(default=datetime.utcnow, editable=False)
def __str__(self):
return "<%s %s>" % (
self.__class__.__name__,
"; ".join(
[
"ID: %s" % self.pk,
"a: %s" % self.a,
"created_ts: %s" % self.created_ts,
]
)
)
我想为 a
的每个不同值找到具有最新 created_ts
的 MyModel
实例。我可以使用单个 QuerySet 这样做吗?如果是这样,那个 QuerySet 是什么?如果不是,获得该结果的最有效方法是什么?
最后,我想要Integer/MyModel-Instance对。答案应该大致如下所示:
{
1: <MyModel ID: 1; a: 1; created_ts: 2004-11-08 06:01:00>,
5: <MyModel ID: 2; a: 5; created_ts: 2004-11-05 08:01:32>,
3: <MyModel ID: 3; a: 3; created_ts: 2004-11-04 11:01:42>,
0: <MyModel ID: 4; a: 0; created_ts: 2004-11-03 06:12:10>,
}
不确定如何使用单个查询集执行此操作,但如果您愿意以不使用 raw 为代价执行更多查询,则可以执行此操作
from django.db.models import Max
max_ts_queryset = MyModel.objects.values('a').order_by('a').annotate(max_ts=Max('created_ts'))
获取每个 a 值的最大 ts,然后用
之类的东西循环它final_list = {}
for obj in max_ts_queryset:
final_list[obj['a']] = MyModel.objects.get(a=obj['a'], created_ts=obj['max_ts']
return final_list
不是最直接的答案,但希望对您有所帮助:
那你可以用raw queries
MyModel.objects.raw("SELECT * FROM mymodel main INNER JOIN (SELECT max(created_ts) as max_ts, a FROM mymodel GROUP BY a) sub on main.a = sub.a AND main.created_ts = sub.max_ts")
我自己没有运行查询,所以它可能有语法错误,但你明白了。
如果您使用的是 Postgres,这将有效。如果您使用的是 MySQL 或 sqlite,则无法使用。
MyModel.objects.order_by('a', '-created_ts').distinct('a')
编辑: 哎呀刚刚看到你在 MySQL.
这对你有帮助
from django.db.models import Count, Max
MyModel.objects.values('a').annotate(count=Count("a"),latest_date=Max('created_ts'))
数据在table
a created_ts
- -----------
1 2015-09-08 20:05:51.144321+00:00
1 2015-09-08 20:08:40.687936+00:00
3 2015-09-08 20:08:58.472077+00:00
2 2015-09-08 20:09:08.493748+00:00
2 2015-09-08 20:10:20.906069+00:00
输出
[
{'count': 2, 'latest_date': datetime.datetime(2015, 9, 8, 20, 8, 40, 687936, tzinfo=<UTC>), 'a': 1},
{'count': 2, 'latest_date': datetime.datetime(2015, 9, 8, 20, 10, 20, 906069, tzinfo=<UTC>), 'a': 2},
{'count': 1, 'latest_date': datetime.datetime(2015, 9, 8, 20, 8, 58, 472077, tzinfo=<UTC>), 'a': 3}
]
从 https://docs.djangoproject.com/en/1.8/ref/models/querysets/#queryset-api 开始,由于查询集的结果是一个查询集,您应该能够像这样链接:
MyModel.objects.order_by('created_ts').distinct('a')
您的问题与您之前的问题几乎完全相同:How to make Django Queryset that selects records with max value within a group
因此,我将使用我以前 proposed solution 的一部分来回答:
MyClass.objects.filter(
created_ts__in=MyClass.objects.values(
"a"
).annotate(
created_ts=models.Max(
"created_ts"
)
).values_list("created_ts", flat=True)
)
请注意,这只需要一个 SQL 请求,正如您在查询前后打印 len(django.db.connection.queries) 所见。
但是,请注意,后一种解决方案仅在您的 created_ts 属性保证唯一时才有效,这可能不是您的情况。