Jakie są zalety i wady wykonywania obliczeń w sql w porównaniu z twoją aplikacją

154

shopkeeper tabela zawiera następujące pola:

id (bigint),amount (numeric(19,2)),createddate (timestamp)

Powiedzmy, że mam powyższą tabelę. Chcę uzyskać dane z wczoraj i wygenerować raport, drukując kwotę do centów.

Jednym ze sposobów jest wykonanie obliczeń w mojej aplikacji java i wykonanie prostego zapytania

Date previousDate ;// $1 calculate in application

Date todayDate;// $2 calculate in application

select amount where createddate between $1 and $2 

a następnie przejrzyj rekordy i zamień kwoty na centy w mojej aplikacji java i wygeneruj raport

Innym sposobem jest wykonywanie obliczeń w samym zapytaniu sql:

select cast(amount * 100 as int) as "Cents"
from shopkeeper  where createddate  between date_trunc('day', now()) - interval '1 day'  and  date_trunc('day', now())

a następnie przejrzyj rekordy i wygeneruj raport

W jeden sposób całe moje przetwarzanie odbywa się w aplikacji java i uruchamiane jest proste zapytanie. W innym przypadku wszystkie konwersje i obliczenia są wykonywane w kwerendzie Sql.

Powyższy przypadek użycia jest tylko przykładem, w rzeczywistym scenariuszu tabela może mieć wiele kolumn, które wymagają przetwarzania podobnego rodzaju.

Czy możesz mi powiedzieć, które podejście jest lepsze pod względem wydajności i innych aspektów i dlaczego?

hellojava
źródło
2
Obliczenia dat będą miały niewielki lub żaden wpływ - zakładając, że twój silnik sql faktycznie obliczy twoje daty tylko raz. zdefiniowanie ich w aplikacji ma sens, ponieważ i tak zostaną tam zdefiniowane, czy to dla tytułu raportu, czy też innych rzeczy. pomnożenie wartości przez 100 w tym przypadku można wykonać na dowolnym poziomie, ponieważ i tak będziesz przechodzić przez te wiersze w celu renderowania, a * 100 prawdopodobnie nie będzie wolniejsze na żadnej warstwie z wyjątkiem front-endu. W obu przypadkach obliczenia są minimalne i przyćmione przez otaczające operacje, a nie dotyczą wydajności.
Morg.

Odpowiedzi:

206

Zależy to od wielu czynników - ale przede wszystkim:

  • złożoność obliczeń (wolą robić złożoną chrzęst na app-serwer, ponieważ łuski out ; zamiast serwerze DB, która skaluje się )
  • ilość danych (jeśli potrzebujesz uzyskać dostęp do dużej ilości danych / zagregować je, zrobienie tego na serwerze db pozwoli zaoszczędzić przepustowość i dysk io, jeśli agregacje można wykonać w indeksach)
  • wygoda (sql nie jest najlepszym językiem do skomplikowanej pracy - szczególnie nie jest świetny do pracy proceduralnej, ale bardzo dobry do pracy opartej na zestawie; jednak kiepska obsługa błędów)

Jak zawsze, jeśli zrobić przynieść tył danych do aplikacji serwera, minimalizując kolumn i wierszy będą na swoją korzyść. Upewnienie się, że zapytanie jest dostrojone i odpowiednio zindeksowane, pomoże w obu przypadkach.

Uwaga:

a następnie przejrzyj rekordy

Zapętlanie się przez rekordy jest prawie zawsze czymś złym w sql - preferowane jest pisanie operacji opartej na zbiorach.

Generalnie wolę ograniczyć pracę bazy danych do minimum „przechowuj te dane, pobierz te dane” - jednak zawsze są przykłady scenariuszy, w których eleganckie zapytanie na serwerze może zaoszczędzić dużo przepustowości.

Weź również pod uwagę: jeśli jest to kosztowne obliczeniowo, czy można je gdzieś buforować?

