Django에서 SELECT COUNT (*) GROUP BY 및 ORDER BY를 수행하는 방법은 무엇입니까?


99

시스템을 통과하는 모든 이벤트를 추적하기 위해 트랜잭션 모델을 사용하고 있습니다.

class Transaction(models.Model):
    actor = models.ForeignKey(User, related_name="actor")
    acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
    action_id = models.IntegerField() 
    ......

내 시스템에서 상위 5 명의 배우를 얻으려면 어떻게해야합니까?

SQL에서는 기본적으로

SELECT actor, COUNT(*) as total 
FROM Transaction 
GROUP BY actor 
ORDER BY total DESC

답변:


181

문서에 따르면 다음을 사용해야합니다.

from django.db.models import Count
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')

values ​​() : "그룹화 기준"에 사용할 열을 지정합니다.

장고 문서 :

"values ​​() 절을 사용하여 결과 집합에 반환되는 열을 제한하면 주석을 평가하는 방법이 약간 다릅니다. 원본 QuerySet의 각 결과에 대해 주석이 달린 결과를 반환하는 대신 원본 결과는 다음과 같이 그룹화됩니다. values ​​() 절에 지정된 필드의 고유 한 조합 "

annotate () : 그룹화 된 값에 대한 작업을 지정합니다.

장고 문서 :

요약 값을 생성하는 두 번째 방법은 QuerySet의 각 개체에 대해 독립적 인 요약을 생성하는 것입니다. 예를 들어 책 목록을 검색하는 경우 각 책에 기여한 저자 수를 알고 싶을 수 있습니다. 각 책은 저자와 다 대다 관계를 가지고 있습니다. QuerySet의 각 책에 대해이 관계를 요약하려고합니다.

annotate () 절을 사용하여 객체 별 요약을 생성 할 수 있습니다. annotate () 절이 지정되면 QuerySet의 각 객체에 지정된 값이 주석으로 추가됩니다.

order by 절은 자명합니다.

요약하면, 작성자의 쿼리 세트를 생성하여 그룹화하고 주석을 추가하고 (반환 된 값에 추가 필드가 추가됨) 마지막으로이 값을 기준으로 정렬합니다.

자세한 내용은 https://docs.djangoproject.com/en/dev/topics/db/aggregation/ 을 참조하십시오.

참고 : Count를 사용하는 경우 Count에 전달 된 값은 집계에 영향을주지 않고 최종 값에 지정된 이름 만 영향을줍니다. 집계자는 Count에 전달 된 값이 아니라 값의 고유 한 조합 (위에서 언급 한대로)별로 그룹화합니다. 다음 쿼리는 동일합니다.

Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
Transaction.objects.all().values('actor').annotate(total=Count('id')).order_by('total')

나를 위해 그것은 Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')django.db.models에서 Count를 가져 오는 것을 잊지 마십시오. 감사합니다
Ivancho 2014 년

3
참고 사항 : Count(및 기타 집계 자)를 사용하는 경우 전달 된 값 Count은 집계에 영향 을 주지 않고 최종 값에 지정된 이름 만 적용됩니다. 애그리 게이터 values는에 전달 된 값이 아니라 (위에서 언급 한) 고유 한 조합으로 그룹화됩니다 Count.
kronosapiens

postgres 검색 결과 쿼리 세트에 이것을 사용하여 패싯을 얻을 수도 있습니다!
yekta

2
@kronosapiens 적어도 요즘에는 영향을 미칩니다 (Django 2.1.4를 사용하고 있습니다). 예에서, total주어진 이름과 sql에서 사용 된 개수 COUNT('actor')는이 경우 중요하지 않지만 예를 들어 values('x', 'y').annotate(count=Count('x')), COUNT(x)COUNT(*)COUNT(x, y)./manage.py shell
또는를

35

@Alvaro가 Django의 직접 동등한 for GROUP BY문에 대답 한 것처럼 :

SELECT actor, COUNT(*) AS total 
FROM Transaction 
GROUP BY actor

사용하는 것이다 values()하고 annotate()다음 방법 :

Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()

그러나 한 가지 더 지적해야합니다.

모델에 정의 된 기본 순서가있는 경우 class Meta.order_by()절은 올바른 결과에 대한 의무입니다. 주문할 의도가없는 경우에도 건너 뛸 수 없습니다.

또한 고품질 코드의 경우 . 가없는 경우에도 항상 .order_by()뒤에 절을 추가하는 것이 좋습니다 . 이러한 접근 방식은 진술을 미래 지향적으로 만들 것입니다 . 향후 변경 사항에 관계없이 의도 한대로 작동합니다 .annotate()class Meta: orderingclass Meta: ordering


예를 들어 보겠습니다. 모델에 다음이있는 경우 :

class Transaction(models.Model):
    actor = models.ForeignKey(User, related_name="actor")
    acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
    action_id = models.IntegerField()

    class Meta:
        ordering = ['id']

그렇다면 그러한 접근 방식은 작동하지 않을 것입니다.

Transaction.objects.values('actor').annotate(total=Count('actor'))

Django GROUP BY는 모든 필드에서 추가 작업 을 수행하기 때문 입니다.class Meta: ordering

쿼리를 인쇄 할 경우 :

>>> print Transaction.objects.values('actor').annotate(total=Count('actor')).query
  SELECT "Transaction"."actor_id", COUNT("Transaction"."actor_id") AS "total"
  FROM "Transaction"
  GROUP BY "Transaction"."actor_id", "Transaction"."id"

집계가 의도 한대로 작동하지 않을 것이므로이 .order_by()동작을 지우고 적절한 집계 결과를 얻으려면 절을 사용해야합니다.

참조 : 공식 Django 문서의 기본 순서 또는 order_by ()와의 상호 작용 .


3
.order_by()orderingMeta 에서 나를 구 했습니다.
Babken Vardanyan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.