I am having a query that fetches the latest price and a price 24h before multiple assets in TimescaleDb. Then my application proceeds to calculate % of the price change on the application side. Because there might not be any trades, the latest price for the time bucket might not be available, so I need to fill the gap "if not value in this time bucket, then get whatever is the previous value". Then the price change is stored in a normal PostgreSQL table, as a denormalised value, to be faster and more useful with normal PostgreSQL queries, with queries similar toJOIN latest_price
.
The queries are performed on TimescaleDB continous aggregate views.
I am using last()
and I am aware that fetching the latest value has been subject to discussion before. However, in my question, there is a special case of fetching multiple latest and close to latest values at once.
Currently, my method for price and 24h price delta calculation does it with two last()
queries, clocking 100 seconds wall time in production. Whereas, a similar reference query that does not use last()
but uses a naive GROUP BY
does it in 1 second.
What would be the best approach to optimise a query that needs to fetch multiple latest values for a group of items from TimescaleDB, and then perform calculations on them? Would subqueries be faster? Whan an example subquery would look like?
Would creating a trigger that stores the latest value when candle_ohlcvx_aggregate_1h
is updated make sense? Do triggers work with continuous aggregate views? How would this trigger look like?
My price calculation queries:
-- Get the latest price
SELECT
pair_id,
last(close, bucket) as last_close
FROM candle_ohlcvx_aggregate_15m
WHERE (bucket < :now_ts) AND (pair_id IN :pair_ids)
GROUP BY pair_id;
-- Get the latest price 24h ago
SELECT
pair_id,
last(close, bucket) as last_close
FROM candle_ohlcvx_aggregate_15m
WHERE (bucket < (:now_ts - INTERVAL '1 DAY')) AND (pair_id IN :pair_ids)
GROUP BY pair_id;
My reference query without last that clocks 1 second wall time - calculates the volume for one day using an hourly bucket, but does not gap-fill the last value using last()
making it fast:
SELECT
pair_id, SUM(buy_volume) + SUM(sell_volume) as vol
FROM candle_ohlcvx_aggregate_1h
WHERE (bucket >= :now_ts - INTERVAL '1 DAY') AND (bucket < :now_ts) AND (pair_id IN :pair_ids)
GROUP BY pair_id
ORDER BY pair_id ASC;
"""