Jeśli chcesz dokładnego „co jest lepsze”; zakoduj go w obie strony i porównaj (zauważ, że pierwszy szkic jednego z nich prawdopodobnie nie jest dostrojony w 100%). Ale weź pod uwagę typowe użycie: jeśli w rzeczywistości jest wywoływane 5 razy (osobno) na raz, to zasymuluj to: nie porównuj tylko jednego „1 z nich do 1 z nich”.

Marc Gravell
źródło
Pętla implikuje przetwarzanie mniej lub bardziej „wiersz na raz”. A to oznacza 2 * opóźnienie w sieci oraz cztery przełączniki kontekstowe w obie strony. Tak: to jest drogie. „Natywna” operacja DBMS wykonuje całą ciężką pracę, aby zminimalizować operacje wejścia / wyjścia dysku (wywołania systemowe), ale udaje się pobrać więcej niż jeden wiersz na wywołanie systemowe. Row na raz zajmuje co najmniej cztery wywołania systemowe.
wildplasser
@wildplasser nie jest konieczne; serwer może przesyłać strumieniowo wiersze, które konsumujesz po ich przybyciu - metafora „czytnika” nie jest rzadkością.
Marc Gravell
1
@Marc Cavell: Cóż, to zależy. W przypadku, gdy ślad programu aplikacyjnego jest tylko jednym rekordem logicznym, jest mniej więcej OK. Ale większość znanych mi „frameworków” ma tendencję do zasysania wszystkich rekordów podczas uruchamiania i odpalania ich jeden po drugim. Blokowanie to kolejna pułapka.
wildplasser
Myślę, że dobra zasada jest taka: nie przywracaj z serwera SQL wierszy danych, których ostatecznie nie potrzebujesz. Na przykład, jeśli musisz wykonać zagregowane operacje, prawdopodobnie należą one do języka SQL. Połączenia między tabelami lub podzapytaniami? SQL. Takie też podejście stosujemy z odznakami i póki co radzimy sobie ze skalą :-)
Sklivvz
1
@zinking, która byłaby operacją opartą na zestawie. W tym scenariuszu nie piszesz kodu pętli - to jest szczegół implementacji. Przez „pętlę” mam na myśli jawne pętle, na przykład kursor
Marc Gravell
86

Posłużę się metaforą: jeśli chcesz kupić złoty naszyjnik w Paryżu, złotnik mógłby usiąść w Kapsztadzie lub Paryżu, to kwestia umiejętności i gustu. Ale w tym celu nigdy nie wysłałbyś ton rudy złota z RPA do Francji. Ruda jest przetwarzana w miejscu wydobycia (lub przynajmniej w ogólnym obszarze), tylko złoto jest wysyłane. To samo powinno dotyczyć aplikacji i baz danych.

Jeśli chodzi o PostgreSQL , na serwerze można zrobić prawie wszystko, całkiem wydajnie. RDBMS doskonale sprawdza się przy złożonych zapytaniach. Dla potrzeb proceduralnych możesz wybierać spośród wielu języków skryptowych po stronie serwera : tcl, python, perl i wiele innych. Jednak głównie używam PL / pgSQL .

Najgorszym scenariuszem byłoby wielokrotne przechodzenie do serwera dla każdego wiersza większego zestawu. (To byłoby jak wysyłanie jednej tony rudy na raz.)

Po drugie , jeśli wysyłasz kaskadę zapytań, każde w zależności od poprzedniego, podczas gdy wszystko to można zrobić w jednym zapytaniu lub procedurze na serwerze. (To jak wysyłanie po kolei złota i każdego klejnotu na osobnym statku).

Przechodzenie między aplikacją a serwerem jest kosztowne. Dla serwera i klienta. Spróbuj ograniczyć ten problem, a wygrasz - ergo: w razie potrzeby użyj procedur po stronie serwera i / lub wyrafinowanego języka SQL.

Właśnie zakończyliśmy projekt, w którym prawie wszystkie złożone zapytania spakowaliśmy do funkcji Postgres. Aplikacja przekazuje parametry i pobiera potrzebne zbiory danych. Szybki, czysty, prosty (dla programisty aplikacji), I / O zredukowane do minimum ... błyszczący naszyjnik o niskim śladzie węglowym.

