【问题标题】:Trouble converting SQL query to django无法将 SQL 查询转换为 django
【发布时间】:2023-03-15 11:07:01
【问题描述】:

我正在尝试将此 SQL 查询(在 mySQL 中完美运行)转换为 Django ORM,以便它可以在 PostgreSQL 环境中使用(因为 SQL 查询对 PG 无效)。我尝试了几种使用聚合和注释的不同方法,但我就是想不通。

我正在尝试做的是计算每种类型的条目数(总共有 3 种类型),每个月,当前日期两侧的 6 个月!

有效的 SQL 查询是

    select count(c.id) as Quantity, date_format(expiry, '%Y-%m') as Expiry, type from certificate as c
join type as t on t.id = c.type_id
where expiry >= date_format(date_sub(now(), interval 6 month), '%Y-%m-%d')
and expiry <= date_format(date_add(now(), interval 6 month), '%Y-%m-%d')
group by month(expiry), type
order by expiry asc

我将 'date_format' 更改为 postgres 'to_char' 但我似乎无法按 'month(expiry)' 分组,而 ORM 解决方案似乎是更好的方法。

主要的证书模型如下:

class Certificate(models.Model):
    epc_rating = models.IntegerField(null=True, db_index=False)
    building_area = models.IntegerField(db_index=False)
    building_emissions = models.DecimalField(max_digits=6, decimal_places=2, db_index=False)
    energy_usage = models.DecimalField(max_digits=6, decimal_places=2, db_index=False)
    refrig_weight = models.IntegerField(db_index=False)
    ac_output = models.IntegerField(db_index=False)
    annual_heating = models.CharField(blank=True, null=True, max_length=50, db_index=False)
    renewable_heating = models.CharField(blank=True, null=True, max_length=50, db_index=False)
    annual_electric = models.CharField(blank=True, null=True, max_length=50, db_index=False)
    typical_heating = models.CharField(blank=True, null=True, max_length=50, db_index=False)
    typical_electric = models.CharField(blank=True, null=True, max_length=50, db_index=False)
    renewable_electric = models.CharField(blank=True, null=True, max_length=50, db_index=False)

    expiry = models.DateField(db_index=True) # This is the expiry date used

    manager = models.CharField(blank=True, null=True, max_length=100, db_index=False)
    rrn = models.ForeignKey(RRN, on_delete=models.CASCADE)
    type = models.ForeignKey(Type, on_delete=models.CASCADE)
    postcode = models.ForeignKey(Postcode, on_delete=models.CASCADE)
    site = models.ForeignKey(Site, on_delete=models.CASCADE)
    heating = models.ForeignKey(Heating, on_delete=models.CASCADE)
    complexity = models.ForeignKey(Complexity, on_delete=models.CASCADE)
    employer = models.ForeignKey(Employer, on_delete=models.CASCADE)
    environment = models.ForeignKey(Environment, on_delete=models.CASCADE)
    assessor = models.ForeignKey(Assessor, on_delete=models.CASCADE)
    scheme = models.ForeignKey(Scheme, on_delete=models.CASCADE)

    class Meta:
        managed = True
        db_table = 'certificate'

并且该表有一个指向“类型”表的外键链接,其模型如下:

class Type(models.Model):
    type = models.CharField(blank=True, null=True, max_length=25, db_index=True)

    class Meta:
        managed = True
        db_table = 'type'

类型表中有 3 种“类型”(我知道,命名约定令人困惑),它们是“EPC”、“TM44”和“DEC”。正如我所说,我正在尝试计算一年中每个月内每种类型的数量?

任何帮助将不胜感激,我已经尝试转换它大约 2-3 周,最终决定寻求帮助。提前致谢。

编辑 所以我应该添加我尝试过的内容......我不太了解在这个查询中我可以在哪里使用聚合函数,但我管理的注释:

currentDate = datetime.now().date()
prev_half_year = currentDate - relativedelta(months=6)
next_half_year = currentDate + relativedelta(months=6)
expiryObj = Certificate.objects.filter(expiry__range=[prev_half_year, next_half_year]).annotate(epc_count=Count('id', filter=Q(type__type='EPC'))).count()

我在最后添加了返回整数 151 的计数(这可能是正确的,我不能确定,因为这是我需要弄清楚的查询,以便我可以检查)。问题是我不确定这是否是全年的正确数字,而且我看不到如何添加 group_by 语句来获取各个月份。如果我将它添加到末尾,例如 count().group_by('expiry')”,我会得到“AttributeError:'int' object has no attribute 'group_by'”,如果我在计数之前添加它,我会得到“AttributeError:' QuerySet' 对象没有属性 'group_by'"!此外,SQL 查询能够按 Month(expiry) 分组,但我还没有找到任何与如何将其转换为 Django ORM 相关的文档。

进一步编辑 感谢 'iklinac' 提供一些关于在哪里寻找的提示。我已经设法使用此查询进一步得到结果:

results = Certificate.objects.filter(expiry__range=[prev_half_year, next_half_year]).values_list('expiry__year', 'expiry__month').annotate(Sum('type_id')).order_by('expiry__year')

这将返回一个查询集---- --- 这似乎与我正在寻找的内容接近,因为搜索的月份是正确的,但总和值(第 3 个条目tuple) 似乎与任何事物无关?我在网站上手动执行了 2020 年 6 月的查询(返回的查询中有 30 个条目)但是,该站点仅返回 8 个条目? Picture of Website Manual Query Returned Values Differ From ORM Returned Values

【问题讨论】:

标签: python sql django postgresql


【解决方案1】:

你几乎明白了。只需要稍作改动。 而不是Sum('type_id') 使用Count('type_id') 来获取计数

results = Certificate.objects.filter(
    expiry__range=[prev_half_year, next_half_year]).values_list(
        'expiry__year', 'expiry__month').annotate(Count('type_id')).order_by('expiry__year')

【讨论】:

  • 感谢您的回复...不幸的是,这仍然返回相同的元组 --> (2020, 6, 3603) -- 它正确计算了当月到期的记录总数,但我只能'不知道如何将元组的最后一个元素分成单独的类型 ID(应该有 3 个)。为尝试干杯。
  • 啊哈...但是您确实让我做了一些事情...更改 values_list 以包含 'values_list('expiry__year', 'expiry__month', 'type__type')' 与您的 Count 添加解决了它.非常感谢,这已经困扰了我好几个星期了。
猜你喜欢
  • 1970-01-01
  • 2016-04-20
  • 1970-01-01
  • 1970-01-01
  • 2020-09-12
  • 2020-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多