写数据库查询时遇到问题

时间:2010-02-04 10:15:23

标签: python sql django

我有两个模型LocationEvent,它们由Event模型上的ForeignKey链接。模型分解如下:

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')

每个位置都有多个按start_date降序排序的事件。我正在尝试的查询检索每个位置的下一个即将发生的事件。

理想情况下,我想在单个查询中进行此操作(我不想为每个位置运行查询,因为这会导致对数据库进行大量不必要的命中)。我尝试过使用Django的ORM,我也尝试过使用原始SQL,但是我遇到了一些障碍。

非常感谢任何帮助。

更新

我想出了一个潜在的解决方案,尽管我不相信这是最好的方法。它的工作原理应该足够了,但我很想知道这样做的最佳方法是什么。

无论如何,我写的代码如下:

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)

4 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

“理想情况下,我想在单个查询中进行此操作(我不想为每个位置运行查询,因为这会导致对数据库进行大量不必要的命中)。”

这是一个错误的假设。

1)Django的ORM使用缓存。它可能不会像您想象的那样经常查询数据库。数据库有一个缓存。查询的成本可能不是您的想法。

2)你有select_related。 ORM可以为您进行联接。 http://docs.djangoproject.com/en/dev/ref/models/querysets/#id4

只需编写最简单的循环来获取位置和事件。在极不可能的情况下,这是您应用程序中最慢的部分(并且您可以证明它是最慢的),然后添加select_related并查看改进程度。

在您能够证明此特定查询正在查杀您的应用程序之前,请继续前进,不要担心“点击数据库”。

每个地点的下一个活动?

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

或者

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

将其返回到要渲染的模板。

在对基准进行基准测试并证明它是您应用程序的瓶颈之前,请不要忽视这一点。

答案 2 :(得分:0)

我认为您应该查看django aggregation link text,因此您的结果将按位置进行格式化,条件是过滤未来/过期事件,min(start_time)返回下一个事件时间

答案 3 :(得分:0)

以下内容可能有用:

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)

分享并享受。

相关问题