Erwin Brandstetter
źródło
12
Byłbym ostrożny, jeśli chodzi o używanie tej analogii do podejmowania znaczących decyzji projektowych z innymi programistami. Analogie są bardziej zabiegiem retorycznym niż logicznym. Przesyłanie danych na serwer aplikacji jest o wiele tańsze niż wysłanie rudy złota do złotnika.
Doug,
3
Będziesz wysyłać rudy lub złoto w zależności od tego, co jest tańsze, jeśli nie masz technologii konwersji rudy na złoto lub jest ona zbyt droga (ponieważ górnicy chcą zabić tych innych pracowników), wyślesz je w inne miejsce, może między złotnikiem a górnikami, zwłaszcza jeśli masz więcej niż jednego złotnika.
Dainius,
1
dokładnie z tym, co się zgadzam, nie sądzę, że wykonywanie obliczeń opartych na pętli w SQL @a_horse_with_no_name jest zawsze złe, czasami i tak trzeba to zrobić, wolałbym, aby było to obliczane, gdy dane są pobierane, jak wskazała metafora Erwina. lub musisz to powtórzyć za opłatą, gdy dane zostaną przywrócone.
zinking
-1 Ponieważ jest to argument jednostronny, ignoruje kompromisy i stawia słomnika po stronie przeciwnej, zamiast rozważać i odrzucać najlepszy przypadek strony przeciwnej. „Przejście między aplikacją a serwerem jest kosztowne” - absolutnie: ale nie jest to jedyna rzecz, która jest droga, a różne wydatki należy zważyć. Może się okazać, że w konkretnym przypadku najlepsze są zapytania „wyszukanego SQL” lub procedury składowane; ale przy podejmowaniu tego rodzaju ustaleń generalnie należy brać pod uwagę szczegóły sprawy.
yfeldblum
Fajna analogia, ale niestety opiera się na błędnych założeniach. Transport rudy złota jest bardzo powszechny. Współczynnik usuwania złota wynosi około 1: 1 (złoto do odpadu), jednak często tańsze jest przetwarzanie go poza siedzibą, gdzie dostępny jest lepszy sprzęt i jakość wykonania. W zależności od wielkości przesyłki, zwiększenie wydajności przerobu o 0,1% może pozwolić na relatywny wzrost przychodów (pomimo podwojenia ceny przesyłki) - bo złoto jest dziś dość drogie. Inne rudy, takie jak na przykład żelazo, są również zwykle wysyłane (współczynnik usuwania żelaza wynosi około 60%!).
Chris Koston
18

W takim przypadku prawdopodobnie lepiej wykonasz obliczenia w języku SQL, ponieważ silnik bazy danych prawdopodobnie będzie miał bardziej wydajne procedury arytmetyczne dziesiętne niż Java.

Generalnie jednak w przypadku obliczeń na poziomie wiersza nie ma dużej różnicy.

Gdzie ma to znaczenie, to:

  • Obliczenia zbiorcze, takie jak SUM (), AVG (), MIN (), MAX () tutaj silnik bazy danych będzie o rząd wielkości szybszy niż implementacja Java.
  • Wszędzie tam, gdzie obliczenia są używane do filtrowania wierszy. Filtrowanie w bazie danych jest znacznie bardziej wydajne niż odczytywanie wiersza, a następnie odrzucanie go.
James Anderson
źródło
12

Nie ma czarnego / białego określenia, które części logiki dostępu do danych powinny być wykonywane w SQL, a jakie części powinny być wykonywane w aplikacji. Podoba mi się sformułowanie Marka Gravella , rozróżnianie

  • złożone obliczenia
  • obliczenia wymagające dużej ilości danych

Moc i wyrazistość SQL są mocno niedoceniane. Od czasu wprowadzenia funkcji okna , wiele obliczeń, które nie są ściśle zorientowane na zestawy, można wykonać bardzo łatwo i elegancko w bazie danych.

