Vì mỗi Count() trên một quan hệ khác nhau tạo thêm một LEFT OUTER JOIN.
Hai join cùng lúc sinh tích Descartes giữa hai bảng con, nên mỗi dòng bị lặp lại và hàm đếm cộng dồn trên các dòng trùng.
# author has 2 books and 5 followers
Author.objects.annotate(Count('book'), Count('follower'))
# book__count = 10, follower__count = 10 -> both wrongCách sửa 1 — distinct=True:
Author.objects.annotate(
Count('book', distinct=True),
Count('follower', distinct=True),
)Đúng cho Count vì COUNT(DISTINCT id) loại được dòng trùng. Nhưng không cứu được Sum / Avg: các giá trị trùng là hợp lệ (hai sách cùng giá) nên DISTINCT sẽ gộp nhầm cả dữ liệu thật.
Cách sửa 2 — Subquery (dùng khi có Sum/Avg, hoặc muốn tránh join hoàn toàn):
from django.db.models import OuterRef, Subquery, Sum
revenue = (Book.objects
.filter(author=OuterRef('pk'))
.values('author')
.annotate(total=Sum('price'))
.values('total'))
Author.objects.annotate(revenue=Subquery(revenue))Mỗi aggregate chạy trong subquery độc lập nên không join chồng lên nhau. Trên bảng lớn, subquery thường còn nhanh hơn vì tránh được tích Descartes.
Dấu hiệu nhận biết khi debug: in str(qs.query) ra, thấy nhiều LEFT OUTER JOIN trong cùng một câu là gần như chắc chắn đang bị nhân dòng.