Оптимизация поиска транзакций по критериям внутри динамический диапазонов (window) для исторических данных с последующей аггрегацией

Ниже приведен рабочие запросы, которые хотелось бы оптимизировать по производительность для mssql 2019 Решаемая задача.

  1. В исторических данных trades требуется найти все возможные пары транзакций, которые могут находиться друг от друга на растоянии не более @shot_period (в милисекунд)
  2. Внутри диапазона все цены (price) идущие после первой/начальной транзакции включительно до конечной (минимальной/искомой) - должны быть меньше первой транзакции. price1 > price2, price1 > price3, price1 > price4, etc
  3. Искомая/минимальная транзакция имеет наименьшее значение внутри заданного временного диапазона (@shot_period) min(priceN)
  4. Процентное сотношение цен первой и минимальной транзакции должно быть больше или равно @min_percent
    100*((price1/priceN)-1) > @min_percent
  5. Для случаев когда может быть несколько пар транзакций, с разными начальными tid (tid1), но с одинаковыми минимальными tid (tid2), береться только первая пара с самой ранней tid (tid1)
  6. Для транзакций внутри каждой найденой пары, нужно подсчитать sum(cost), sum(amount), count(*)

Пример данных


DECLARE @shot_period INT = 5000; --(milisseconds)
DECLARE @min_percent FLOAT = 1;

--pair - 'ETH/BTC', 'LTC/BTC', ....
--tid - transaction id
--ts - timestamp (milliseconds)
--dt - datetime

-- Insert statements for procedure here
SELECT a.pair, 
        a.tid tid1, 
        a.ts ts1, 
        a.dt dt1, 
        a.price price1, 
        c.tid tid2, 
        c.ts ts2, 
        c.dt dt2, 
        c.price price2,  
        ROUND(100*((a.price/c.price) - 1),4) shotPercent   
        
INTO #tmp1
FROM trades a WITH(NOLOCK, NOWAIT) CROSS APPLY(
                    SELECT TOP(1) b.tid, b.ts, b.dt, b.price 
                    FROM trades b WITH(NOLOCK, NOWAIT)
                    WHERE b.pair = a.pair 
                        AND b.ts BETWEEN a.ts and a.ts + @shot_period
                        AND b.tid > a.tid
                    ORDER BY b.price, b.tid
             ) c

WHERE ROUND(100*((a.price/c.price) - 1),4) >= @min_percent

SELECT r.pair, r.tid1, r.ts1, r.dt1, r.price1, r.tid2, r.ts2, r.dt2, r.price2, r.shotPercent, COUNT(*) shotTrades, MAX(CASE WHEN t.tid = r.tid1 THEN 0 ELSE t.price END) maxPrice, SUM(t.amount) shotAmount, sum(t.cost) shotCost   
INTO #tmp1a
FROM #tmp1 r WITH(NOLOCK, NOWAIT) INNER HASH JOIN trades t WITH(NOLOCK, NOWAIT) ON t.pair = r.pair AND t.ts BETWEEN r.ts1 AND r.ts2 AND t.tid BETWEEN r.tid1 AND r.tid2
GROUP BY r.pair, r.tid1, r.ts1, r.dt1, r.price1, r.tid2, r.ts2, r.dt2, r.price2, r.shotPercent
HAVING MAX(CASE WHEN t.tid = r.tid1 THEN 0 ELSE t.price END) < r.price1

DROP TABLE #tmp1;

CREATE CLUSTERED INDEX t1a ON #tmp1a(pair, tid2, tid1);

SELECT pair, CAST(q.dt1 AS DATE) as [date],  tid1, ts1, dt1, price1, tid2, ts2, dt2, price2, shotPercent, shotTrades, shotAmount, shotCost
INTO #tmp2
FROM (
        SELECT pair, tid1, ts1, dt1, price1, tid2, ts2, dt2, price2, shotPercent, shotTrades, shotAmount, shotCost, ROW_NUMBER() OVER(PARTITION BY pair, tid2 ORDER BY tid1) rn
        FROM #tmp1a WITH(NOLOCK, NOWAIT)
     ) q
WHERE rn = 1

Ответы (0 шт):