2016-04-15 5 views
0

[Bearbeiten: Verwenden von Django 1.9 & MySQL 5.6; kein DISTINCT ON Keyword]Django - Letzter zugehöriger Status-Datensatz für jedes Objekt in einem einzelnen Abfrage-Set (großer Datensatz)

Ich habe zwei Modelle bekam entspricht in etwa wie folgt zusammen:

class Vehicle(models.Model): 
    vin = models.CharField(max_length=255) 
    ... # lots more not-interesting fields 


class Status(models.Model): 
    """The status of a vehicle at a moment in time""" 
    vehicle = models.ForeignKey(Vehicle, related_name='status') 
    code = models.CharField(max_length=20) 
    time = models.DateTimeField() 

     class Meta: 
      order_by = ('time',) 

Wie kann ich eine einzelne Abfrage verwenden den aktuellen Status jedes Fahrzeug zurück? Es gibt Hunderte von Fahrzeugen und Hunderttausende von Statusaufzeichnungen.

Looping über jedes Fahrzeug und Auswahl des letzten Status ist waaaaay zu langsam für die Anzahl der Fahrzeuge (Hunderte) und Status (Hunderttausende).

Ich versuchte, mit .annotate() und .values ​​() dies zu tun; Warum funktioniert das nicht? Ich würde erwarten, dass dies ein kartesisches Produkt von Vehicle und Status-Tabellen zurückgibt, dann filtern Sie alle außer dem letzten Status heraus.

vehicles = Vehicle.objects.annotate(
    status_time=F('status__time'), 
    status_time_latest=Max('status_time'), 
    status_code=F('status__code'), 
).filter(
    status_time=F('status_time_latest'), 
).values() 

Stattdessen Djangos (1,9) scheint für jedes Fahrzeug nur den ersten Statuscode zurückkehrt zu werden (Reihenfolge nach ID).

Ist das, was select_related() ist, oder würde das Ende der Übertragung der gesamten Status-Tabelle über die Leitung? Es ist viel zu groß, um jedes Mal abzulegen, wenn ich diese Abfrage ausführen muss. Ich würde lieber die Verarbeitung auf den Datenbankserver verlagern.

Antwort

2

Sie eine Mischung aus order_by verwenden könnte und distinct zu erreichen, was Sie wollen:

vehicles = Vehicle.objects 
       .annotate(status_time=F('status__time'), status_code=F('status__code')) 
       .order_by('id', '-status_time').distinct('id') 

Brechen it down:

# first annotate all vehicle objects with all the statuses 
vehicles = Vehicle.objects.annotate(status_time=F('status__time'), status_code=F('status__code')) 

# order by id, and in decreasing order or status_time 
vehicles = vehicles.order_by('id', '-status_time') 

# get distinct using id, this will make sure that the first entry for 
# each Vehicle is retained and since we ordered in decreasing order of 
# status_time within each vehicle the first entry will have latest status 
vehicles = vehicles.distinct('id') 
+0

Tolle Idee in der Theorie, aber ich erhalte „NotImplementedError: DISTINCT ON-Felder werden von diesem Datenbank-Backend nicht unterstützt "mit MySQL 5.6 –

+0

Das ist mein schlechtes. Ich hätte das mysql-Tag bemerkt. Obwohl das wunderbar in Postgres funktioniert. – AKS

+0

Nein, es ist cool. Ich füge das zu der wachsenden Liste von Gründen hinzu, um zu PostgreSQL zu wechseln, genau dort mit dem nativen JSON-Feldtyp für beliebige Datenstrukturen. Im Moment bin ich mit MySQL festgefahren, also werde ich nach anderen Lösungen suchen, aber das ist eine großartige Antwort für jemanden mit dem richtigen Tech-Stack. –

Verwandte Themen