Was sind die Vor- und Nachteile der Berechnung in SQL im Vergleich zu Ihrer Anwendung?

154

shopkeeper Tabelle enthält folgende Felder:

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

Angenommen, ich habe die obige Tabelle. Ich möchte die Aufzeichnungen für gestern abrufen und einen Bericht erstellen, indem der Betrag in Cent gedruckt wird.

Eine Möglichkeit besteht darin, Berechnungen in meiner Java-Anwendung durchzuführen und eine einfache Abfrage auszuführen

Date previousDate ;// $1 calculate in application

Date todayDate;// $2 calculate in application

select amount where createddate between $1 and $2 

und dann die Datensätze durchlaufen und den Betrag in meiner Java-Anwendung in Cent umrechnen und den Bericht generieren

Eine andere Möglichkeit besteht darin, Berechnungen in der SQL-Abfrage selbst durchzuführen:

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())

Führen Sie dann eine Schleife durch die Datensätze und generieren Sie den Bericht

In einer Weise wird meine gesamte Verarbeitung in einer Java-Anwendung ausgeführt und eine einfache Abfrage wird ausgelöst. In anderen Fällen werden alle Konvertierungen und Berechnungen in einer SQL-Abfrage durchgeführt.

Der obige Anwendungsfall ist nur ein Beispiel. In einem realen Szenario kann eine Tabelle viele Spalten enthalten, die eine Verarbeitung ähnlicher Art erfordern.

Können Sie mir bitte sagen, welcher Ansatz in Bezug auf Leistung und andere Aspekte besser ist und warum?

Hallojava
quelle
2
Die Datumsberechnungen haben kaum oder gar keine Auswirkungen - vorausgesetzt, Ihre SQL-Engine berechnet Ihre Daten tatsächlich nur einmal. Es ist durchaus sinnvoll, sie in Ihrer Anwendung zu definieren, da sie dort ohnehin irgendwann definiert werden, sei es für den Berichtstitel oder andere Dinge. Das Multiplizieren des Werts mit 100 kann in diesem Fall auf jeder Ebene erfolgen, da Sie diese Zeilen ohnehin zum Rendern durchlaufen und * 100 auf keiner Ebene außer dem Front-End langsamer ist. In beiden Fällen sind Ihre Berechnungen minimal und werden von den umgebenden Vorgängen in den Schatten gestellt, was kein Leistungsproblem darstellt.
Morg.

Antworten:

206

Es hängt von vielen Faktoren ab - aber am wichtigsten:

  • Komplexität von Berechnungen (bevorzugt komplexe Knirschen auf einem App-Server zu tun, denn das Skala aus , sondern als ein DB - Server, die skaliert bis )
  • Datenvolumen (Wenn Sie auf viele Daten zugreifen / diese aggregieren müssen, spart dies auf dem Datenbankserver Bandbreite und Festplatte io, wenn die Aggregate in Indizes durchgeführt werden können.)
  • Bequemlichkeit (SQL ist nicht die beste Sprache für komplexe Arbeiten - besonders nicht gut für prozedurale Arbeiten, aber sehr gut für satzbasierte Arbeiten; miese Fehlerbehandlung jedoch)

Wie immer, wenn Sie tun die Daten zurück an den App-Server bringen, minimiert die Spalten und Zeilen werden zu Ihrem Vorteil sein. Wenn Sie sicherstellen, dass die Abfrage optimiert und entsprechend indiziert ist, können Sie beide Szenarien unterstützen.

Zu Ihrer Notiz:

und durchlaufen Sie dann die Datensätze

Das Durchlaufen von Datensätzen ist in SQL fast immer das Falsche - das Schreiben einer satzbasierten Operation wird bevorzugt.

Im Allgemeinen ziehe ich es vor, den Job der Datenbank auf ein Minimum zu beschränken: "Diese Daten speichern, diese Daten abrufen". Es gibt jedoch immer Beispiele für Szenarien, in denen eine elegante Abfrage auf dem Server viel Bandbreite sparen kann.

