Intento codificar la consulta Django equivalente a partir de esta consulta SQL, pero estoy atascado. Cualquier ayuda es bienvenida. Recibo una id de carrera y de esta carrera quiero hacer algunas estadísticas: nb_race = el número de carreras de un Caballo antes de la carrera dada, best_chrono = mejor tiempo de un Caballo antes de la carrera dada.
SELECT *, (SELECT count(run.id) FROM runner run INNER JOIN race ON run.race_id = race.id WHERE run.horse_id = r.horse_id AND race.datetime_start < rc.datetime_start ) AS nb_race, (SELECT min(run.chrono) FROM runner run INNER JOIN race ON run.race_id = race.id WHERE run.horse_id = r.horse_id AND race.datetime_start < rc.datetime_start ) AS best_time FROM runner r, race rc WHERE r.race_id = rc.id AND rc.id = 7890Modelos Django:
class Horse(models.Model): id = AutoField(primary_key=True) name = models.CharField(max_length=255, blank=True, null=True, default=None) class Race(models.Model): id = AutoField(primary_key=True) datetime_start = models.DateTimeField(blank=True, null=True, default=None) name = models.CharField(max_length=255, blank=True, null=True, default=None) class Runner(models.Model): id = AutoField(primary_key=True) horse = models.ForeignKey(Horse, on_delete=models.PROTECT) race = models.ForeignKey(Race, on_delete=models.PROTECT) chrono = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, default=None)La expresión de subconsulta se puede usar para compilar un conjunto de consulta adicional como una subconsulta que depende del conjunto de consulta principal y ejecutarlos juntos como un solo SQL.
from django.db.models import OuterRef, Subquery, Count, Min, F # prepare a repeated expression about previous runners, but don't execute it yet prev_run = ( Runner.objects .filter( horse=OuterRef('horse'), race__datetime_start__lt=OuterRef('race__datetime_start')) .values('horse') ) queryset = ( Runner.objects .values('id', 'horse_id', 'race_id', 'chrono', 'race__name', 'race__datetime_start') .annotate( nb_race=Subquery(prev_run.annotate(nb_race=Count('id')).values('nb_race')), best_time=Subquery(prev_run.annotate(best_time=Min('chrono')).values('best_time')) ) )Algunos trucos utilizados aquí se describen en los documentos vinculados:
.values(...) a un campo: solo el valor agregado.annotate() se usa en la subconsulta (no .aggregate() ). Eso agrega un GROUP BY race.horse_id , pero no es un problema porque también hay WHERE race.horse_id = ... y un optimizador de SQL finalmente ignorará el "group by" en un backend de base de datos moderno.Se compila en una consulta equivalente al SQL del ejemplo. Compruebe el SQL:
>>> print(str(queryset.query)) SELECT ..., (SELECT COUNT(U0.id) FROM runner U0 INNER JOIN race U1 ON (U0.race_id = U1.id) WHERE (U0.horse_id = runner.horse_id AND U1.datetime_start < race.datetime_start) GROUP BY U0.horse_id ) AS nb_race, ... FROM runner INNER JOIN race ON (runner.race_id = race.id)Una diferencia marginal es que una subconsulta utiliza algunos alias internos como U0 y U1.