回答

收藏

Django中GROUP BY总结中注释

技术问答 技术问答 193 人阅读 | 0 人回复 | 2023-09-14

更新
3 W' T# e% C) H9 M0 ^9 W0 B多亏了发布的答案,我找到了更简单的方法来解决这个问题。历史记录中可以看到原始问题。5 }: V5 j: f3 K1 x" W9 C6 s* _: h! |/ o; A
问题我正在尝试将军SQL查询转换为Django,但是我遇到了不明白的错误。0 l8 w* M# W" e' E+ g* C
这是我所拥有的Django模型:
# V! D& p  x% C$ I( d2 tclass Title(models.Model):  title_id = models.CharField(primary_key=True,max_length=12)  title = models.CharField(max_length=80)  publisher = models.CharField(max_length=100)  price = models.DecimalField(decimal_places=2,blank=True,null=True)我有以下数据:
8 Q% E/ x9 o5 O5 qAnyone?Binnet & Hardley             MC3021     .99  The Gourmet MicrowaveBinnet & Hardley             MC22222       1919.99  Silicon Valley   Gastronomic TreatsAlgodata Infosystems         PC1035    .95  But Is It User Friendly?Algodata Infosystems         BU1032      .99  The Busy Executive's   Database GuideAlgodata Infosystems         PC88888        20                                20  Secrets of Silicon ValleyAnyone?Binnet & Hardley             MC3021         2.99  The Gourmet MicrowaveBinnet & Hardley             MC2222        19.99  Silicon Valley   Gastronomic TreatsAlgodata Infosystems         PC1035        22.95  But Is It User Friendly?Algodata Infosystems         BU1032        19.99  The Busy Executive's   Database GuideAlgodata Infosystems         PC8888        20     Secrets of Silicon Valley这是我要执行的操作:引入带注释的字段dbl_price,该字段的价格是价格的两倍,然后将收入的查询集分组publisher,计算每个出版商dbl_price发布者发布的所有标题的所有值的总和。3 P& Q; V; e4 R  A1 L
执行此操作SQL查询如下:
* \) l$ V. s/ X7 D. nSELECT SUM(dbl_price) AS total_dbl_price,publisherFROM (  SELECT price * 2 AS dbl_price,publisher  FROM title) AS A GROUP BY publisher所需输出为:
. V' w6 o7 v/ t' Z7 ppublisher                    tot_dbl_prices---------------------------  --------------Algodata Infosystems           .88Binnet & Hardley               5555555555555555555555555555555555555555545555555555555555555555555555555555555                       .96New Age Books                                                        .86Django查询查询如下:9 J! @; }5 ]6 `0 `0 V9 H7 C
Title.objects .annotate(dbl_price=2*F('price')) .values('publisher') .annotate(tot_dbl_prices=Sum('dbl_price'))但给出一个错误:9 a' ], L+ F/ g1 {
KeyError: 'dbl_price'.说不能查询dbl_price集中找到字段。$ i6 I; a, R0 o! q
错误原因这就是错误的原因:文档说
7 G, o* S. r. r* }9 U8 S1 E* ^还要注意,average_rating已明确包含在要返回的值列表中。这是必需的,因为values()和annotate()子句的顺序。
3 F4 _% r/ y, w! [" t如果values()子句位于annotate()在句子之前,所有注释将自动添加到结果集中。但是,如果values()子句在annotate()应用后,需要包括聚合列在内的显式。$ ]/ H+ `7 X# L" i3 _0 \9 S
因此,dbl_price因为它是由聚合物找不到的Prior创建的annotate,但不包括在中values()。* u* ]' ^0 \: T, q8 J; c
但是,我不能把它包括在内values,因为我想用values(紧随其后annotate)因为: u; Y  t; ]( }; g$ _# ?
如果values()子句位于annotate()使用前values()子句描述的分组计算注释。
  \# i0 Z: h+ t5 p* Y