Bedenken Sie auch: Wenn dies rechenintensiv ist, kann es irgendwo zwischengespeichert werden?

Wenn Sie ein genaues "was besser ist" wollen; codieren Sie es in beide Richtungen und vergleichen Sie es (wobei Sie feststellen, dass ein erster Entwurf von beiden wahrscheinlich nicht zu 100% abgestimmt ist). Berücksichtigen Sie jedoch die typische Verwendung: Wenn es in der Realität fünfmal (separat) gleichzeitig aufgerufen wird, simulieren Sie Folgendes: Vergleichen Sie nicht nur eine einzige "1 davon mit einer davon".

Marc Gravell
quelle
Schleifen implizieren mehr oder weniger "zeilenweise" Verarbeitung. Und das bedeutet 2 * Netzwerklatenz plus vier Kontextwechsel-Roundtrips. Ja, das ist teuer. Eine "native" DBMS-Operation erledigt die ganze harte Arbeit, um Festplatten-E / A (Systemaufrufe) zu minimieren, schafft es jedoch, mehr als eine Zeile pro Systemaufruf abzurufen. Zeile für Zeile dauert mindestens vier Systemaufrufe.
Wildplasser
@wildplasser nicht notwendig; Der Server kann Streaming-Zeilen sein, die Sie beim Eintreffen verbrauchen - eine "Leser" -Metapher ist keine Seltenheit.
Marc Gravell
1
@ Marc Cavell: Nun, es kommt darauf an. In dem Fall, in dem der Footprint eines Anwendungsprogramms nur ein logischer Datensatz ist, ist er mehr oder weniger in Ordnung. Aber die meisten der mir bekannten "Frameworks" saugen beim Start alle Datensätze an und feuern sie nacheinander ab. Das Sperren ist eine weitere Gefahr.
Wildplasser
Ich denke, dass eine gute Faustregel lautet: Bringen Sie keine Datenzeilen von SQL Server zurück, die Sie letztendlich nicht benötigen. Wenn Sie beispielsweise aggregierte Vorgänge ausführen müssen, gehören diese wahrscheinlich zu SQL. Verknüpfungen zwischen Tabellen oder Unterabfragen? SQL. Das ist auch der Ansatz, den wir mit Abzeichen verwenden, und bis jetzt bewältigen wir die Skalierung :-)
Sklivvz
1
@zinking das wäre eine satzbasierte Operation. In diesem Szenario schreiben Sie den Schleifencode nicht - das ist ein Implementierungsdetail. Mit "Schleifen" meine ich explizite Schleifen, zum Beispiel einen Cursor
Marc Gravell
86

Lassen Sie mich eine Metapher verwenden: Wenn Sie in Paris eine goldene Halskette kaufen möchten , könnte der Goldschmied in Kapstadt oder Paris sitzen, das ist eine Frage des Könnens und des Geschmacks. Aber dafür würden Sie niemals Tonnen Golderz von Südafrika nach Frankreich liefern. Das Erz wird am Bergbaustandort (oder zumindest im allgemeinen Gebiet) verarbeitet, nur das Gold wird verschifft. Gleiches sollte für Apps und Datenbanken gelten.

Soweit PostgreSQL Bezug auf können Sie fast alles auf dem Server sehr effizient ausführen. Das RDBMS zeichnet sich durch komplexe Abfragen aus. Für prozedurale Anforderungen können Sie aus einer Vielzahl von serverseitigen Skriptsprachen auswählen: tcl, python, perl und viele mehr. Meistens benutze ich jedoch PL / pgSQL .

Das schlimmste Szenario wäre, wiederholt für jede einzelne Zeile eines größeren Satzes zum Server zu gehen. (Das wäre so, als würde man eine Tonne Erz pro Zeit versenden.)

Zweitens , wenn Sie eine Kaskade von Abfragen senden, die jeweils von der vorherigen abhängen, während alles in einer Abfrage oder Prozedur auf dem Server ausgeführt werden kann. (Das ist so, als würde man das Gold und jedes der Juwelen nacheinander mit einem separaten Schiff versenden.)

