Как запросить максимальное количество связанных записей, удовлетворяющих предикату - PullRequest
3 голосов
/ 19 августа 2011

Я работаю над приложением Django, для которого я столкнулся с хитрым агрегированным запросом, который я хотел бы оценить.

В приложении demo моего проекта я объявил следующие классы моделей для представления фондов библиотек:

from django.db import models

class Book(models.Model):
    interesting = models.BooleanField()

class Library(models.Model):
    books = models.ManyToManyField(Book)

То, что я хотел бы запросить, - это максимальное количество «интересных» книг в одной библиотеке, то есть максимальное количество «интересующих» книг в каждой библиотеке.

В SQL это:

select max(a.num_interesting_books) as max
from (select count(demo_book.id) as num_interesting_books
      from demo_book
      inner join demo_library_books on (demo_book.id = demo_library_books.book_id)
      where demo_book.interesting=TRUE
      group by demo_library_books.library_id) as a

Использование следующих тестовых данных:

insert into demo_library(id) values (1), (2), (3);
insert into demo_book(id, interesting) values
(1, FALSE), (2, FALSE), (3, TRUE),
(4, TRUE), (5, TRUE),
(6, TRUE), (7, TRUE), (8, TRUE), (9, FALSE);
insert into demo_library_books(library_id, book_id) values
(1, 1), (1, 2), (1, 3),
(2, 4), (2, 5),
(3, 6), (3, 7), (3, 8), (3, 9), (3, 3);

Приведенный выше оператор SELECT приводит к:

 max
-----
   4
(1 row)

как и ожидалось.

Можно ли использовать API запросов Django для вычисления этого значения?

1 Ответ

5 голосов
/ 19 августа 2011

Кажется, я понял это:

Library.objects.filter(books__interesting=True).annotate(num_interesting_books=Count('pk')).aggregate(max=Max('num_interesting_books'))

В переводе на SQL это:

select max(a.num_interesting_books) as max
from (select demo_library.*, count(demo_library.id) as num_interesting_books
      from demo_library
      inner join demo_library_books on (demo_library.id = demo_library_books.library_id)
      inner join demo_book on (demo_library_books.book_id = demo_book.id)
      where demo_book.interesting=TRUE
      group by demo_library.id) as a
...