Как мне представить сложные подзапросы и вычисляемые таблицы в Django ORM? - PullRequest
0 голосов
/ 24 апреля 2019

У меня есть следующий SQL, который в основном работает.Как бы я представлял это в Django ORM?Я хотел бы избежать запуска полного необработанного запроса

Я не уверен, что делать с подзапросом в Django ORM и как правильно выполнить декартово произведение (достигается с помощью CROSS JOIN)

SELECT datum,
       alldata.worker_id,
       reporting_plan.project_id,
       SUM(effort::float)/60/60
FROM
  (SELECT DISTINCT datum,
                   reporting_plan.worker_id AS worker_id
   FROM
     (SELECT datum::date
      FROM generate_series('2019-05-01', '2019-12-31', '1 day'::interval) datum) AS dates
   CROSS JOIN reporting_plan
   ORDER BY datum,
            worker_id) AS alldata
LEFT OUTER JOIN reporting_plan ON alldata.worker_id = reporting_plan.worker_id
AND datum <= reporting_plan.end
AND datum >= reporting_plan.start
GROUP BY datum,
         alldata.worker_id,
         reporting_plan.worker_id,
         reporting_plan.project_id
ORDER BY datum,
         alldata.worker_id,
         reporting_plan.worker_id,
         reporting_plan.project_id

Ожидаемый результат - это список со всеми датами на таймфрейме и всеми работниками и соответствующей информацией планирования (проекты и усилия).

Спасибо!

РЕДАКТИРОВАТЬ :

Основываясь на отзывах @jimjimjim, мне удалось удалить CROSS JOIN, но получить те же результаты:

SELECT datum::date,
       alldata.worker_id,
       reporting_plan.project_id,
       SUM(effort::float)/60/60
FROM
  (SELECT DISTINCT generate_series ('2019-05-01', '2019-12-31', '1 day'::interval) AS datum,
                   worker_id
   FROM reporting_plan
   ORDER BY datum,
            worker_id) AS alldata
LEFT OUTER JOIN reporting_plan ON alldata.worker_id = reporting_plan.worker_id
AND datum <= reporting_plan.end
AND datum >= reporting_plan.start
GROUP BY datum,
         alldata.worker_id,
         reporting_plan.worker_id,
         reporting_plan.project_id
ORDER BY datum,
         alldata.worker_id,
         reporting_plan.project_id

Ответы [ 2 ]

1 голос
/ 29 апреля 2019

Ваш вопрос здесь немного отличается от вопроса Reddit: при таком подходе вы будете делать необработанный запрос, но возвращать объекты ORM. Я сделаю следующее:

  1. Создать модель для запроса:
    class WorkerEffort(models.Model):
        date = models.DateField(...)
        worker = models.ForeignKey(Worker, db_column="worker_id")
        project = models.ForeignKey(Project, db_column="project_id")
        effort = models.DecimalField()

        class Meta:
            managed = False

управляемая информация здесь: https://docs.djangoproject.com/en/2.2/ref/models/options/#managed

  1. Выполните запрос raw к этой модели:
WorkerEffort.objects.raw('''
SELECT datum,
       alldata.worker_id,
       reporting_plan.project_id,
       SUM(effort::float)/60/60
FROM
  (SELECT DISTINCT datum,
                   worker_id
   FROM
     (select *, generate_series(start, end, '1 day'::interval) as datum from reporting_plan) 
     ) AS alldata
LEFT OUTER JOIN reporting_plan ON alldata.worker_id = reporting_plan.worker_id
GROUP BY datum,
         alldata.worker_id,
         reporting_plan.worker_id,
         reporting_plan.project_id
ORDER BY datum,
         alldata.worker_id,
         reporting_plan.worker_id,
         reporting_plan.project_id
''')

С помощью необработанного запроса вы сможете сопоставлять идентификаторы с объектами ORM через WorkerEffort (модель запроса).

0 голосов
/ 24 апреля 2019

Это дает вам тот же результат? Это исключает перекрестное объединение, что облегчает перезапись с использованием ORM, а поскольку ряды создаются с использованием reporting_plan.start и end, вам не нужно включать их в условие объединения. Если даты, указанные в вашем запросе, должны быть параметризованы, вы можете включить их в качестве аргументов в команду ORM, а не в сам запрос.

SELECT datum,
       alldata.worker_id,
       reporting_plan.project_id,
       SUM(effort::float)/60/60
FROM
  (SELECT DISTINCT datum,
                   worker_id
   FROM
     (select *, generate_series(start, end, '1 day'::interval) as datum from reporting_plan) 
     ) AS alldata
LEFT OUTER JOIN reporting_plan ON alldata.worker_id = reporting_plan.worker_id
GROUP BY datum,
         alldata.worker_id,
         reporting_plan.worker_id,
         reporting_plan.project_id
ORDER BY datum,
         alldata.worker_id,
         reporting_plan.worker_id,
         reporting_plan.project_id
...