2010-02-04 6 views
2

J'ai deux modèles, Location et Event, qui sont liés par ForeignKey sur le modèle Event. Les modèles se décomposent comme suit:Problème lors de l'écriture d'une requête de base de données

class Location(models.Model): 
    city = models.CharField('city', max_length=25) 
    slug = models.SlugField('slug') 

class Event(models.Model): 
    location = models.ForeignKey(Location) 
    title = models.CharField('title', max_length=75) 
    start_date = models.DateTimeField('start_date') 
    end_date = models.DateTimeField('end_date') 

Chaque emplacement a plusieurs événements qui sont commandés par start_date descendant. La requête que j'essaie de faire récupère le prochain événement à venir pour chacun des emplacements.

Idéalement, je voudrais faire cela en une seule requête (je ne veux pas exécuter une requête pour chaque emplacement, car cela causerait beaucoup de hits inutiles à la base de données). J'ai essayé d'utiliser l'ORM de Django, et j'ai aussi essayé d'utiliser du SQL brut, mais j'ai rencontré un peu de blocage.

Toute aide serait grandement appréciée.

Mise à jour

Je suis venu avec une solution potentielle, bien que je ne suis pas convaincu que c'est la meilleure méthode. Cela fonctionne, ce qui devrait suffire, mais je suis curieux de voir quelle serait la meilleure façon de le faire.

Quoi qu'il en soit, le code que j'ai écrit lit ainsi:

l = Location.objects.select_related() 
qs = None 

# Concatenate the related event querysets into a new queryset 
for e in l: 
    if qs is None: 
     qs = e.event_set.all() 
    else: 
     qs = qs | e.event_set.all() 

# Order a slice of the queryset by start_date ascending 
qs = sorted(qs[:l.count()], key=lambda s: s.start_date) 
+1

Post le code de votre essai et le SQL brut pour comparer – Pentium10

+0

@Gary - Je n'ai pas le temps en ce moment, mais c'est certainement, certainement possible dans l'ORM de Django - ne pas tomber dans le SQL brut pour cela. Je vais essayer de poster une réponse plus tard si personne ne t'a aidé. –

Répondre

1

« Idéalement je voudrais faire cela dans une seule requête (je ne veux pas exécuter une requête pour chaque emplacement, car cela causerait beaucoup de coups inutiles à la base de données). "

Ceci est une fausse hypothèse.

1) L'ORM de Django utilise un cache. Il ne peut pas interroger la base de données aussi souvent que vous le pensez. Les bases de données ont un cache. Le coût d'une requête n'est peut-être pas ce que vous pensez.

2) Vous avez select_related. L'ORM peut faire la jointure pour vous. Il suffit d'écrire la boucle la plus simple possible pour aller chercher des emplacements et des événements.

Dans le cas très improbable que c'est la partie la plus lente de votre application (et vous pouvez prouver qu'il est le plus lent), puis ajouter select_related et voir combien cela améliore les choses. Jusqu'à ce que vous puissiez prouver que cette requête spécifique est en train de tuer votre application, continuez et ne vous inquiétez pas des "hits à la base de données".

Prochain événement dans chaque emplacement?

[ l.event_set.order_by(start_date).all()[0] for l in Location.objects.select_related().all() ] 

Ou peut-être

events = [] 
for l in Location.objects.select_related().all(): 
    events.append(l.event_set.order_by(start_date).all()[0]) 

Et que revenir au modèle à rendre. Ne pas rejeter ceci jusqu'à ce que vous l'ayez comparé et prouvé que c'est le goulot d'étranglement dans votre application.

0

Je pense que vous devriez regarder l'agrégation django link text, de sorte que votre résultat sera agragated par emplacement avec une condition pour filtrer les événements futurs/expirés et min (start_time) retourne la prochaine fois que l'événement

1
select id, (
    select * from event 
    where location=location.id 
    and start_date>NOW() 
    order by start_date asc 
    limit 1 
    ) 
from location 
0

Quelque chose comme ce qui suit peut être utile:

SELECT * FROM EVENTS V 
    WHERE (V.LOCATION, V.START_DATE) IN 
    (SELECT E.LOCATION, MIN(E.START_DATE) 
     FROM EVENTS E 
     WHERE E.START_DATE >= NOW 
     GROUP BY E.LOCATION) 

Partager et profiter.

Questions connexes