Как использовать функцию `lttb` от timescaledb с кверисетом django?
У меня есть проект django, который хранит данные нескольких устройств в реальном времени. Для этого я использовал timescaledb, которая подходит для временных рядов. 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))
Я могу получить результат этого запроса в виде набора raw query set с помощью:
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, пожалуйста, помогите мне написать модульный метод менеджера для этого? Например, я должен иметь возможность фильтровать или упорядочивать только то, что мне нужно.
Django или нет, но я полагаю, что вы можете вставлять его в строку. Давайте посмотрим на пример:
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()).*"
Проблема в том, что пользовательские функции требуют output_field, так как unnest возвращает 2 дополнительных столбца, я не знаю, поддерживает ли это Django ORM.