2017-10-21 116 views
1

J'ai un modèle Django qui contient un enregistrement unique avec une date. Je compte actuellement les enregistrements dans des plages de jours, par ex. X Nombre ont déjà passé la date d'aujourd'hui, X arrivera dans les 10 prochains jours, X arrivera dans les 30 prochains jours. Le code ci-dessous est ce que j'utilise actuellement, il récupère toutes les valeurs d'une requête records.objects.all() par rapport au modèle, puis parcourt chaque objet pour calculer le delta datetime et incrémenter le compteur correspondant.Calcul performant de datetime diffs en jours

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 

Pour environ 50 000 dossiers cela prend environ 5-6 secondes, ce qui est plus que ce que je voudrais, je suis en train de réduire ce que le nombre d'enregistrements est susceptible d'augmenter. La question est vraiment autour du calcul performant des diffs datetime et le regroupement du nombre de jours résultant comme s'il y avait une meilleure méthode à travers une requête Django ou une autre méthode que je n'ai pas pu trouver je suis ouvert à l'essayer. J'ai exploré l'utilisation de DateAdd dans un SQL brut mais il me semblerait avoir besoin d'interroger la base de données pour chaque plage de dates et me conduirait quand même à devoir parcourir les résultats.

+0

S'il vous plaît préparer http://www.rextester.com démo avec des données d'échantillons et la sortie désirée. Je suis sûr qu'en SQL pur nous pourrions obtenir des temps inférieurs à 1 sec. – lad2025

+1

http://rextester.com/AWPX46055 - Pensez que j'ai bien fait. La sortie désirée est juste le retour des nombres par les groupes ci-dessus, passé, dans les dix jours, dans les trente jours. Je vois aussi un besoin futur pour quelque chose de plus de 30 jours. Les dates limites sont cumulatives. – Draineh

+1

Merci pour les données d'échantillon – lad2025

Répondre

1

Utilisation de SQL fenêtré 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

C'est parfait merci, sous temps de réponse sous deuxième maintenant – Draineh

1

Avant d'optimiser les performances, je considérerais l'exécution par lots. Votre plus petite fenêtre de changement semble être 1 jour. Donc, en filtrant sur un champ « mis à jour » dans le modèle d'enregistrement, vous pouvez appeler toutes les heures (par Cron) un:

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

et faites votre opération. Notez que vous pouvez limiter le nombre d'enregistrements récupérés. Ainsi, toutes les heures, 2083 (ou 5000) enregistrements seront traités en divisant la tâche au cours de la journée. Ce nombre peut être mis à l'échelle en fonction du nombre d'enregistrements dans la base de données (par exemple, 50000/24 ​​= 2083)

Votre migration peut également vouloir indiquer que vous souhaitez la définir longtemps dans le passé pour que chaque enregistrement en direct soit traité une fois au début.

+0

Vous avez raison que la plus petite fenêtre est 1 jour. En ce moment, je prends les résultats et je les présente directement aux utilisateurs (une partie du problème). Avec cette approche, je suppose que je pourrais mettre en cache le résultat et appeler cela chaque fois qu'un utilisateur visualise les chiffres plutôt que de les calculer à la volée – Draineh

+1

Oui, le point ici est, où vous vous trouvez en écrivant 'Model.objects.all()' vous devrait penser au filtrage, pour réduire le stress sur la base de données. Si vous pouvez trouver d'autres moyens de filtrage, c'est bien. PAR EXEMPLE. Lorsqu'un utilisateur effectue une action, mettez à jour tous les enregistrements associés à cet utilisateur. Le batch-every-hour est la solution exhaustive et évite les enregistrements oubliés. – rollinger