Niezależnie od ogólnej architektury aplikacji należy zawsze przestrzegać trzech praktycznych zasad:

  • utrzymać niewielką ilość danych przesyłanych między bazą danych a aplikacją (na korzyść obliczania rzeczy w DB)
  • utrzymanie niewielkiej ilości danych ładowanych z dysku przez bazę danych (na korzyść umożliwienia bazy danych optymalizacji instrukcji, aby uniknąć niepotrzebnego dostępu do danych)
  • nie przesuwaj bazy danych do granic jej mocy obliczeniowej za pomocą złożonych, równoległych obliczeń (na korzyść pobierania danych do pamięci aplikacji i wykonywania tam obliczeń)

Z mojego doświadczenia wynika, że ​​mając przyzwoitą bazę danych i pewną przyzwoitą wiedzę na temat przyzwoitej bazy danych, szybko nie napotkasz limitów procesora tej bazy danych.

Dalsze lektury, w których wyjaśniono te rzeczy:

Lukas Eder
źródło
2

Ogólnie rzecz biorąc, rób rzeczy w SQL, jeśli są szanse, że również inne moduły lub komponenty w tym samym lub innych projektach będą musiały uzyskać te wyniki. atomowa operacja wykonana po stronie serwera jest również lepsza, ponieważ wystarczy wywołać przechowywany proces z dowolnego narzędzia do zarządzania bazą danych, aby uzyskać ostateczne wartości bez dalszego przetwarzania.

W niektórych przypadkach nie ma to zastosowania, ale jeśli tak jest, ma to sens. również ogólnie db box ma najlepszy sprzęt i wydajność.

Davide Piras
źródło
Możliwość ponownego wykorzystania może występować na dowolnej warstwie i nie jest powodem (z punktu widzenia wydajności) do umieszczania większej liczby obliczeń w języku SQL. „Ogólnie rzecz biorąc, okno db”: jest to błędne, a ponadto, jak powiedział Marc Gravell, skalowanie nie działa w ten sam sposób. Większość baz danych wymaga niewielkiego sprzętu do przyzwoitego działania, a wzorzec wydajności ma niewiele wspólnego z serwerem aplikacji (tj. Wydałbym 2/3 mojego budżetu na serwer SQL na boskie IO, podczas gdy nie wydałbym więcej niż kilkaset na stos pamięci serwera aplikacji).
Morg.
1

Jeśli piszesz na ORM lub piszesz zwykłe aplikacje o niskiej wydajności, użyj dowolnego wzorca upraszczającego aplikację. Jeśli piszesz aplikację o wysokiej wydajności i dokładnie myślisz o skalowaniu, odniesiesz zwycięstwo, przenosząc przetwarzanie na dane. Zdecydowanie zalecam przeniesienie przetwarzania do danych.

Pomyślmy o tym w dwóch krokach: (1) transakcje OLTP (mała liczba rekordów). (2) OLAP (długie skany wielu rekordów).

W przypadku OLTP, jeśli chcesz być szybki (10 000 - 100 000 transakcji na sekundę), musisz usunąć rywalizację o zatrzask, blokadę i martwą blokadę z bazy danych. Oznacza to, że musisz wyeliminować długie przestoje w transakcjach: podróże w obie strony od klienta do DB w celu przeniesienia przetwarzania do klienta są jednym z takich długich przerw. Nie możesz mieć długotrwałych transakcji (aby uczynić atomowy odczyt / aktualizację) i mieć bardzo dużą przepustowość.

Re: skalowanie poziome. Nowoczesne bazy danych skalują się w poziomie. Systemy te już wdrażają HA i odporność na błędy. Wykorzystaj to i spróbuj uprościć przestrzeń aplikacji.

Spójrzmy na OLAP - w tym przypadku powinno być oczywiste, że przeciąganie ewentualnych terabajtów danych z powrotem do aplikacji to okropny pomysł. Systemy te są zbudowane specjalnie, aby działać niezwykle wydajnie w przypadku skompresowanych, wstępnie zorganizowanych danych kolumnowych. Nowoczesne systemy OLAP skalują się również w poziomie i mają wyrafinowane planery zapytań, które rozpraszają pracę w poziomie (wewnętrznie przenosząc przetwarzanie na dane).

