Skip to content
Snippets Groups Projects
Verified Commit 8451aa07 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Optimize query in generate_person_list_with_class_register_statistics

- Move filter conditions into JOIN clause
- Remove superflouos subquery for tardiness
- Remove unnecessary JOINs
  - Remove need for DISTINCT by fixing double JOIN on personal notes
parent fc6fb753
No related branches found
No related tags found
1 merge request!162Resolve "[SQL] Person statistics in "My students" take very long"
Pipeline #6477 passed
from datetime import date
from typing import Dict, Iterable, Iterator, Optional, Union
from django.db.models import Exists, OuterRef, Q, QuerySet
from django.db.models import Exists, FilteredRelation, OuterRef, Q, QuerySet
from django.db.models.aggregates import Count, Sum
from django.db.models.expressions import Subquery
from django.urls import reverse
from django.utils.translation import gettext as _
......@@ -386,59 +385,45 @@ def generate_person_list_with_class_register_statistics(
if persons is None:
persons = self.members.all()
# Build reusable Q objects for filtering by school term and by groups
# Necessary for the following annotations
school_term_q = (
Q(personal_notes__lesson_period__lesson__validity__school_term=self.school_term)
| Q(personal_notes__extra_lesson__school_term=self.school_term)
| Q(personal_notes__event__school_term=self.school_term)
lesson_periods = LessonPeriod.objects.filter(
lesson__validity__school_term=self.school_term
).filter(Q(lesson__groups=self) | Q(lesson__groups__parent_groups=self))
extra_lessons = ExtraLesson.objects.filter(school_term=self.school_term).filter(
Q(groups=self) | Q(groups__parent_groups=self)
)
groups_q = (
Q(personal_notes__lesson_period__lesson__groups=self)
| Q(personal_notes__lesson_period__lesson__groups__parent_groups=self)
| Q(personal_notes__extra_lesson__groups=self)
| Q(personal_notes__extra_lesson__groups__parent_groups=self)
| Q(personal_notes__event__groups=self)
| Q(personal_notes__event__groups__parent_groups=self)
events = Event.objects.filter(school_term=self.school_term).filter(
Q(groups=self) | Q(groups__parent_groups=self)
)
persons = persons.filter(personal_notes__groups_of_person=self).filter(school_term_q).distinct()
persons = persons.select_related("primary_group", "primary_group__school_term")
persons = persons.annotate(
absences_count=Count(
filtered_personal_notes=FilteredRelation(
"personal_notes",
filter=Q(personal_notes__absent=True) & school_term_q & groups_q,
distinct=True,
condition=(
Q(personal_notes__event__in=events)
| Q(personal_notes__lesson_period__in=lesson_periods)
| Q(personal_notes__extra_lesson__in=extra_lessons)
),
)
).annotate(
absences_count=Count(
"filtered_personal_notes", filter=Q(filtered_personal_notes__absent=True),
),
excused=Count(
"personal_notes",
"filtered_personal_notes",
filter=Q(
personal_notes__absent=True,
personal_notes__excused=True,
personal_notes__excuse_type__isnull=True,
)
& school_term_q
& groups_q,
distinct=True,
filtered_personal_notes__absent=True,
filtered_personal_notes__excused=True,
filtered_personal_notes__excuse_type__isnull=True,
),
),
unexcused=Count(
"personal_notes",
filter=Q(personal_notes__absent=True, personal_notes__excused=False)
& school_term_q
& groups_q,
distinct=True,
),
tardiness=Subquery(
Person.objects.filter(school_term_q & groups_q)
.filter(pk=OuterRef("pk"),)
.distinct()
.annotate(tardiness=Sum("personal_notes__late"))
.values("tardiness")
"filtered_personal_notes",
filter=Q(filtered_personal_notes__absent=True, filtered_personal_notes__excused=False),
),
tardiness=Sum("filtered_personal_notes__late"),
tardiness_count=Count(
"personal_notes",
filter=~Q(personal_notes__late=0) & school_term_q & groups_q,
distinct=True,
"filtered_personal_notes", filter=Q(filtered_personal_notes__late__gt=0),
),
)
......@@ -446,9 +431,8 @@ def generate_person_list_with_class_register_statistics(
persons = persons.annotate(
**{
extra_mark.count_label: Count(
"personal_notes",
filter=Q(personal_notes__extra_marks=extra_mark) & school_term_q & groups_q,
distinct=True,
"filtered_personal_notes",
filter=Q(filtered_personal_notes__extra_marks=extra_mark),
)
}
)
......@@ -457,11 +441,11 @@ def generate_person_list_with_class_register_statistics(
persons = persons.annotate(
**{
excuse_type.count_label: Count(
"personal_notes__absent",
filter=Q(personal_notes__absent=True, personal_notes__excuse_type=excuse_type,)
& school_term_q
& groups_q,
distinct=True,
"filtered_personal_notes__absent",
filter=Q(
filtered_personal_notes__absent=True,
filtered_personal_notes__excuse_type=excuse_type,
),
)
}
)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment