假设我有三个型号如下,代表同一公司的几个零售点销售的商品的价格:
class Store(models.Model):
name = models.CharField(max_length=256)
address = models.TextField()
class Product(models.Model):
name = models.CharField(max_length=256)
description = models.TextField()
class Price(models.Model):
store = models.ForeignKey(Store)
product = models.ForeignKey(Product)
effective_date = models.DateField()
value = models.FloatField()
设置价格时,会根据商店和产品特定设置价格。即同一商品在不同的商店可以有不同的价格。这些价格中的每一个都有生效日期。对于给定的store
和给定的product
,当前有效的价格是具有最新effective_date
的价格。
编写查询的最佳方式是什么?它将返回所有商店中所有商品的当前有效价格?
如果我使用Pandas,我会给自己一个带有['store', 'product', 'effective_date', 'price']
列的数据框,然后我会运行
dataframe\
.sort_values(columns=['store', 'product', 'effective_date'], ascending=[True, True, False])\
.groupby('store', 'product')['price'].first()
但必须有一些方法直接在数据库级别上执行此操作。想法?
答案 0 :(得分:2)
如果您的DBMS是PostgreSQL,您可以通过以下方式使用 distinct 与 order_by 结合使用:
Price.objects.order_by('store','product','-effective_date').distinct('store','product')
它将为您提供所有产品/商店组合的所有最新价格。
有关 distinct 的技巧,请查看此处的文档:https://docs.djangoproject.com/en/1.9/ref/models/querysets/#django.db.models.query.QuerySet.distinct
答案 1 :(得分:1)
如果您使用的是PostgreSQL,可以使用order_by
和distinct
获取所有商店中所有产品的当前有效价格,如下所示:
prices = Price.objects.order_by('store', 'product', '-effective_date')
.distinct('store', 'product')
现在,这与你Pandas
所拥有的非常类似。
请注意,使用distinct
中的字段名称仅适用于PostgreSQL。根据{{1}},store
和降序product
对价格进行排序后,effective date
将仅保留每个商店 - 产品对的第一个条目,这将是您最近的价格当前条目。
不是PostgreSQL数据库:
如果您不使用PostgreSQL,可以使用两个查询来执行此操作:
首先,我们获取所有distinct('store', 'product')
群组的最新生效日期:
store-product
一旦我们有这些日期,我们就可以得到这个日期的价格:
latest_effective_dates = Price.objects.values('store_id', 'product_id')
.annotate(led=Max('effective_date')).values('led')
免责声明:这假设任何prices = Price.objects.filter(effective_date__in=latest_effective_dates)
组的effective_date
都不相同。
答案 2 :(得分:1)
如果没有Postgres增加的功能(你应该真正使用它),有一个更复杂的解决方案(基于ryanpitts' idea),这需要两个db命中:
latest_set = Price.objects
.values('store_id', 'product_id') # important to have values before annotate ...
.annotate(max_date=Max('effective_date')).order_by()
# ... to annotate for the grouping that results from values
# Build a query that reverse-engineers the Price records that contributed to
# 'latest_set'. (Relying on the fact that there are not 2 Prices
# for the same product-store with an identical date)
q_statement = Q(product_id=-1) # sth. that results in empty qs
for latest_dict in latest_set:
q_statement |=
(Q(product_id=latest_dict['product_id']) &
Q(store_id=latest_dict['store_id']) &
Q(effective_date=latest_dict['max_date']))
Price.objects.filter(q_statement)