Das Hin- und Herwechseln zwischen App und Server ist teuer. Für Server und Client. Versuchen Sie, dies zu reduzieren, und Sie werden gewinnen: Verwenden Sie bei Bedarf serverseitige Prozeduren und / oder hochentwickeltes SQL.

Wir haben gerade ein Projekt abgeschlossen, in dem wir fast alle komplexen Abfragen in Postgres-Funktionen gepackt haben. Die App übergibt Parameter und erhält die benötigten Datensätze. Schnell, sauber, einfach (für den App-Entwickler), E / A auf ein Minimum reduziert ... eine glänzende Halskette mit geringem CO2-Ausstoß.

Erwin Brandstetter
quelle
12
Ich würde vorsichtig sein, wenn ich diese Analogie verwende, um Designentscheidungen mit anderen Entwicklern sinnvoll zu treffen. Analogien sind eher ein rhetorisches als ein logisches Mittel. Unter anderem ist es viel billiger, Daten an einen App-Server zu senden, als Golderz an einen Goldschmied.
Doug
3
Sie werden Erze oder Gold senden, je nachdem, was billiger ist. Wenn Sie keine Technologie haben, um Erz in Gold umzuwandeln, oder wenn es zu teuer ist (weil Bergleute diese anderen Arbeiter töten wollen), werden Sie es an einen anderen Ort versenden, vielleicht in zwischen Goldschmied und Bergleuten, besonders wenn Sie mehr als einen Goldschmied haben.
Dainius
1
Genau das, was ich zustimme, ich denke nicht, dass es immer schlecht ist, eine schleifenbasierte Berechnung in SQL @a_horse_with_no_name durchzuführen. Manchmal muss dies sowieso getan werden. Ich würde es lieber berechnen, wenn Daten abgerufen werden, wie in Erwins Metapher angegeben. oder Sie müssen dies kostenpflichtig wiederholen, wenn Daten zurückgerufen werden.
Zinking
-1 Da es sich um ein einseitiges Argument handelt, werden Kompromisse ignoriert und ein Strohmann für die gegnerische Seite aufgestellt, anstatt den besten Fall der gegnerischen Seite zu prüfen und zu widerlegen. "Das Hin- und Herwechseln zwischen App und Server ist teuer" - absolut: Aber es ist nicht das einzige, was teuer ist, und die verschiedenen Kosten müssen gegeneinander abgewogen werden. Es kann sich herausstellen, dass "anspruchsvolle SQL" -Abfragen oder gespeicherte Prozeduren für den jeweiligen Fall am besten geeignet sind. Bei dieser Feststellung müssen jedoch im Allgemeinen die Einzelheiten des Falles berücksichtigt werden.
Yfeldblum
Coole Analogie, aber leider basiert sie auf falschen Annahmen. Der Versand von Golderz ist sehr verbreitet. Das Goldentfernungsverhältnis beträgt etwa 1: 1 (Gold zu Abfall), es ist jedoch häufig billiger, es außerhalb des Standorts zu verarbeiten, wo eine bessere Ausrüstung und Verarbeitungsqualität verfügbar sind. Abhängig von der Größe der Sendung kann eine Erhöhung der Verarbeitungseffizienz um 0,1% eine relative Steigerung des Umsatzes ermöglichen (trotz des doppelten Versandpreises) - da das Gold heutzutage ziemlich teuer ist. Andere Erze, wie zum Beispiel Eisen, werden normalerweise ebenfalls verschifft (das Abstreifverhältnis von Eisen beträgt etwa 60%!).
Chris Koston
18

In diesem Fall ist es wahrscheinlich etwas besser, die Berechnung in SQL durchzuführen, da das Datenbankmodul wahrscheinlich über effizientere Dezimalarithmetikroutinen verfügt als Java.

Im Allgemeinen gibt es jedoch für Berechnungen auf Zeilenebene keinen großen Unterschied.

