Podam prawdziwy przykład, aby odpowiedzieć na to pytanie
Musiałem obliczyć ważoną średnią kroczącą na moich danych ohlc, mam około 134000 świec z symbolem dla każdej, aby to zrobić
- Opcja 1 Zrób to w Python / Node itp
- Opcja 2 Zrób to w samym SQL!
Który jest lepszy?
- Gdybym miał to zrobić w Pythonie, w zasadzie musiałbym pobrać wszystkie zapisane rekordy w najgorszym przypadku, wykonać obliczenia i zapisać wszystko z powrotem, co moim zdaniem jest ogromnym marnotrawstwem IO
- Ważona średnia ruchoma zmienia się za każdym razem, gdy pojawi się nowa świeca, co oznacza, że robiłbym ogromne ilości IO w regularnych odstępach czasu, co nie jest dobrą opinią w moim znaku
- W SQL wszystko, co muszę zrobić, to prawdopodobnie napisanie wyzwalacza, który oblicza i przechowuje wszystko, więc wystarczy pobrać końcowe wartości WMA dla każdej pary od czasu do czasu, a to jest o wiele bardziej wydajne
Wymagania
- Gdybym miał obliczyć WMA dla każdej świecy i przechowywać ją, zrobiłbym to w Pythonie
- Ale ponieważ potrzebuję tylko ostatniej wartości, SQL jest znacznie szybszy niż Python
Aby dać ci trochę zachęty, jest to wersja Pythona, która wykonuje ważoną średnią ruchomą
WMA wykonane za pomocą kodu
import psycopg2
import psycopg2.extras
from talib import func
import timeit
import numpy as np
with psycopg2.connect('dbname=xyz user=xyz') as conn:
with conn.cursor() as cur:
t0 = timeit.default_timer()
cur.execute('select distinct symbol from ohlc_900 order by symbol')
for symbol in cur.fetchall():
cur.execute('select c from ohlc_900 where symbol = %s order by ts', symbol)
ohlc = np.array(cur.fetchall(), dtype = ([('c', 'f8')]))
wma = func.WMA(ohlc['c'], 10)
# print(*symbol, wma[-1])
print(timeit.default_timer() - t0)
conn.close()
WMA przez SQL
"""
if the period is 10
then we need 9 previous candles or 15 x 9 = 135 mins on the interval department
we also need to start counting at row number - (count in that group - 10)
For example if AAPL had 134 coins and current row number was 125
weight at that row will be weight = 125 - (134 - 10) = 1
10 period WMA calculations
Row no Weight c
125 1
126 2
127 3
128 4
129 5
130 6
131 7
132 8
133 9
134 10
"""
query2 = """
WITH
condition(sym, maxts, cnt) as (
select symbol, max(ts), count(symbol) from ohlc_900 group by symbol
),
cte as (
select symbol, ts,
case when cnt >= 10 and ts >= maxts - interval '135 mins'
then (row_number() over (partition by symbol order by ts) - (cnt - 10)) * c
else null
end as weighted_close
from ohlc_900
INNER JOIN condition
ON symbol = sym
WINDOW
w as (partition by symbol order by ts rows between 9 preceding and current row)
)
select symbol, sum(weighted_close)/55 as wma
from cte
WHERE weighted_close is NOT NULL
GROUP by symbol ORDER BY symbol
"""
with psycopg2.connect('dbname=xyz user=xyz') as conn:
with conn.cursor() as cur:
t0 = timeit.default_timer()
cur.execute(query2)
# for i in cur.fetchall():
# print(*i)
print(timeit.default_timer() - t0)
conn.close()
Wierz lub nie, ale zapytanie działa szybciej niż wersja Pure Python, wykonująca WAŻONĄ ŚREDNĄ RUCHOMĄ !!! Podszedłem krok po kroku do napisania tego zapytania, więc trzymaj się tego, a wszystko będzie dobrze
Prędkość
0,42141127300055814 sekund Python
0,23801879299935536 sekund SQL
Mam 134000 fałszywych rekordów OHLC w mojej bazie danych podzielonych na 1000 akcji, co jest przykładem sytuacji, w których SQL może przewyższać serwer aplikacji