这是Django实现SQLGROUP BY基础。这意味着我不能包括它dbl_priceinside0 A8 }0 f, @9 `0 F+ z( c
values()因为这种分组将基于字段publisher和的唯一组合dbl_price,而我publisher只需按分组。$ ^) F4 m, z* h1 U
因此,下面的查询实际上与上面的查询不同,因为我在模型的price字段而不是注释dbl_price总结字段:
) F4 c. e# s7 C6 p$ P5 L2 X; ]Title.objects .annotate(dbl_price=2*F('price')) .values('publisher') .annotate(sum_of_prices=Count('price'))因为该price字段位于模型中,而不是注释字段,因此我们不需要将其包含在模型中values查询集中。
6 t2 _# v3 ~; h" K1 p问题因此,我们在这里有它:我需要包含带注释的属性,values把它在查询集中,但我不能这样做,因为values它也用于分组多余的字段会出错)。问题本质上是因为values在Django这取决于上下文(是否使用了两种非常不同的方法)values紧随其后annotate)-即(1)提取值(SQL简单SELECT(2)分组 聚合组(SQL' z0 G$ ~4 p' n. `" u+ @0 u
GROUP BY)-在这种情况下,这两种方式似乎相互冲突。9 Q, \* W3 ~6 J* s
我的问题是    :有什么办法可以解决这个问题(没有回到原来的地方)sql等等)?
' R  @3 j$ a- h2 X5 k请注意:
) i* m8 P; p& @, O8 Z% [# D一切都可以通过annotate语句移到后面来解决有问题的特定示例values,多个答案已经指出了这一点。但由于以下三个原因,我对annotate前面的解决方案(或讨论)更感兴趣values():1.还有一些比较复杂的例子,建议的解决方案不起作用。.我可以想象,带注释的查询集已经传递给另一个函数,这个函数实际上是执行的GROUP" {' r7 x0 P' d) E" B. _
BY,因此,我们唯一了解的是带注释字段的名称集及其类型。.情况似乎很简单,如果values()我很惊讶之前没有注意到和讨论过两种不同用法的冲突。
; j  x9 D2 k  z                                                               
# P# }6 U( I5 P* q; x    解决方案:                                                                # Z4 t- A% m( w# o1 _( D
                                                                更新:自Django 2.一起,一切都可用。不需要任何解决方案,生成的查询是正确的。: r8 w9 o! V) x8 Q$ q! j  y' i3 X
这可能已经太晚了,但我找到了解决方案(已经Django 1.11.1.测试)。2 X; |+ L' j9 _/ S, k# \
问题是,.values('publisher将删除分组所需的对调用.values() 字段    参数中未包含的所有注释。# X) T% P' o9 v( j/ o
而且我们不能dbl_price将param包含到 字段中    ,因为它会添加另一个GROUP BY语句。) A# ?0 ?4 J! G' @: h
对于所有聚合解决方案,首先需要注释字段,然后调用.values()聚合并将其包含在 中字段    param中(不会添加GROUPBY,因为它们是聚合的)。然后,我们应该.annotate()使用ANY调用表达式-这将使django addGROUPBY语句使用query- publisher中    唯一的非聚合字段SQL查询。; P- V' O9 A. @" \/ \8 @+ u
Title.objects    .annotate(dbl_price=2*F('price)     .annotate(sum_of_prices=Sum('dbl_price)     .values('publisher','sum_of_prices    .annotate(titles_count=Count('id'))这种方法的唯一缺点-! J2 a! z+ F5 K
除带注释字段的聚合外,如果不需要任何其他聚合,则必须包括一些聚合。如果没有.annotate()最终调用(应至少包含一个表达式!),Django将不会添加GROUPBY到SQL查询中。一种解决方法是创建字段的副本:+ k2 @8 F* m  F* N
Title.objects    .annotate(dbl_price=2*F('price)     .annotate(_sum_of_prices=Sum('dbl_price')) # note the underscore!    .values('publisher','_sum_of_prices    .annotate(sum_of_prices=F('_sum_of_prices')另外,请提及,您应该小心使用QuerySet排序。你最好.order_by()不需要任何参数来清除顺序,也可以随意呼叫GROUPBY。如果结果查询包含任何其他字段,分组将是错误的。https://docs.djangoproject.com/zh-. z9 @. j/ z6 S: r
CN/1.11/topics/db/aggregation/#interaction-with-default-ordering-or-order-
8 T6 F  k/ }: R/ w: Iby
2 _# c/ M& n& c. ?. I此外,您可能希望从输出中删除假注释,因此再次调用.values()。因此,最终代码如下:8 P' K/ q( k$ L- E: T( H7 d' H
Title.objects    .annotate(dbl_price=2*F('price)     .annotate(_sum_of_prices=Sum('dbl_price)     .values('publisher','_sum_of_prices    .annotate(sum_of_prices=F('_sum_of_prices'))
* E" C* K' L2 e& {4 R    .values('publisher', 'sum_of_prices')7 i1 r8 i1 B6 F6 @7 b
    .order_by('publisher')
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则