Wo es einen Unterschied macht, ist:

  • Bei aggregierten Berechnungen wie SUM (), AVG (), MIN (), MAX () ist das Datenbankmodul um eine Größenordnung schneller als eine Java-Implementierung.
  • Überall dort, wo die Berechnung zum Filtern von Zeilen verwendet wird. Das Filtern in der Datenbank ist viel effizienter als das Lesen und anschließende Verwerfen einer Zeile.
James Anderson
quelle
12

Es gibt kein Schwarz / Weiß darüber, welche Teile der Datenzugriffslogik in SQL ausgeführt werden sollen und welche Teile in Ihrer Anwendung ausgeführt werden sollen. Ich mag Mark Gravells Formulierung, die zwischen unterscheidet

  • komplexe Berechnungen
  • datenintensive Berechnungen

Die Leistungsfähigkeit und Ausdruckskraft von SQL wird stark unterschätzt. Seit der Einführung von Fensterfunktionen können viele nicht streng satzorientierte Berechnungen sehr einfach und elegant in der Datenbank durchgeführt werden.

Unabhängig von der gesamten Anwendungsarchitektur sollten immer drei Faustregeln befolgt werden:

  • Halten Sie die zwischen Datenbank und Anwendung übertragene Datenmenge gering (zugunsten der Berechnung von Daten in der Datenbank).
  • Halten Sie die von der Datenbank von der Festplatte geladene Datenmenge gering (damit die Datenbank Anweisungen optimieren kann, um unnötigen Datenzugriff zu vermeiden).
  • Schieben Sie die Datenbank nicht mit komplexen, gleichzeitigen Berechnungen an ihre CPU-Grenzen (um Daten in den Anwendungsspeicher zu ziehen und dort Berechnungen durchzuführen).

Nach meiner Erfahrung werden Sie mit einem anständigen DBA und einigen anständigen Kenntnissen über Ihre anständige Datenbank nicht sehr bald auf die CPU-Grenzwerte Ihrer DBs stoßen.

Einige weiterführende Literatur, in der diese Dinge erklärt werden:

Lukas Eder
quelle
2

Machen Sie im Allgemeinen Dinge in SQL, wenn die Möglichkeit besteht, dass auch andere Module oder Komponenten in demselben oder anderen Projekten diese Ergebnisse erzielen müssen. Eine atomare Operation, die serverseitig ausgeführt wird, ist auch besser, da Sie nur den gespeicherten Prozess von einem beliebigen Datenbankverwaltungstool aus aufrufen müssen, um endgültige Werte ohne weitere Verarbeitung zu erhalten.

In einigen Fällen trifft dies nicht zu, aber wenn dies der Fall ist, ist dies sinnvoll. auch im allgemeinen hat die db box die beste hardware und leistung.

Davide Piras
quelle
Die Wiederverwendbarkeit kann auf jeder Ebene vorhanden sein und ist kein Grund (in Bezug auf die Leistung), mehr Berechnungen in SQL durchzuführen. "Im Allgemeinen die DB-Box": Dies ist falsch und außerdem funktioniert die Skalierung, wie Marc Gravell sagte, nicht auf die gleiche Weise. Die meisten Datenbanken erfordern wenig Hardware, um anständig ausgeführt zu werden, und das Leistungsmuster hat wenig mit dem eines Anwendungsservers zu tun (dh ich würde 2/3 meines Budgets für einen SQL-Server für gottähnliche E / A ausgeben, während ich nicht mehr ausgeben würde als ein paar Hundert für den Speicherstapel eines Appservers).
Morg.
1

Wenn Sie über ORM schreiben oder gelegentliche Anwendungen mit geringer Leistung schreiben, verwenden Sie ein beliebiges Muster, um die Anwendung zu vereinfachen. Wenn Sie eine Hochleistungsanwendung schreiben und sorgfältig über die Skalierung nachdenken, gewinnen Sie, indem Sie die Verarbeitung auf Daten verlagern. Ich empfehle nachdrücklich, die Verarbeitung auf die Daten zu verlagern.

Lassen Sie uns dies in zwei Schritten betrachten: (1) OLTP-Transaktionen (kleine Anzahl von Datensätzen). (2) OLAP (lange Scans vieler Datensätze).

