在专门的查询中设置group_by

时间:2014-10-16 20:17:29

标签: sql django sqlite group-by django-views

我需要使用平均值执行数据平滑,使用即时创建的非标准group_by变量。我的模型由两个表组成:

class WthrStn(models.Model):
  name=models.CharField(max_length=64, error_messages=MOD_ERR_MSGS)
  owner_email=models.EmailField('Contact email')
  location_city=models.CharField(max_length=32, blank=True)
  location_state=models.CharField(max_length=32, blank=True)
  ...

class WthrData(models.Model):
  stn=models.ForeignKey(WthrStn)
  date=models.DateField()
  time=models.TimeField()
  temptr_out=models.DecimalField(max_digits=5, decimal_places=2)
  temptr_in=models.DecimalField(max_digits=5, decimal_places=2)

  class Meta:
    ordering = ['-date','-time']
    unique_together = (("date", "time", "stn"),)

WthrData表中的数据是以可变时间增量从xml文件输入的,目前为15或30分钟,但这可能会随着时间的推移而变化。该表中有> 20000条记录。我想提供一个选项来显示平滑到可变时间单位的数据,例如30分钟,1小时,2小时或N小时(60,120,180等分钟)

我使用SQLIte3作为数据库引擎。我测试了下面的sql,证明它非常适合在N分钟持续时间内执行“bins”平滑:

select id, date, time, 24*60*julianday(datetime(date || time))/N jsec, avg(temptr_out)
as temptr_out, avg(temptr_in) as temptr_in, avg(barom_mmhg) as barom_mmhg,
avg(wind_mph) as wind_mph, avg(wind_dir) as wind_dir, avg(humid_pct) as humid_pct,
avg(rain_in) as rain_in, avg(rain_rate) as rain_rate,
datetime(avg(julianday(datetime(date || time)))) as avg_date from wthr_wthrdata where
stn_id=19 group by round(jsec,0) order by stn_id,date,time;

注意我使用SQLite3函数'julianday'创建一个输出变量'jsec',它返回整数部分的天数和小数部分的天数。因此,乘以24 * 60可得到分钟数。除以N分钟的分辨率为我提供了一个很好的“分组”变量,可以补偿原始数据的不同时间增量。

如何在Django中实现这一点?我已经尝试了objects.raw(),但它返回一个RawQuerySet,而不是ViewSet的ViewSet,所以我从html模板中获取错误消息:

  </p>
    Number of data entries: {{ valid_form|length }}
  </p>

我尝试使用标准查询,代码如下:

wthrdta=WthrData.objects.all()
wthrdta.extra(select={'jsec':'24*60*julianday(datetime(date || time))/{}'.format(n)})
wthrdta.extra(select = {'temptr_out':'avg(temptr_out)',
  'temptr_in':'avg(temptr_in)',
  'barom_mmhg':'avg(barom_mmhg)',
  'wind_mph':'avg(wind_mph)',
  'wind_dir':'avg(wind_dir)',
  'humid_pct':'avg(humid_pct)',
  'rain_in':'avg(rain_in)',
  'rain_sum_in':'sum(rain_in)',
  'rain_rate':'avg(rain_rate)',
  'avg_date':'datetime(avg(julianday(datetime(date || time))))'})

请注意,这里我使用的是sql-avg函数,而不是使用django aggregate()或annotate()。这似乎生成了正确的sql代码,但我似乎无法将group_by正确设置为我在顶部创建的jsec数据。

有关如何处理此问题的任何建议?我真正需要的是让QuerySet.raw()方法返回一个QuerySet,或者可以转换为QuerySet而不是RawQuerySet的东西。我找不到一个简单的方法来做到这一点。

1 个答案:

答案 0 :(得分:0)

使用我发现的提示,答案很简单 [https://gist.github.com/carymrobbins/8477219][1]

虽然我稍微修改了他的代码。要从RawQuerySet返回QuerySet,我所做的就是添加到我的models.py文件中,就在WthrData类定义的正上方:

class MyManager(models.Manager):
  def raw_as_qs(self, raw_query, params=()):
    """Execute a raw query and return a QuerySet. The first column in the
    result set must be the id field for the model.
    :type raw_query: str | unicode
    :type params: tuple[T] | dict[str | unicode, T]
    :rtype: django.db.models.query.QuerySet
    """
    cursor = connection.cursor()
    try:
      cursor.execute(raw_query, params)
      return self.filter(id__in=(x[0] for x in cursor))
    finally:
      cursor.close()

然后在我的WthrData类定义中:

class WthrData(models.Model):
  objects=MyManager()
  ......

以及稍后的WthrData类:

  def get_smoothWthrData(stn_id,n):
    sqlcode='select id, date, time, 24*60*julianday(datetime(date || time))/%s jsec, avg(temptr_out) as temptr_out, avg(temptr_in) as temptr_in, avg(barom_mmhg) as barom_mmhg, avg(wind_mph) as wind_mph, avg(wind_dir) as wind_dir, avg(humid_pct) as humid_pct, avg(rain_in) as rain_in, avg(rain_rate) as rain_rate, datetime(avg(julianday(datetime(date || time)))) as avg_date from wthr_wthrdata where stn_id=%s group by round(jsec,0) order by stn_id,date,time;'
    return WthrData.objects.raw_as_qs(sqlcode,[n,stn_id]);

这允许我从经过时间增量平滑的高度填充的WthrData表中获取结果,结果将作为QuerySet而不是RawQuerySet返回

相关问题