У меня есть проект django, в котором в режиме реального времени хранятся данные нескольких устройств. Для этого я использовал timescaledb , который подходит для временных рядов и основан на postgres. Timescale предлагает несколько гиперфункций , которые мне нужно использовать (в частности, lttb, которая используется для понижения дискретизации данных).
Например, это один из запросов, которых я хочу достичь:
SELECT time as timestamp, value as value, %s as device_id
FROM unnest((SELECT lttb(timestamp, value, %s)
FROM core_devicedata where device_id=%s and timestamp between %s and %s))
Я могу получить результат этого запроса в виде необработанного запроса, заданного:
for data in DeviceData.objects.raw(query):
...
Я уже пробовал необработанные SQL-запросы с использованием Django. Они работают. Дело в том, что они не предлагают функций фильтрации и упорядочивания, поскольку не возвращают фактический набор запросов. Вместо этого они возвращают необработанный набор запросов. Чего я пытаюсь достичь, так это запустить запрос, как показано ниже, используя только возможности djagno orm.
SELECT time as timestamp, value as value, %s as device_id
FROM unnest((SELECT lttb(timestamp, value, %s)
Какие-либо предложения? Если нет возможности сделать это с помощью самого django orm, помогите мне написать метод модульного менеджера для этого способа? Как будто я должен иметь возможность фильтровать или заказывать, только если захочу.





Джанго или нет, но я предполагаю, что вы можете отключить его встраивание. Давайте посмотрим пример:
select lttb(time,price,5)->unnest() from crypto_ticks where symbol = 'BTC/USD' and time > now() - interval '1 month';
?column?
------------------------------------
("2024-04-09 13:23:29+00",70431)
("2024-04-17 16:08:07+00",59714.9)
("2024-04-22 23:15:59+00",67228.5)
("2024-05-01 08:24:03+00",56534.5)
("2024-05-07 14:37:03+00",63190.8)
(5 rows)
Теперь давайте добавим символ и сгруппируем его.
tsdb=> select symbol, lttb(time,price,5)->unnest() from crypto_ticks where symbol = 'BTC/USD' and time > now() - interval '1 month' group by 1;
symbol | ?column?
---------+------------------------------------
BTC/USD | ("2024-04-09 13:23:29+00",70431)
BTC/USD | ("2024-04-17 16:08:07+00",59714.9)
BTC/USD | ("2024-04-22 23:15:59+00",67228.5)
BTC/USD | ("2024-05-01 08:24:03+00",56534.5)
BTC/USD | ("2024-05-07 14:37:38+00",63266.6)
(5 rows)
Теперь давайте отменим вложение значений с помощью ().*
tsdb=> select symbol, (lttb(time,price,5)->unnest()).* from crypto_ticks where symbol = 'BTC/USD' and time > now() - interval '1 month' group by 1;
symbol | time | value
---------+------------------------+---------
BTC/USD | 2024-04-09 13:23:29+00 | 70431
BTC/USD | 2024-04-17 16:08:07+00 | 59714.9
BTC/USD | 2024-04-22 23:15:59+00 | 67228.5
BTC/USD | 2024-05-01 08:24:03+00 | 56534.5
BTC/USD | 2024-05-07 14:37:38+00 | 63266.6
Мой первый совет: ORM поможет вам только до определенного момента. Когда ситуация начинает усложняться, лучше всего пойти по пути необработанных запросов, иначе вы потратите больше времени на борьбу с ORM, чем на решение реальных бизнес-задач.
Вы можете добавить фильтрацию и упорядочение к необработанному запросу с помощью предложений WHERE и ORDER BY.
Вы можете писать собственные функции ORM SQL с помощью выражений Func() . Вы можете увидеть несколько примеров функций timescaledb, написанных для Django, в данном случае time_bucket, в репозитории библиотеки django-timescale.
Вы можете определить функцию примерно так (это не работает):
class LTTB(Func):
arity = 3
function = "lttb"
template = "(%(function)s(%(expressions)s)->unnest()).*"
Проблема в том, что пользовательские функции требуют выходного_поля, поскольку unnest возвращает 2 дополнительных столбца, я не знаю, поддерживает ли это Django ORM.
Спасибо за Ваш ответ. Тогда я продолжу необработанный sql.
Хотя это не совсем то, что я ищу, но это действительно полезный ответ для изучения новых вещей. Спасибо.