Wenn Sie im OLTP-Fall schnell sein möchten (10.000 - 100.000 Transaktionen pro Sekunde), müssen Sie Latch-, Lock- und Dead-Lock-Konflikte aus der Datenbank entfernen. Dies bedeutet, dass Sie lange Verzögerungen bei Transaktionen vermeiden müssen: Roundtrips vom Client zur Datenbank, um die Verarbeitung auf den Client zu verschieben, sind eine solche lange Verzögerung. Sie können keine langlebigen Transaktionen (um das Lesen / Aktualisieren atomar zu machen) und keinen sehr hohen Durchsatz haben.

Betreff: horizontale Skalierung. Moderne Datenbanken werden horizontal skaliert. Diese Systeme implementieren bereits HA und Fehlertoleranz. Nutzen Sie das und versuchen Sie, Ihren Anwendungsbereich zu vereinfachen.

Schauen wir uns OLAP an - in diesem Fall sollte es offensichtlich sein, dass es eine schreckliche Idee ist, möglicherweise Terrabyte an Daten zurück in die Anwendung zu ziehen. Diese Systeme wurden speziell für den äußerst effizienten Betrieb mit komprimierten, vororganisierten Säulendaten entwickelt. Moderne OLAP-Systeme lassen sich auch horizontal skalieren und verfügen über ausgefeilte Abfrageplaner, die die Arbeit horizontal verteilen (interne Verarbeitung der Verarbeitung auf Daten).

Ryan
quelle
0

Ob Berechnungen am Frontend oder am Backend durchgeführt werden sollen, ist sehr wichtig, wenn wir unser Ziel bei der Geschäftsimplementierung bestimmen können. Zur Zeit ist Java-Code möglicherweise leistungsfähiger als ein gut geschriebener SQL-Code oder umgekehrt. Aber immer noch, wenn Sie verwirrt sind, können Sie versuchen, zuerst festzustellen -

  1. Wenn Sie über die Datenbank SQL etwas Unkompliziertes erreichen können, sollten Sie es besser machen, da db eine viel bessere Leistung erbringt und dort und dann mit dem Ergebnisabruf Berechnungen durchführt. Wenn die eigentliche Berechnung jedoch zu viel Berechnung von hier und da erfordert, können Sie mit dem Anwendungscode fortfahren. Warum? Da Szenarien wie Schleifen in den meisten Fällen nicht am besten von SQL verarbeitet werden, sind Front-End-Sprachen für diese Dinge besser geeignet.
  2. Wenn an vielen Stellen eine ähnliche Berechnung erforderlich ist, ist es offensichtlich besser, den Berechnungscode am DB-Ende zu platzieren, um die Dinge am selben Ort zu halten.
  3. Wenn viele Berechnungen durchgeführt werden müssen, um das Endergebnis über viele verschiedene Abfragen zu erzielen, wählen Sie auch db end, da Sie denselben Code in eine gespeicherte Prozedur einfügen können, um eine bessere Leistung zu erzielen, als die Ergebnisse aus dem Backend abzurufen und sie dann vorne zu berechnen Ende.

Es gibt viele andere Aspekte, über die Sie nachdenken können, bevor Sie entscheiden, wo der Code platziert werden soll. Eine Wahrnehmung ist völlig falsch - Alles kann am besten in Java (App-Code) und / oder alles am besten in der Datenbank (SQL-Code) ausgeführt werden.

Neo
quelle
0

Aus Sicht der Leistung: Dies ist eine sehr einfache arithmetische Operation, die mit ziemlicher Sicherheit viel schneller ausgeführt werden kann als das tatsächliche Abrufen der Daten von den Datenträgern, die der Datenbank zugrunde liegen. Außerdem ist es wahrscheinlich, dass die Berechnung der Werte in der where-Klausel zu jeder Laufzeit sehr schnell ist. Zusammenfassend sollte der Engpass die Festplatten-E / A sein, nicht die Berechnung der Werte.

