我的postgres数据库表看起来(简化)如下:timeframe :: timestamp,value :: integer。
我有一个分组查询(分组时间范围:1小时,1天,一周等),我有不同的聚合函数。
示例查询在sql中显示如下:
SELECT
date_trunc(%s, timeframe),
SUM(CASE WHEN metric = 'visitors' THEN value ELSE 0 END) / NULLIF(SUM(CASE WHEN metric = 'total' THEN value ELSE 0 END), 0) as value
FROM aggregation_metrichour
GROUP BY date_trunc(%s, timeframe)
我可以使用原始查询,但我需要orm动态过滤特定日期范围,其他一些信息并进行授权。
我的查询中唯一会改变的部分是价值是计算机的方式,我在这里使用不同的东西,如产品总和,平均值和总和。
我试图用orm构建它,但失败了。
我想要做的是在我的django查询集中添加一个自定义选择列,其中包括一些带有聚合函数的奇特原始计算。 对我来说最好的方法是只计算一个字符串并将其添加到查询集中。
这是我到目前为止所做的:
result = MetricHour.objects \
.extra(select={'date': 'date_trunc(%s, timeframe)'}, select_params=[interval]) \
.values('date') \
.annotate(value=Sum('value'))
print 'simple', result.query
# simple SELECT (date_trunc(day, timeframe)) AS "date",
# SUM("aggregation_metrichour"."value") AS "value" FROM
# "aggregation_metrichour" GROUP BY (date_trunc(day, timeframe))
result = MetricHour.objects \
.extra(select={'date': 'date_trunc(%s, timeframe)'}, select_params=[interval]) \
.extra(select={'value': "SUM(CASE WHEN metric = 'visitors' THEN value ELSE 0 END) / NULLIF (SUM(value), 0)"}) \
.values('date', 'value')
print 'extra-select', result.query # wont add a group by
# extra-select SELECT (date_trunc(day, timeframe)) AS "date",
# (SUM(CASE WHEN metric = 'visitors' THEN value ELSE 0 END) /
# NULLIF (SUM(value), 0)) AS "value" FROM "aggregation_metrichour"
然后我找到this stackoverflow question关于如何编写自己的聚合函数。但是我认为django中的代码改变了。我现在需要在VisitorRate(models.Aggregate)中设置一个名称作为字符串,我不知道如何添加新的自定义类型
class VisitorRateSql(models.sql.aggregates.Sum):
sql_template = "SUM(CASE WHEN metric = visitors' THEN value ELSE 0 END) / NULLIF (SUM(value), 0)"
class VisitorRate(models.Sum):
name = 'Sum'
sql = VisitorRateSql
def add_to_query(self, query, alias, col, source, is_summary):
aggregate = VisitorRateSql(col,
source=source,
is_summary=is_summary,
**self.extra)
query.aggregates[alias] = aggregate
result = MetricHour.objects \
.extra(select={'date': 'date_trunc(%s, timeframe)'}, select_params=[interval]) \
.values('date') \
.annotate(value=VisitorRate('value'))
print "Annotate Class", result.query
# Annotate Class SELECT (date_trunc(day, timeframe)) AS "date",
# SUM(CASE WHEN metric = visitors' THEN value ELSE 0 END) /
# NULLIF (SUM(value), 0) AS "value" FROM "aggregation_metrichour"
# GROUP BY (date_trunc(day, timeframe))
更新:我没有完全理解models.Aggregate函数 这看起来更好。但是我确实需要为我的用例简化这个。我想将sql_template直接提供给注释函数
我想知道如何轻松地为我的分组查询添加新的自定义选择查询! 任何帮助表示赞赏!
答案 0 :(得分:2)
我找到了一个很好的解决方案,其参数化类可用作聚合函数:
def custom_aggregation(select_query):
class SqlAggregate(models.sql.aggregates.Aggregate):
sql_function = ''
sql_template = select_query
class VisitorRate(models.Aggregate):
sql = SqlAggregate
def add_to_query(self, query, alias, col, source, is_summary):
aggregate = self.sql(col,
source=source,
is_summary=is_summary,
**self.extra)
query.aggregates[alias] = aggregate
return VisitorRate
aggregate_query = "SUM(CASE WHEN metric = visitors' THEN value ELSE 0 END) / NULLIF (SUM(value), 0)"
AggregationFunction = custom_aggregation(aggregate_query)
result = MetricHour.objects \
.extra(select={'date': 'date_trunc(%s, timeframe)'}, select_params=[interval]) \
.values('date') \
.annotate(value=AggregationFunction('value'))