我想获得每个客户的最新购买清单,按日期排序。
以下查询执行我想要的日期除外:
(Purchase.objects
.all()
.distinct('customer')
.order_by('customer', '-date'))
它会生成如下查询:
SELECT DISTINCT ON
"shop_purchase.customer_id"
"shop_purchase.id"
"shop_purchase.date"
FROM "shop_purchase"
ORDER BY "shop_purchase.customer_id" ASC,
"shop_purchase.date" DESC;
由于customer_id
,我被迫使用ORDER BY
作为第一个DISTINCT ON
表达式。
我想按日期排序,所以我真正需要的查询应该是这样的:
SELECT * FROM (
SELECT DISTINCT ON
"shop_purchase.customer_id"
"shop_purchase.id"
"shop_purchase.date"
FROM "shop_purchase"
ORDER BY "shop_purchase.customer_id" ASC,
"shop_purchase.date" DESC;
)
AS result
ORDER BY date DESC;
我不想使用python排序,因为我仍然需要页面限制查询。数据库中可能有数万行。
实际上它现在在python中排序并导致非常长的页面加载时间,所以这就是我试图解决这个问题的原因。
基本上我想要这样的东西https://stackoverflow.com/a/9796104/242969。是否可以用django查询集来表达它而不是编写原始SQL?
实际的模型和方法有几页长,但这里是上面查询集所需的模型集。
class Customer(models.Model):
user = models.OneToOneField(User)
class Purchase(models.Model):
customer = models.ForeignKey(Customer)
date = models.DateField(auto_now_add=True)
item = models.CharField(max_length=255)
如果我有以下数据:
Customer A -
Purchase(item=Chair, date=January),
Purchase(item=Table, date=February)
Customer B -
Purchase(item=Speakers, date=January),
Purchase(item=Monitor, date=May)
Customer C -
Purchase(item=Laptop, date=March),
Purchase(item=Printer, date=April)
我希望能够提取以下内容:
Purchase(item=Monitor, date=May)
Purchase(item=Printer, date=April)
Purchase(item=Table, date=February)
每个客户的列表中最多只有一次购买。购买是每个客户的最新消息。它按最新日期排序。
此查询将能够提取:
SELECT * FROM (
SELECT DISTINCT ON
"shop_purchase.customer_id"
"shop_purchase.id"
"shop_purchase.date"
FROM "shop_purchase"
ORDER BY "shop_purchase.customer_id" ASC,
"shop_purchase.date" DESC;
)
AS result
ORDER BY date DESC;
我正试图找到一种不必使用原始SQL来实现此结果的方法。
答案 0 :(得分:5)
这可能不是您正在寻找的,但它可能会让您更接近。看看Django's annotate。
以下是可能有所帮助的示例:
from django.db.models import Max
Customer.objects.all().annotate(most_recent_purchase=Max('purchase__date'))
这将为您提供一份客户模型列表,其中每个客户模型都有一个名为“most_recent_purchase”的新属性,并包含他们上次购买的日期。生成的sql看起来像这样:
SELECT "demo_customer"."id",
"demo_customer"."user_id",
MAX("demo_purchase"."date") AS "most_recent_purchase"
FROM "demo_customer"
LEFT OUTER JOIN "demo_purchase" ON ("demo_customer"."id" = "demo_purchase"."customer_id")
GROUP BY "demo_customer"."id",
"demo_customer"."user_id"
另一种选择是将属性添加到您的客户模型中,如下所示:
@property
def latest_purchase(self):
return self.purchase_set.order_by('-date')[0]
您显然需要处理此属性中没有任何购买的情况,这可能无法很好地执行(因为您将为每个客户运行一个查询以获取他们的最新购买)。
我过去曾使用过这两种技术,但在不同情况下它们都能正常运行。我希望这有帮助。祝你好运!
答案 1 :(得分:5)
每当使用Django ORM编写一个困难的查询时,我首先在psql(或您使用的任何客户端)中尝试查询。您想要的SQL是不这个:
SELECT * FROM (
SELECT DISTINCT ON
"shop_purchase.customer_id" "shop_purchase.id" "shop_purchase.date"
FROM "shop_purchase"
ORDER BY "shop_purchase.customer_id" ASC, "shop_purchase.date" DESC;
) AS result
ORDER BY date DESC;
在上面的SQL中,内部SQL在(customer_id,id和date)的组合上寻找distinct,并且由于id对于所有人都是唯一的,因此您将从表中获取所有记录。我假设id是按照惯例的主键。
如果您需要找到每个客户的最后一次购买,您需要执行以下操作:
SELECT "shop_purchase.customer_id", max("shop_purchase.date")
FROM shop_purchase
GROUP BY 1
但上述查询的问题在于它只会为您提供客户名称和日期。在子查询中使用这些结果时,使用它将无法帮助您查找记录。
要使用IN
,您需要一个唯一参数列表来标识记录,例如 id
如果您的记录 id 是序列密钥,那么您可以利用最新日期也是最大ID的事实。所以你的SQL变成了:
SELECT max("shop_purchase.id")
FROM shop_purchase
GROUP BY "shop_purchase.customer_id";
请注意,我在selected子句中只保留了一个字段( id ),以便在使用IN的子查询中使用它。
完整的SQL现在将是:
SELECT *
FROM shop_customer
WHERE "shop_customer.id" IN
(SELECT max("shop_purchase.id")
FROM shop_purchase
GROUP BY "shop_purchase.customer_id");
使用Django ORM看起来像:
(Purchase.objects.filter(
id__in=Purchase.objects
.values('customer_id')
.annotate(latest=Max('id'))
.values_list('latest', flat=True)))
希望它有所帮助!
答案 2 :(得分:3)
我有类似的情况,这就是我计划如何去做的事情:
query = Purchase.objects.distinct('customer').order_by('customer').query
query = 'SELECT * FROM ({}) AS result ORDER BY sent DESC'.format(query)
return Purchase.objects.raw(query)
上升它给了我想要的查询。缺点是它是原始查询,我无法附加任何其他查询集过滤器。
答案 3 :(得分:1)
如果我需要一些数据子集(N项)以及Django查询,这是我的方法。这是使用PostgreSQL和方便的json_build_object()
函数(Postgres 9.4+)的示例,但同样可以在其他数据库系统中使用其他聚合函数。对于较旧的PostgreSQL版本,您可以使用array_agg()
和array_to_string()
函数的组合。
想象一下,您拥有Article
和Comment
模型以及列表中的每篇文章,您想要选择3条最近的评论(更改LIMIT 3
以调整子集的大小或{{1}改变子集的排序。)
ORDER BY c.id DESC