【问题标题】:Problem writing a database query编写数据库查询的问题
【发布时间】:2010-02-04 10:15:23
【问题描述】:

我有两个模型,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)

【问题讨论】:

  • 发布您的尝试代码和原始 SQL 以进行比较
  • @Gary - 我现在没有时间,但这在 Django 的 ORM 中绝对是可能的 - 不要为此投入原始 SQL。如果没有人帮助您,我会尝试稍后发布答案。

标签: python sql django


【解决方案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】:
    select id, (
        select * from event 
        where location=location.id 
        and start_date>NOW() 
        order by start_date asc 
        limit 1
        )
     from location
    

    【讨论】:

      【解决方案3】:

      我认为您应该查看 django aggregationlink text,因此您的结果将按位置汇总,并带有过滤未来/过期事件的条件,并且 min(start_time) 返回下一个事件时间

      【讨论】:

        【解决方案4】:

        以下内容可能有用:

        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)
        

        分享和享受。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多