【问题标题】:Django ORM LEFT JOIN on fields with same values具有相同值的字段上的 Django ORM LEFT JOIN
【发布时间】:2019-08-03 18:09:39
【问题描述】:

我正在为水文学家编写网络界面。水文学家应该像这样查看具有不同水文测量值的表格。

+----------------+----------------------+-------+--------------------+-------------+------------------+
| observation_id | observation_datetime | level | water_temperature  |precipitation|precipitation_type|
+----------------+----------------------+-------+--------------------+-------------+------------------+

| 1 | 2019-03-11 11:00:00 | 11 | 21 | 31 |
| 2 | 2019-03-12 12:00:00 | 12 | 22 | 32 |
| 3 | 2019-03-13 13:00:00 | 13 | 23 | 33 |
| 4 | 2019-03-14 14:00:00 | 14 | 24 | 34 |

我有这些模型来描述测量结果

class AbstractMeasurement(model.Model):
    observation_datetime = models.DateTimeField()
    observation = models.ForeignKey(Observation, on_delete = models.DO_NOTHING)

class Meta:
    abstract = True

class Level(AbstractMeasurement):
    level = models.DecimalField()

class WaterTemperature(AbstractMeasurement):
    air_temperature = models.DecimalField()

class Precipitation(AbstractMeasurement):
    precipitation = models.DecimalField()
    precipitation_type = models.CharField()

等等

水平主要测量和测量不能没有水平。级别是基本模型。

在mysql中我可以通过这个查询来完成

    SELECT level.observation_id, 
            level.observation_datetime, 
            level.level, 
            water_temperature.water_temperature, 
            precipitation.precipitation, 
            precipitation.precipitation_type 
    FROM level 
    LEFT JOIN precipitation ON 
            level.observation_datetime = precipitation.observation_datetime 
            AND 
            level.observation_id = precipitation.observation_id 
    LEFT JOIN water_temperature ON 
            level.observation_datetime = water_temperature.observation_datetime 
            AND 
            level.observation_id = water_temperature.observation_id;

如何在 django 中使用没有外键关系的模型进行 LEFT JOIN?

【问题讨论】:

  • 你能在没有 FK 的情况下使用任何数据库吗?
  • 你能详细说明ObservationMeasurement的区别以及两者之间的关系吗?
  • 一旦你的数据结构清晰了,在 Django 中找到表现良好的表示应该不难。
  • Observation 包含有关水文站和观察者的信息。测量是水位、水温等。我将更改我的模型,将所有测量值合并到Measurement 模型中

标签: django django-models django-orm


【解决方案1】:

你可以实现你想要的,但它会不必要地低效(甚至比你发布的 SQL 查询还要低)。由于您当前的模型结构无论如何都相当扭曲,如果您可以更改模型,您应该这样做。

也就是说,这里是如何带来例如Precipitation 数据到您的 Level 查询中。每个字段和行需要一个子查询:

from django.db.models import Q, OuterRef, Subquery

join_criteria = Q(
    observation_id=OuterRef('observation_id'), 
    observation_datetime = OuterRef('observation_datetime')
)

subquery_precipitation = Subquery(Precipitation.objects
    .filter(join_criteria)
    .values('precipitation')[:1])

subquery_precipitation_type = Subquery(Precipitation.objects
    .filter(join_criteria)
    .values('precipitation_type')[:1])

levels = (Level.objects
        .annotate(precipitation=subquery_precipitation)
        .annotate(precipitation_type=subquery_precipitation_type))

现在尝试将查询中的字段数与预期的行数相乘——这就是需要执行的子查询数。

所以这是一个概念证明,您可以在紧要关头使用小表格和一些字段。它不适用于大型数据集和许多领域。你真的应该为此重新考虑你的模型。

有了合适的模型,应该很容易实现你所需要的。

【讨论】:

  • 我试过你的代码,但结果与 SQL 不同。我在工作中在另一个数据库中看到的数据库结构,所以我创建了这种模型。您说,对于大型数据集,不适合使用字段较少的表。有没有与数据库结构相关的文章或最佳实践
  • 有什么区别?
  • 你是对的,加入标准是错误的。我将id 更改为observation_id。当您在 Django 模型中明确定义了您的关系时,您不必注意这种错误 :)。
  • 由 django 查询和 sql 生成的列值降水、降水类型、水温不同。在 Outref 中,我尝试同时放置 'id' 和 'observation_id'
  • observation_id=OuterRef('observation_id') 应该可以工作。
猜你喜欢
  • 2015-08-21
  • 2020-11-22
  • 1970-01-01
  • 2015-07-05
  • 2015-08-21
  • 2016-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多