Django Окно аннотации с использованием в сочетании с отдельным предложением - PullRequest
6 голосов
/ 15 апреля 2020

У меня есть Django модель, хранящаяся в Postgres DB, состоящей из значений счетчиков с нерегулярными интервалами:

WidgetCount
 - Time
 - Count

Я пытаюсь использовать оконную функцию с Lag, чтобы дать мне значения предыдущей строки в качестве аннотации. Моя проблема в том, что когда я пытаюсь объединить это с каким-то определенным усечением даты, оконная функция использует исходные строки, а не четко сгруппированные.

Например, если у меня есть следующие строки:

time                count
2020-01-20 05:00    15
2020-01-20 06:00    20
2020-01-20 09:00    30
2020-01-21 06:00    35
2020-01-21 07:00    40
2020-01-22 04:00    50
2020-01-22 06:00    54
2020-01-22 09:00    58

И я хочу вернуть набор запросов, показывающий первое чтение в день, я могу использовать:

from django.db.models.functions import Trunc

WidgetCount.objects.distinct("date").annotate(date=Trunc("time", "day"))

, что дает мне:

date        count
01/01/20    15
01/01/21    35
01/01/22    50

Я хотел бы добавить аннотацию, которая дает мне вчерашнее значение (чтобы я мог показать изменение за день).

date        count   yesterday_count
01/01/20    15
01/01/21    35      15
01/01/22    50      35

Если я сделаю:

from django.db.models.functions import Trunc, Lag
from django.db.models import Window

WidgetCount.objects.distinct("date").annotate(date=Trunc("time", "day"), yesterday_count=Window(expression=Lag("count")))

Возвращение во второй строке дает мне 30 для вчерашнего дня - ie , он показывает мне предыдущую строку перед применением отдельного предложения.

Если я добавлю предложение части следующим образом:

WidgetCount.objects.distinct("date").annotate(date=Trunc("time", "day"), yesterday_count=Window(expression=Lag("count"), partition_by=F("date")))

Тогда вчера значение_счета равно None для всех строк.

Я могу сделать этот расчет в Python, если мне нужно, но это сводит меня с ума, и я хотел бы выяснить, возможно ли то, что я пытаюсь сделать.

Спасибо!

Ответы [ 2 ]

4 голосов
/ 27 апреля 2020

Я думаю, что основная проблема в том, что вы смешиваете операции, которые используются в аннотации, генерирует сгруппированный набор запросов, такой как sum, с помощью операции, которая упрощает создание нового поля для каждой записи в данном наборе запросов, например yesterday_count=Window(expression=Lag("count")).

Так что заказ здесь действительно имеет значение. Поэтому, когда вы пытаетесь:

WidgetCount.objects.distinct("date").annotate(date=Trunc("time", "day"), yesterday_count=Window(expression=Lag("count")))

Результирующий набор запросов просто аннотируется WidgetCount.objects.distinct ("date"), группировка не выполняется.

Я бы предложил разделить ваши операции, чтобы вам было легче понять, что происходит, и обратите внимание, что вы перебираете объект python, поэтому вам не нужно делать никаких новых запросов!

Обратите внимание на использование операции SUM в качестве примера, поскольку я получаю непредвиденную ошибку с оператором FirstValue. Поэтому я пишу с Сум, чтобы продемонстрировать идею, которая остается той же. Идея должна быть такой же для первого значения, просто изменив acc_count=Sum("count") на first_count=FirstValue("count")

for truncDate_groups in Row.objects.annotate(trunc_date=Trunc('time','day')).values("trunc_date")\
                      .annotate(acc_count=Sum("count")).values("acc_count","trunc_date")\
                      .order_by('trunc_date')\
                      .annotate(y_count=Window(Lag("acc_count")))\
                      .values("trunc_date","acc_count","y_count"):
    print(truncDate_groups)

ВЫХОД:

{'trunc_date': datetime.datetime(2020, 1, 20, 0, 0, tzinfo=<UTC>), 'acc_count': 65, 'y_count': None}
{'trunc_date': datetime.datetime(2020, 1, 21, 0, 0, tzinfo=<UTC>), 'acc_count': 75, 'y_count': 162}
{'trunc_date': datetime.datetime(2020, 1, 22, 0, 0, tzinfo=<UTC>), 'acc_count': 162, 'y_count': 65}

Оказывается, оператору FirstValue требуется использовать функцию Windows, чтобы вы не могли вкладывать FirtValue и затем вычислять Lag, поэтому в этом сценарии я не совсем уверен, сможете ли вы это сделать. Возникает вопрос, как получить доступ к столбцу First_Value без вложенности windows.

0 голосов
/ 22 апреля 2020

Я не проверял это локально, но я думаю, что вы хотите GROUP BY вместо использования DISTINCT здесь.

WidgetCount.objects.values(
    date=Trunc('time', 'day'),
).order_by('date').annotate(
    date_count=Sum('count'),  # Will trigger a GROUP BY date
).annotate(
    yesterday_count=Window(Lag('date_count')),
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...