2017-10-21 2 views
1

Ich habe ein Django-Modell, das einen eindeutigen Datensatz mit einem Datum enthält. Ich zähle derzeit Datensätze in Bereiche von Tagen, z. X Nummer hat das heutige Datum bereits überschritten, X wird innerhalb der nächsten 10 Tage passieren, X wird innerhalb der nächsten 30 Tage passieren. Der folgende Code ist, was ich derzeit verwende, zieht alle Werte von einer records.objects.all() Abfrage gegen das Modell zurück und durchläuft dann jedes Objekt, um das Datetime-Delta zu berechnen und den relevanten Zähler zu inkrementieren.Performante Berechnung von datetime diffs in Tagen

for x in records: 
    if x.date is None: 
     missingValue += 1 
    else: 
     delta = x.date - date.today() 
     if delta.days < 0: 
      passed += 1 
     if delta.days < 10: 
      tenDays += 1 
     if delta.days < 30: 
      thirtyDays += 1 

Für rund 50.000 Datensätze dies etwa 5-6 Sekunden dauert, die länger ist als ich möchte, ich versuche, dies als die Anzahl der Datensätze zu reduzieren, ist wahrscheinlich zu erhöhen. Die Frage ist wirklich um die performante Berechnung von datetime diffs und Gruppierung der resultierenden Anzahl von Tagen, als ob es eine bessere Methode durch eine Django-Abfrage oder eine andere Methode gibt, die ich nicht finden konnte.

Ich habe die Verwendung von DateAdd in einer Raw-SQL untersucht, aber es scheint, dass ich die Datenbank für jeden Datumsbereich abfragen muss und immer noch dazu führen würde, dass ich die Ergebnisse durchlaufen muss.

+0

Bitte bereiten http://www.rextester.com Demo mit Beispieldaten und gewünschten Ausgang. Ich bin sicher, dass wir in reinem SQL Zeiten unter 1 Sekunde bekommen konnten. – lad2025

+1

http://rextester.com/AWPX46055 - Denken Sie, dass ich das richtig gemacht habe. Gewünschte Ausgabe ist nur die Rückgabe von Zahlen durch die oben genannten Gruppen, die innerhalb von zehn Tagen innerhalb von dreißig Tagen bestanden werden. Ich sehe auch einen zukünftigen Bedarf für etwas über 30 Tage. Die internen Daten sind kumulativ. – Draineh

+1

Danke für Beispieldaten – lad2025

Antwort

1

Verwenden von SQL gefensterten COUNT:

WITH cte AS (
    SELECT *,CASE WHEN DATEDIFF(DAY,GETDATE(),targetdate) <=0 THEN 0 
        WHEN DATEDIFF(DAY,GETDATE(),targetdate) <=10 THEN 10 
        WHEN DATEDIFF(DAY,GETDATE(),targetdate) <=30 THEN 30 
        ELSE 31 END AS grp 
    FROM [record] 
    --WHERE targetdate > GETDATE() - 60 -- last 60 days 
) 
SELECT DISTINCT grp, COUNT(*) OVER(ORDER BY grp) AS running_count 
FROM cte; 

Rextester Demo

+1

Das ist perfekt danke, Sekunden Reaktionszeit jetzt – Draineh

1

Vor der Optimierung der Leistung würde ich eine Batch-Ausführung betrachten. Ihr kleinstes Änderungsfenster scheint 1 Tag zu sein. So durch Filterung auf einem ‚aktualisiert‘ Feld im Datensatz-Modell können Sie jede Stunde anrufen (pro cron) ein:

from datetime import datetime, timedelta 
records.objects.filter(updated__lt = datetime.now()-timedelta(days=1))[:2083] 

und Ihren Betrieb tun. Hinweis: Sie können die Anzahl der abgerufenen Datensätze begrenzen. So werden jede Stunde 2083 (oder 5000) Datensätze verarbeitet, die die Aufgabe über den Tag verteilen. Diese Zahl, die Sie anhand der Anzahl der Datensätze in der Datenbank skalieren können (z. B. 50000/24 ​​= 2083)

Auch Ihre Migration möchte möglicherweise widerspiegeln, dass Sie sie weit in der Vergangenheit festlegen möchten, sodass jeder Live-Datensatz einmal verarbeitet wird am Anfang.

+0

Sie haben Recht, dass das kleinste Fenster 1 Tag ist. Im Moment nehme ich die Ergebnisse und präsentiere sie direkt den Benutzern (Teil der Ausgabe). Mit diesem Ansatz könnte ich das Ergebnis zwischenspeichern und jedes Mal aufrufen, wenn ein Benutzer die Zahlen ansieht, statt sie im laufenden Betrieb zu berechnen. – Draineh

+1

Ja, der Punkt hier ist, wo auch immer Sie 'Model.objects.all()' Sie schreiben sollte über Filterung nachdenken, um die Belastung der Datenbank zu reduzieren. Wenn Sie andere Filtermethoden finden können, ist das gut. Z.B. Wenn ein Benutzer eine Aktion ausführt, aktualisieren Sie alle mit diesem Benutzer verknüpften Datensätze. Die Charge-pro-Stunde ist die erschöpfende Lösung und vermeidet vergessene Aufzeichnungen. – rollinger

Verwandte Themen