Ryan
źródło
0

To, czy wykonać obliczenia na froncie, czy na zapleczu, jest bardzo zdecydowane, czy możemy określić nasz cel we wdrożeniu biznesowym. W pewnym momencie kod Java może działać lepiej niż kod sql, zarówno dobrze napisany, jak i odwrotnie. Ale nadal, jeśli jesteś zdezorientowany, możesz spróbować najpierw ustalić -

  1. Jeśli możesz osiągnąć coś prostego za pomocą bazy danych sql, lepiej idź do tego, ponieważ db będzie działał znacznie lepiej i przeprowadzi obliczenia tam, a następnie z pobieraniem wyniku. Jeśli jednak rzeczywiste obliczenia wymagają zbyt wielu obliczeń z tego miejsca, możesz przejść do kodu aplikacji. Czemu? Ponieważ scenariusze takie jak zapętlenie w większości przypadków nie są najlepiej obsługiwane przez sql, gdzie języki frontonuase są lepiej zaprojektowane do takich rzeczy.
  2. W przypadku, gdy podobne obliczenia są wymagane z wielu miejsc, to oczywiście umieszczenie kodu obliczeniowego na końcu db będzie lepsze, aby zachować wszystko w tym samym miejscu.
  3. Jeśli trzeba wykonać wiele obliczeń, aby uzyskać ostateczny wynik za pomocą wielu różnych zapytań, przejdź również do końca db, ponieważ możesz umieścić ten sam kod w procedurze składowanej, aby działał lepiej niż pobieranie wyników z zaplecza, a następnie obliczanie ich z przodu koniec.

Jest wiele innych aspektów, które możesz przemyśleć, zanim zdecydujesz, gdzie umieścić kod. Jedna opinia jest całkowicie błędna - wszystko można zrobić najlepiej w Javie (kod aplikacji) i / lub wszystko najlepiej zrobić za pomocą db (kod sql).

Neo
źródło
0

Z punktu widzenia wydajności: jest to bardzo prosta operacja arytmetyczna, którą prawie na pewno można wykonać znacznie szybciej niż faktyczne pobieranie danych z dysków, które stanowią podstawę bazy danych. Ponadto obliczanie wartości w klauzuli where prawdopodobnie będzie bardzo szybkie w dowolnym czasie wykonywania. Podsumowując, wąskim gardłem powinno być We / Wy dysku, a nie obliczanie wartości.

Jeśli chodzi o czytelność, myślę, że jeśli używasz ORM, powinieneś to zrobić w środowisku serwera aplikacji, ponieważ ORM pozwoli ci bardzo łatwo pracować z podstawowymi danymi, używając operacji opartych na zbiorach. Jeśli i tak zamierzasz napisać surowy SQL, nie ma nic złego w wykonywaniu tam obliczeń, Twój SQL również wyglądałby trochę ładniej i łatwiejszy do odczytania, gdyby był odpowiednio sformatowany.

Johannes Gehrs
źródło
0

Co najważniejsze, „wydajność” nie jest zdefiniowana.

Najbardziej liczy się dla mnie czas programisty.

Napisz zapytanie SQL. Jeśli jest zbyt wolny lub DB staje się wąskim gardłem, zastanów się ponownie. Do tego czasu będziesz w stanie porównać te dwa podejścia i podjąć decyzję na podstawie rzeczywistych danych dotyczących Twojej konfiguracji (sprzętu i dowolnego stosu, na którym się znajdujesz).

user2757750
źródło
0

Nie sądzę, aby różnice w wydajności można było uzasadnić bez konkretnych przykładów i punktów odniesienia, ale mam inne podejście:

Które możesz utrzymać lepiej? Na przykład możesz chcieć przełączyć swój front-end z Java na Flash, HTML5, C ++ lub coś innego. Ogromna liczba programów przeszła taką zmianę, a nawet istnieje w więcej niż jednym języku, ponieważ muszą działać na wielu urządzeniach.