Aus Gründen der Lesbarkeit denke ich, wenn Sie ein ORM verwenden, sollten Sie dies in Ihrer App-Server-Umgebung tun, da Sie mit dem ORM sehr einfach mit den zugrunde liegenden Daten arbeiten können, indem Sie satzbasierte Operationen verwenden. Wenn Sie ohnehin unformatiertes SQL schreiben möchten, ist die Berechnung dort nicht falsch. Ihr SQL würde auch ein wenig besser aussehen und leichter zu lesen sein, wenn es richtig formatiert wird.

Johannes Gehrs
quelle
0

Entscheidend ist, dass "Leistung" nicht definiert ist.

Das, was mir am wichtigsten ist, ist die Entwicklerzeit.

Schreiben Sie die SQL-Abfrage. Wenn es zu langsam ist oder die Datenbank zu einem Engpass wird, überdenken Sie es erneut. Zu diesem Zeitpunkt können Sie die beiden Ansätze vergleichen und Ihre Entscheidung auf der Grundlage realer Daten treffen, die für Ihr Setup relevant sind (Hardware und der Stapel, auf dem Sie sich befinden).

user2757750
quelle
0

Ich glaube nicht, dass die Leistungsunterschiede ohne spezifische Beispiele und Benchmarks begründet werden können, aber ich habe eine andere Sichtweise:

Welches können Sie besser pflegen? Beispielsweise möchten Sie möglicherweise Ihr Front-End von Java auf Flash, HTML5, C ++ oder etwas anderes umstellen. Eine große Anzahl von Programmen hat eine solche Änderung durchlaufen oder existiert zunächst sogar in mehr als einer Sprache, da sie auf mehreren Geräten arbeiten müssen.

Selbst wenn Sie eine richtige mittlere Ebene haben (aus dem angegebenen Beispiel scheint dies nicht der Fall zu sein), kann sich diese Ebene ändern und JBoss wird möglicherweise zu Ruby / Rails.

Andererseits ist es unwahrscheinlich, dass Sie das SQL-Backend durch etwas ersetzen, das keine relationale Datenbank mit SQL ist, und selbst wenn Sie dies tun, müssen Sie das Front-End ohnehin von Grund auf neu schreiben, sodass der Punkt umstritten ist.

Meine Idee ist, dass es bei Berechnungen in der Datenbank viel einfacher ist, später ein zweites Front-End oder eine zweite Ebene zu schreiben, da Sie nicht alles neu implementieren müssen. In der Praxis denke ich jedoch, dass "wo kann ich dies mit Code tun, den die Leute verstehen werden" der wichtigste Faktor ist.

Kajetan Abt
quelle
Wenn Sie von jboss zu ruby ​​wechseln, ist es sehr wahrscheinlich, dass Sie db ändern (und Sie müssen diese Berechnungen trotzdem übernehmen), und es ist nicht so unwahrscheinlich, dass Sie zu etwas anderem wie nosql wechseln können.
Dainius
0

Um die Beantwortung zu vereinfachen, müsste der Lastausgleich betrachtet werden. Sie möchten die Last dort platzieren, wo Sie die größte Kapazität haben (wenn dies sinnvoll ist). In den meisten Systemen wird der SQL Server schnell zu einem Engpass. Die Antwort lautet wahrscheinlich, dass SQL nicht eine Unze mehr Arbeit leisten soll als nötig.

In den meisten Architekturen sind es auch die SQL Server, die den Kern des Systems bilden, und externe Systeme, die hinzugefügt werden.

Aber die obige Mathematik ist so trivial, dass der beste Ort, an dem Sie es platzieren können, der Ort ist, an dem Sie es platzieren möchten, es sei denn, Sie bringen Ihr System an seine Grenzen. Wenn die Mathematik nicht trivial wäre, wie beispielsweise die Berechnung von sin / cos / tan für beispielsweise eine Entfernungsberechnung, könnte der Aufwand nicht trivial werden und eine sorgfältige Planung und Prüfung erfordern.

Donovanr
quelle
0

