我有这个模型。
class Item(models.Model):
name=models.CharField(max_length=128)
Item
多次转移。转移可以成功与否。
class TransferLog(models.Model):
item=models.ForeignKey(Item)
timestamp=models.DateTimeField()
success=models.BooleanField(default=False)
如何查询最新 Items
成功的所有TransferLog
?
使用"最新"我的意思是按timestamp
排序。
TransferLog表
这是一个数据样本。此处item1
不应包括在内,因为上次转帐不成功:
ID|item_id|timestamp |success
---------------------------------------
1 | item1 |2014-11-15 12:00:00 | False
2 | item1 |2014-11-15 14:00:00 | True
3 | item1 |2014-11-15 16:00:00 | False
我知道如何用python中的循环解决这个问题,但我想在数据库中进行查询。
答案 0 :(得分:4)
如果日志中的时间戳增加,那么有效的技巧就是,即传输结束被记录为时间戳(不是转移的开始),或者如果你可以预期的那么转移已经在新的开始之前结束。您可以使用TransferLog
最高id
而不是最高时间戳的对象。
from django.db.models import Max
qs = TransferLog.objects.filter(id__in=TransferLog.objects.values('item')
.annotate(max_id=Max('id')).values('max_id'), success=True)
它在子查询中按item_id
创建组,并将每个组的最高id
发送到主查询,在该查询中,它按组中最新行的success
进行过滤。
您可以看到它被Django直接编译为最佳可能的一个查询。
验证了如何编译到SQL:print(qs.query.get_compiler('default').as_sql())
SELECT L.id, L.item_id, L.timestamp, L.success FROM app_transferlog L
WHERE L.success = true AND L.id IN
( SELECT MAX(U0.id) AS max_id FROM app_transferlog U0 GROUP BY U0.item_id )
(我编辑了示例结果编译SQL,以便通过短别名替换许多 "app_transferlog"."
字段"
来提高可读性 {{1} } field,通过将 L.
参数直接替换为SQL并编辑空格和括号。)
可以通过添加一些示例过滤器并在同一查询中选择相关的True
来改进:
Item
验证了如何编译到SQL:kwargs = {} # e.g. filter: kwargs = {'timestamp__gte': ..., 'timestamp__lt':...}
qs = TransferLog.objects.filter(
id__in=TransferLog.objects.filter(**kwargs).values('item')
.annotate(max_id=Max('id')).values('max_id'),
success=True).select_related('item')
print(qs.query.get_compiler('default').as_sql()[0])
最新的TransferLog和相关项目的有用字段由一个查询获取:
SELECT L.id, L.item_id, L.timestamp, L.success, I.id, I.name
FROM app_transferlog L INNER JOIN app_item I ON ( L.item_id = I.id )
WHERE L.success = %s AND L.id IN
( SELECT MAX(U0.id) AS max_id FROM app_transferlog U0
WHERE U0.timestamp >= %s AND U0.timestamp < %s
GROUP BY U0.item_id )
print(qs.query.get_compiler('default').as_sql()[1])
# result
(True, <timestamp_start>, <timestamp_end>)
可以根据情况更加优化查询,例如如果您对时间戳不感兴趣,那么您需要使用大数据...
如果没有增加时间戳的假设,它实际上比普通的Django ORM更复杂。我的解决方案可以找到here。
接受后编辑:
两个查询可以为非增量数据集提供精确解决方案:
for logitem in qs:
item = logitem.item # the item is still cached in the logitem
...
上次失败的转移。 (使用失败列表,因为它比成功转移列表小得多。)这种方式可以有效地模拟查询,否则需要自定义SQL:
id
我正在考虑使用PostgresQL实现聚合函数(例如SELECT a_boolean_field_or_expression,
rank() OVER (PARTITION BY parent_id ORDER BY the_maximized_field DESC)
FROM ...
WHERE rank = 1 GROUP BY parent_object_id
)作为Django的扩展,它是如何有用的。
答案 1 :(得分:2)
试试这个
# your query
items_with_good_translogs = Item.objects.filter(id__in=
(x.item.id for x in TransferLog.objects.filter(success=True))
因为您说过&#34;如何查询所有项目哪个最新的TransferLog成功?&#34;,如果您使用{{1}启动查询,则在逻辑上很容易理解模型。
我使用的Q Object在这样的地方非常有用。 (否定,或......)
Item
提供(x.item.id for x in TransferLog.objects.filter(success=True)
的列表TransferLog
为真。
答案 2 :(得分:0)
您可能会更容易从ItemLog接近这个:
dataset = ItemLog.objects.order_by('item','-timestamp').distinct('item')
可悲的是,没有清除False项目,我找不到一种方法来应用不同的过滤器。但是你可以用python listcomprehension过滤掉它:
dataset = [d.item for d in dataset if d.success]
如果您在给定的时间段内为日志文件执行此操作,则最好在排序和区分之前对其进行过滤:
dataset = ItemLog.objects.filter(
timestamp__gt=start,
timestamp__lt=end
).order_by(
'item','-timestamp'
).distinct('item')
答案 3 :(得分:0)
如果您可以修改模型,我实际上认为您可以更轻松地使用ManyToMany关系而不是显式的ForeignKey - Django有一些内置的便捷方法,可以使您的查询更容易。 ManyToMany上的文档为here。我建议使用以下模型:
class TransferLog(models.Model):
item=models.ManyToManyField(Item)
timestamp=models.DateTimeField()
success=models.BooleanField(default=False)
然后你可以做(我知道,不是一个很好的,单行代码,但我想明确更清楚):
results = []
for item in Item.objects.all():
if item.transferlog__set.all().order_by('-timestamp')[0].success:
results.append(item)
然后您的结果数组将包含其最新传输成功的所有项目。我知道,它仍然是Python中的一个循环......但也许是一个更清晰的循环。