复杂的 GROUP BY 与 Django 的 ORM
Complex GROUP BY with Django's ORM
我有一个跟踪用电量的 Django 应用程序,我很难想出一种方法来使用 Django 的 ORM 来获取一些信息。
我的具体用例是这样的:我有一组用电量读数,每个读数都有一个日期时间字段、消耗量和成本(还有其他一些,但这些是相关的)。我需要将按月、年、电表和电价分组的消耗和成本值相加。换句话说,我需要能够获得每个月、每年、每个价格的总能耗值和相应的成本(如果你在 table 下看 [=41] 更容易理解=]).
这是我的 ElectricityReading
模型及其父模型 Reading
(分开是因为我们还有水和煤气的消耗读数,它们也来自 Reading
):
from model_utils.models import TimeStampedModel
# Other imports here...
class Reading(TimeStampedModel):
meter = models.ForeignKey(Meter)
datetime = models.DateTimeField() # Terrible property name, I know :)
class Meta:
abstract = True
class ElectricityReading(Reading):
price = models.ForeignKey(ElectricityPrice)
consumption = models.DecimalField(max_digits=18, decimal_places=3,
null=True, blank=True, default=None)
cost = models.DecimalField(max_digits=18, decimal_places=3, null=True,
blank=True, default=None)
现在我正在使用这个原始 SQL 来执行此操作,我根据一些参数构建它:
SELECT
(EXTRACT(YEAR FROM datetime)) AS reading_date_year,
(EXTRACT(MONTH FROM datetime)) AS reading_date_month,
SUM(consumption) as total_consumption,
SUM(cost) as total_cost,
COUNT(id) as num_readings,
price_id
FROM electricity_reading
WHERE meter_id IN (10)
AND datetime >= '2015-10-01 00:00'
AND datetime <= '2015-12-31 23:59'
GROUP BY reading_date_year, reading_date_month, price_id, meter_id
ORDER BY meter_id, reading_date_year, reading_date_month, price_id
此 SQL 查询产生类似以下数据的结果(由值和简化的列名组成,以便更好地格式化):
╔══════╦═══════╦═════════════╦══════╦══════════════╦═══════╗
║ year ║ month ║ consumption ║ cost ║ num_readings ║ price ║
╠══════╬═══════╬═════════════╬══════╬══════════════╬═══════╣
║ 2015 ║ 10 ║ 600 ║ 804 ║ 456 ║ 1 ║
║ 2015 ║ 10 ║ 728 ║ 471 ║ 1998 ║ 2 ║
║ 2015 ║ 10 ║ 848 ║ 792 ║ 1266 ║ 3 ║
║ 2015 ║ 10 ║ 256 ║ 705 ║ 744 ║ 5 ║
║ 2015 ║ 11 ║ 528 ║ 377 ║ 630 ║ 1 ║
║ 2015 ║ 11 ║ 016 ║ 687 ║ 1680 ║ 2 ║
║ 2015 ║ 11 ║ 240 ║ 826 ║ 1289 ║ 3 ║
║ 2015 ║ 11 ║ 736 ║ 522 ║ 720 ║ 5 ║
║ 2015 ║ 12 ║ 584 ║ 627 ║ 608 ║ 1 ║
║ 2015 ║ 12 ║ 776 ║ 078 ║ 1627 ║ 2 ║
║ 2015 ║ 12 ║ 600 ║ 401 ║ 1410 ║ 3 ║
║ 2015 ║ 12 ║ 864 ║ 842 ║ 744 ║ 5 ║
╚══════╩═══════╩═════════════╩══════╩══════════════╩═══════╝
使用 Django 的 ORM,我认为我需要的代码大致如下:
objs = ElectricityReading.objects\
.filter(
meter=10,
datetime__gte='2015-05-01 00:00',
datetime__lte='2015-08-31 23:59'
).only('price_id')\
.annotate(reading_date_year=YearTransform('datetime'))\
.annotate(reading_date_month=MonthTransform('datetime'))\
.annotate(total_consumption=Sum('consumption'))\
.annotate(total_cost=Sum('cost'))\
.annotate(num_readings=Count('id'))\
.order_by('meter_id', 'reading_date_year', 'reading_date_month', 'price_id')
但是它生成的SQL并不是我需要的:
SELECT
id,
price_id,
EXTRACT('year' FROM datetime AT TIME ZONE 'Europe/Lisbon') AS reading_date_year,
EXTRACT('month' FROM datetime AT TIME ZONE 'Europe/Lisbon') AS reading_date_month,
SUM(consumption) AS total_consumption, SUM(cost) AS total_cost,
COUNT(id) AS num_readings
FROM geratriz_electricityreading
WHERE (
datetime >= '2015-05-01 00:00:00+01:00'
AND datetime <= '2015-08-31 23:59:00+01:00'
AND meter_id = 10)
GROUP BY
id,
EXTRACT('year' FROM datetime AT TIME ZONE 'Europe/Lisbon'),
EXTRACT('month' FROM datetime AT TIME ZONE 'Europe/Lisbon')
ORDER BY meter_id ASC, reading_date_year ASC, reading_date_month ASC, price_id ASC
由于没有按照我的需要进行分组,这导致从数据库返回更多行。
我似乎无法用 Django 的 ORM 复制的 SQL 查询部分是末尾的 GROUP BY
子句。 Django 坚持按 ID 分组,我似乎无法找到一种方法使其按 meter_id 和 price_id.
分组
考虑到我已经在这上面花了多少时间,我倾向于说我试图完成的事情用 Django 的 ORM 根本不可能,但我希望有人会告诉我我遗漏了什么。
尝试使用 values()
objs = ElectricityReading.objects\
.filter(
meter=10,
datetime__gte='2015-05-01 00:00',
datetime__lte='2015-08-31 23:59'
.values('price_id')\
.annotate(reading_date_year=YearTransform('datetime'))\
.annotate(reading_date_month=MonthTransform('datetime'))\
.annotate(total_consumption=Sum('consumption'))\
.annotate(total_cost=Sum('cost'))\
.annotate(num_readings=Count('id'))\
.order_by('meter_id', 'reading_date_year', 'reading_date_month', 'price_id')
这应该对 price_id
上的结果进行分组。如果您一次显示几米而不是 meter=10
,那么您可以做 values('price_id', 'meter')
,它会在两个字段上分组。
我有一个跟踪用电量的 Django 应用程序,我很难想出一种方法来使用 Django 的 ORM 来获取一些信息。
我的具体用例是这样的:我有一组用电量读数,每个读数都有一个日期时间字段、消耗量和成本(还有其他一些,但这些是相关的)。我需要将按月、年、电表和电价分组的消耗和成本值相加。换句话说,我需要能够获得每个月、每年、每个价格的总能耗值和相应的成本(如果你在 table 下看 [=41] 更容易理解=]).
这是我的 ElectricityReading
模型及其父模型 Reading
(分开是因为我们还有水和煤气的消耗读数,它们也来自 Reading
):
from model_utils.models import TimeStampedModel
# Other imports here...
class Reading(TimeStampedModel):
meter = models.ForeignKey(Meter)
datetime = models.DateTimeField() # Terrible property name, I know :)
class Meta:
abstract = True
class ElectricityReading(Reading):
price = models.ForeignKey(ElectricityPrice)
consumption = models.DecimalField(max_digits=18, decimal_places=3,
null=True, blank=True, default=None)
cost = models.DecimalField(max_digits=18, decimal_places=3, null=True,
blank=True, default=None)
现在我正在使用这个原始 SQL 来执行此操作,我根据一些参数构建它:
SELECT
(EXTRACT(YEAR FROM datetime)) AS reading_date_year,
(EXTRACT(MONTH FROM datetime)) AS reading_date_month,
SUM(consumption) as total_consumption,
SUM(cost) as total_cost,
COUNT(id) as num_readings,
price_id
FROM electricity_reading
WHERE meter_id IN (10)
AND datetime >= '2015-10-01 00:00'
AND datetime <= '2015-12-31 23:59'
GROUP BY reading_date_year, reading_date_month, price_id, meter_id
ORDER BY meter_id, reading_date_year, reading_date_month, price_id
此 SQL 查询产生类似以下数据的结果(由值和简化的列名组成,以便更好地格式化):
╔══════╦═══════╦═════════════╦══════╦══════════════╦═══════╗ ║ year ║ month ║ consumption ║ cost ║ num_readings ║ price ║ ╠══════╬═══════╬═════════════╬══════╬══════════════╬═══════╣ ║ 2015 ║ 10 ║ 600 ║ 804 ║ 456 ║ 1 ║ ║ 2015 ║ 10 ║ 728 ║ 471 ║ 1998 ║ 2 ║ ║ 2015 ║ 10 ║ 848 ║ 792 ║ 1266 ║ 3 ║ ║ 2015 ║ 10 ║ 256 ║ 705 ║ 744 ║ 5 ║ ║ 2015 ║ 11 ║ 528 ║ 377 ║ 630 ║ 1 ║ ║ 2015 ║ 11 ║ 016 ║ 687 ║ 1680 ║ 2 ║ ║ 2015 ║ 11 ║ 240 ║ 826 ║ 1289 ║ 3 ║ ║ 2015 ║ 11 ║ 736 ║ 522 ║ 720 ║ 5 ║ ║ 2015 ║ 12 ║ 584 ║ 627 ║ 608 ║ 1 ║ ║ 2015 ║ 12 ║ 776 ║ 078 ║ 1627 ║ 2 ║ ║ 2015 ║ 12 ║ 600 ║ 401 ║ 1410 ║ 3 ║ ║ 2015 ║ 12 ║ 864 ║ 842 ║ 744 ║ 5 ║ ╚══════╩═══════╩═════════════╩══════╩══════════════╩═══════╝
使用 Django 的 ORM,我认为我需要的代码大致如下:
objs = ElectricityReading.objects\
.filter(
meter=10,
datetime__gte='2015-05-01 00:00',
datetime__lte='2015-08-31 23:59'
).only('price_id')\
.annotate(reading_date_year=YearTransform('datetime'))\
.annotate(reading_date_month=MonthTransform('datetime'))\
.annotate(total_consumption=Sum('consumption'))\
.annotate(total_cost=Sum('cost'))\
.annotate(num_readings=Count('id'))\
.order_by('meter_id', 'reading_date_year', 'reading_date_month', 'price_id')
但是它生成的SQL并不是我需要的:
SELECT
id,
price_id,
EXTRACT('year' FROM datetime AT TIME ZONE 'Europe/Lisbon') AS reading_date_year,
EXTRACT('month' FROM datetime AT TIME ZONE 'Europe/Lisbon') AS reading_date_month,
SUM(consumption) AS total_consumption, SUM(cost) AS total_cost,
COUNT(id) AS num_readings
FROM geratriz_electricityreading
WHERE (
datetime >= '2015-05-01 00:00:00+01:00'
AND datetime <= '2015-08-31 23:59:00+01:00'
AND meter_id = 10)
GROUP BY
id,
EXTRACT('year' FROM datetime AT TIME ZONE 'Europe/Lisbon'),
EXTRACT('month' FROM datetime AT TIME ZONE 'Europe/Lisbon')
ORDER BY meter_id ASC, reading_date_year ASC, reading_date_month ASC, price_id ASC
由于没有按照我的需要进行分组,这导致从数据库返回更多行。
我似乎无法用 Django 的 ORM 复制的 SQL 查询部分是末尾的 GROUP BY
子句。 Django 坚持按 ID 分组,我似乎无法找到一种方法使其按 meter_id 和 price_id.
考虑到我已经在这上面花了多少时间,我倾向于说我试图完成的事情用 Django 的 ORM 根本不可能,但我希望有人会告诉我我遗漏了什么。
尝试使用 values()
objs = ElectricityReading.objects\
.filter(
meter=10,
datetime__gte='2015-05-01 00:00',
datetime__lte='2015-08-31 23:59'
.values('price_id')\
.annotate(reading_date_year=YearTransform('datetime'))\
.annotate(reading_date_month=MonthTransform('datetime'))\
.annotate(total_consumption=Sum('consumption'))\
.annotate(total_cost=Sum('cost'))\
.annotate(num_readings=Count('id'))\
.order_by('meter_id', 'reading_date_year', 'reading_date_month', 'price_id')
这应该对 price_id
上的结果进行分组。如果您一次显示几米而不是 meter=10
,那么您可以做 values('price_id', 'meter')
,它会在两个字段上分组。