Django ORM LEFT JOIN на поля с одинаковыми значениями - PullRequest
0 голосов
/ 13 марта 2019

Я пишу веб-интерфейс для гидрологов. Гидролог должен увидеть таблицу с различными гидрологическими измерениями, подобными этой.

+----------------+----------------------+-------+--------------------+-------------+------------------+
| 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 с моделями без отношения внешнего ключа?

1 Ответ

0 голосов
/ 13 марта 2019

Вы можете достичь того, что вы хотите, но это будет излишне неэффективно (даже больше, чем отправленный вами 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))

Теперь попробуйте умножить количество полей на ожидаемое количество строк в вашем запросе - это количество подзапросов, которые необходимо выполнить.

Так что это доказательство концепции, которую вы можете использовать в крайнем случае для небольших таблиц и нескольких полей. Это не подходит для больших наборов данных и многих полей. Вы должны действительно переосмыслить свои модели для этого.

С правильными моделями должно быть действительно легко добиться того, что вам нужно.

...