Nawet jeśli masz odpowiednią warstwę środkową (z podanego przykładu wydaje się, że tak nie jest), ta warstwa może się zmienić i JBoss może stać się Ruby / Rails.

Z drugiej strony jest mało prawdopodobne, że zastąpisz zaplecze SQL czymś, co nie jest relacyjną bazą danych SQL, a nawet jeśli to zrobisz, i tak będziesz musiał przepisać front-end od zera, więc kwestia jest dyskusyjna.

Mój pomysł jest taki, że jeśli wykonasz obliczenia w bazie danych, znacznie łatwiej będzie później napisać drugi front-end lub warstwę środkową, ponieważ nie musisz ponownie implementować wszystkiego. Jednak w praktyce myślę, że „gdzie mogę to zrobić z kodem, który ludzie zrozumieją” jest najważniejszym czynnikiem.

Kajetan Abt
źródło
Jeśli zmienisz jboss na ruby, jest bardzo prawdopodobne, że zmienisz db (a mimo to będziesz musiał zastosować te obliczenia) i nie jest tak mało prawdopodobne, że możesz zmienić coś na coś innego, na przykład nosql.
Dainius,
0

Aby uprościć odpowiedź na to pytanie, należy spojrzeć na równoważenie obciążenia. Chcesz umieścić ładunek tam, gdzie masz największą pojemność (jeśli ma to jakiś sens). W większości systemów to serwer SQL szybko staje się wąskim gardłem, więc prawdopodobnie odpowiedź jest taka, że ​​nie chcesz, aby SQL wykonywał o jedną uncję pracy więcej niż musi.

Również w większości architektur to serwer (y) SQL stanowią rdzeń systemu, a systemy zewnętrzne są dodawane.

Ale powyższa matematyka jest tak trywialna, że ​​jeśli nie pchasz swojego systemu do granic możliwości, najlepszym miejscem do umieszczenia jest tam, gdzie chcesz. Gdyby matematyka nie była trywialna, jak na przykład obliczanie sin / cos / tan dla, powiedzmy, obliczenia odległości, wysiłek może stać się nietrywialny i wymagać starannego planowania i testowania.

Donovanr
źródło
0

Inne odpowiedzi na to pytanie są interesujące. O dziwo, nikt nie odpowiedział na twoje pytanie. Zastanawiasz się:

  1. Czy lepiej jest rzutować na centy w zapytaniu? Nie sądzę, by rzutowanie na centy cokolwiek dodało do zapytania.
  2. Czy lepiej jest użyć funkcji now () w zapytaniu? Wolałbym przekazywać daty do zapytania, zamiast obliczać je w zapytaniu.

Więcej informacji: W przypadku pierwszego pytania chcesz mieć pewność, że agregowanie ułamków działa bez błędów zaokrągleń. Myślę, że liczba 19,2 jest rozsądna dla pieniędzy, aw drugim przypadku liczby całkowite są OK. Z tego powodu używanie pływaka dla pieniędzy jest niewłaściwe.

Jeśli chodzi o pytanie drugie, jako programista lubię mieć pełną kontrolę nad datą uznawaną za „teraz”. Podczas korzystania z funkcji takich jak now () może być trudno napisać automatyczne testy jednostkowe. Ponadto, jeśli masz dłuższy skrypt transakcji, dobrze jest ustawić zmienną równą now () i użyć tej zmiennej, aby cała logika używała dokładnie tej samej wartości.

Chris Schoon
źródło
0

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ć

  1. Opcja 1 Zrób to w Python / Node itp
  2. 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

PirateApp
źródło
1
Jeśli jednak musisz to zrobić miliony razy tak szybko, jak to możliwe, znacznie łatwiej jest tworzyć równoległe aplikacje Pythona niż repliki db. Dopóki pewna skala oparta bardziej na SQL jest z pewnością szybsza / tańsza, ale ostatecznie pojawia się punkt zwrotny, kiedy lepiej jest wykonać te obliczenia w aplikacji.
Lenny