Die anderen Antworten auf diese Frage sind interessant. Überraschenderweise hat niemand Ihre Frage beantwortet. Sie fragen sich:

  1. Ist es besser, in der Abfrage in Cent umzuwandeln? Ich glaube nicht, dass die Besetzung von Cent Ihrer Anfrage etwas hinzufügt.
  2. Ist es besser, now () in der Abfrage zu verwenden? Ich würde es vorziehen, Daten in die Abfrage zu übergeben, anstatt sie in der Abfrage zu berechnen.

Weitere Informationen: Bei Frage 1 möchten Sie sicherstellen, dass die Aggregation der Brüche ohne Rundungsfehler funktioniert. Ich denke, die Zahl 19,2 ist für Geld angemessen und im zweiten Fall sind die ganzen Zahlen in Ordnung. Die Verwendung eines Floats für Geld ist aus diesem Grund falsch.

Bei Frage zwei möchte ich als Programmierer die volle Kontrolle darüber haben, welches Datum als "jetzt" gilt. Bei Verwendung von Funktionen wie now () kann es schwierig sein, automatische Komponententests zu schreiben. Wenn Sie ein längeres Transaktionsskript haben, kann es auch sinnvoll sein, eine Variable auf now () zu setzen und die Variable so zu verwenden, dass die gesamte Logik genau denselben Wert verwendet.

Chris Schoon
quelle
0

Lassen Sie mich ein reales Beispiel nehmen, um diese Frage zu beantworten

Ich musste einen gewichteten gleitenden Durchschnitt für meine Ohlc-Daten berechnen. Ich habe ungefähr 134000 Kerzen mit einem Symbol für jede, um dies zu tun

  1. Option 1 Machen Sie es in Python / Node etc etc.
  2. Option 2 Machen Sie es in SQL selbst!

Welches ist besser?

  • Wenn ich dies in Python tun müsste, müsste ich im schlimmsten Fall alle gespeicherten Datensätze abrufen, die Berechnung durchführen und alles zurückspeichern, was meiner Meinung nach eine enorme Verschwendung von E / A darstellt
  • Gewichtete Änderungen des gleitenden Durchschnitts jedes Mal, wenn Sie eine neue Kerze erhalten, was bedeutet, dass ich in regelmäßigen Abständen große Mengen an E / A durchführen würde, was in meinem Zeichen keine gute Meinung ist
  • In SQL muss ich wahrscheinlich nur einen Trigger schreiben, der alles berechnet und speichert, sodass nur ab und zu die endgültigen WMA-Werte für jedes Paar abgerufen werden müssen, und das ist viel effizienter

Bedarf

  • Wenn ich WMA für jede Kerze berechnen und speichern müsste, würde ich es auf Python tun
  • Da ich aber nur den letzten Wert benötige, ist SQL viel schneller als Python

Um Sie zu ermutigen, ist dies die Python-Version, um einen gewichteten gleitenden Durchschnitt zu erstellen

WMA erfolgt über Code

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 durch 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()

Ob Sie es glauben oder nicht, die Abfrage wird schneller ausgeführt als die Pure Python-Version eines WEIGHTED MOVING AVERAGE !!! Ich habe Schritt für Schritt diese Abfrage geschrieben, also bleib dran und du wirst es gut machen

Geschwindigkeit

0,42141127300055814 Sekunden Python

0,23801879299935536 Sekunden SQL

Ich habe 134000 gefälschte OHLC-Datensätze in meiner Datenbank, die auf 1000 Aktien aufgeteilt sind. Dies ist ein Beispiel dafür, wo SQL Ihren App-Server übertreffen kann

PirateApp
quelle
1
Wenn Sie dies jedoch millionenfach so schnell wie möglich tun müssen, ist es viel einfacher, parallele Python-Apps zu erstellen als DB-Replikate. Bis zu einer bestimmten Skala, die sich mehr auf SQL stützt, ist dies sicherlich schneller / billiger, aber irgendwann gibt es einen Wendepunkt, an dem es besser ist, diese Berechnung in Ihrer Anwendung durchzuführen.
Lenny