it-swarm.com.de

Was sind die Vor- und Nachteile von Berechnungen in sql vs. in Ihrer Anwendung

Die shopkeeper-Tabelle hat folgende Felder:

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

Sagen wir, ich habe die obige Tabelle. Ich möchte die Datensätze für gestern abrufen und Einen Bericht erstellen, indem der Betrag in Cent gedruckt wird.

Eine Möglichkeit ist, 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 Cent in meiner Java-Anwendung umrechnen und den Bericht generieren

Ein anderer Weg ist wie das Durchführen von Berechnungen in SQL-Abfrage selbst:

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

und dann die Datensätze durchlaufen und den Bericht generieren

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

Der obige Anwendungsfall ist nur ein Beispiel. In einem realen Szenario kann eine Tabelle viele Spalten enthalten, für die eine ähnliche Verarbeitung erforderlich ist.

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

132
hellojava

Das hängt von vielen Faktoren ab - vor allem aber entscheidend:

  • komplexität der Berechnungen (lieber komplexes Crunching auf einem App-Server, da out skaliert wird; anstelle eines Db-Servers, der up skaliert)
  • datenvolumen (wenn Sie auf eine Vielzahl von Daten zugreifen bzw. diese zusammenfassen müssen, sparen Sie auf dem Datenbankserver die Bandbreite und die Festplatte, wenn die Aggregate innerhalb von Indizes ausgeführt werden können)
  • bequemlichkeit (sql ist nicht die beste Sprache für komplexe Arbeiten - vor allem für prozessuale Arbeit nicht geeignet, aber sehr gut für satzbasiertes Arbeiten; schlechte Fehlerbehandlung)

Wenn Sie do die Daten zurück zum App-Server bringen, ist das Minimieren der Spalten und Zeilen zu Ihrem Vorteil. Wenn Sie sicherstellen, dass die Abfrage abgestimmt und entsprechend indiziert ist, wird dies für beide Szenarien hilfreich sein.

Re deine Notiz:

und dann die Datensätze durchlaufen 

Looping durch Datensätze ist fast immer das Falsche in SQL - das Schreiben einer set-basierten Operation wird bevorzugt.

Als generelle Regel halte 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 am Server viel Bandbreite sparen kann .

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

Wenn Sie ein genauer "wollen, der besser ist"; Codiere es in beide Richtungen und vergleiche es (wobei zu beachten ist, dass ein erster Entwurf wahrscheinlich nicht zu 100% abgestimmt ist). Aber bei der typischen Verwendung sollten Sie Folgendes berücksichtigen: Wenn es in Wirklichkeit fünfmal (separat) auf einmal aufgerufen wird, dann simulieren Sie das: Vergleichen Sie nicht nur eine "1 von diesen vs. 1 von diesen".

187
Marc Gravell

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

In Bezug auf PostgreSQL können Sie auf dem Server fast alles 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 verwende ich aber PL/pgSQL .

Im schlimmsten Fall wird für jede einzelne Zeile eines größeren Satzes wiederholt auf den Server zugegriffen. (Das wäre so, als würde man eine Tonne Erz pro Zeit verschicken.)

Zweitens in Zeile , wenn Sie eine Kaskade von Abfragen senden, von denen jede von der vorherigen abhängt, während alles in einer Abfrage oder Prozedur ausgeführt werden kann der Server. (Das ist so, als würde man das Gold und jedes Juwel nacheinander mit einem eigenen Schiff versenden.)

Das Hin- und Herwechseln zwischen App und Server ist teuer. Für Server und Client. Versuchen Sie, dies einzudämmen, 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), I/O auf ein Minimum reduziert ... eine glänzende Kette mit geringem CO2-Ausstoß.

78

In diesem Fall sind Sie wahrscheinlich etwas besser dran, wenn Sie die Berechnung in SQL durchführen, da die Datenbank-Engine wahrscheinlich eine effizientere Dezimal-Rechenroutine als Java hat.

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

Wo es einen Unterschied macht, ist:

  • Aggregierte Berechnungen wie SUM (), AVG (), MIN (), MAX () Hier ist das Datenbankmodul um eine Größenordnung schneller als eine Java-Implementierung.
  • Überall, wo die Berechnung zum Filtern von Zeilen verwendet wird. Das Filtern in der DB ist viel effizienter als das Lesen einer Zeile und das anschließende Löschen.
17
James Anderson

Es gibt kein Schwarz/Weiß in Bezug darauf, welche Teile der Datenzugriffslogik in SQL ausgeführt werden sollten und welche Teile in Ihrer Anwendung ausgeführt werden sollten. Ich mag Mark Gravells Wortlaut, zwischen zu unterscheiden

  • komplexe Berechnungen
  • datenintensive Berechnungen

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

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

  • die zwischen Datenbank und Anwendung übertragene Datenmenge gering halten (zugunsten der Berechnung von Daten in der Datenbank)
  • die von der Datenbank geladene Datenmenge gering halten (zugunsten der Datenbankoptimierungsanweisungen, um unnötigen Datenzugriff zu vermeiden)
  • schieben Sie die Datenbank nicht mit komplexen, gleichzeitigen Berechnungen an ihre CPU-Grenzen (zugunsten von Daten in den Anwendungsspeicher ziehen und dort Berechnungen durchführen).

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

Einige weitere Lektüre, wo diese Dinge erklärt werden:

12
Lukas Eder

Tun Sie im Allgemeinen Dinge in SQL, wenn die Wahrscheinlichkeit besteht, dass andere Module oder Komponenten in demselben oder anderen Projekten diese Ergebnisse erzielen. Eine atomare Operation, die auf der Serverseite ausgeführt wird, ist auch besser, weil Sie die gespeicherte Prozedur von einem beliebigen DB-Verwaltungstool aus aufrufen müssen, um die endgültigen Werte ohne weitere Verarbeitung zu erhalten.

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

2
Davide Piras

Wenn Sie über ORM oder gelegentlich Anwendungen mit geringer Leistung schreiben, verwenden Sie ein Muster, das die Anwendung vereinfacht. Wenn Sie eine Hochleistungsanwendung schreiben und sorgfältig über die Skalierung nachdenken, gewinnen Sie, wenn Sie die Verarbeitung auf Daten umstellen. Ich befürworte dringend, die Verarbeitung auf die Daten zu verschieben. 

Betrachten wir dies in zwei Schritten: (1) OLTP (kleine Anzahl von Datensätzen) Transaktionen. (2) OLAP (lange Scans vieler Datensätze).

Wenn Sie im Fall OLTP schnell sein möchten (Transaktionen zwischen 10.000 und 100.000 Transaktionen pro Sekunde), müssen Sie den Latch-, Lock- und Deadlock-Konflikt aus der Datenbank entfernen. Das bedeutet, dass Sie lange Transaktionen in Transaktionen vermeiden müssen: Roundtrips vom Mandanten in die DB, um die Verarbeitung zum Mandanten zu verschieben, sind eine solche lange Zeit. Sie können keine langlebigen Transaktionen haben (zum Lesen/Aktualisieren von Atomic) und einen sehr hohen Durchsatz haben. 

Re: horizontale Skalierung. Moderne Datenbanken sind horizontal skalierbar. 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 möglicherweise eine schreckliche Idee ist, möglicherweise Terrabytes 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 skalieren auch horizontal und verfügen über ausgefeilte Abfrageplaner, die die Arbeit horizontal verteilen (interne Verschiebung der Verarbeitung zu Daten).

1
Ryan

Um die Antwort auf diese Fragen zu vereinfachen, sollten Sie sich die Lastverteilung ansehen. Sie möchten die Last dort platzieren, wo Sie die größte Kapazität haben (wenn es Sinn macht). In den meisten Systemen wird der SQL-Server schnell zu einem Engpass. Daher ist die Antwort wahrscheinlich, dass Sie nicht möchten, dass SQL mehr als nötig ist. 

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

Die obige Rechnung ist jedoch so trivial, dass der beste Ort, an dem Sie ihn einsetzen möchten, an dem Ort liegt, an dem Sie ihn einsetzen möchten, es sei denn, Sie bringen Ihr System an die Grenze. Wenn die Berechnungen nicht trivial wären, z. B. die Berechnung von sin/cos/tan für eine Entfernungsberechnung, könnte der Aufwand nicht trivial sein und eine sorgfältige Planung und Prüfung erfordern.

0
Donovanr

Lassen Sie mich ein reales Beispiel nehmen, um diese Frage anzusprechen

Ich musste einen gewichteten gleitenden Durchschnitt für meine Ohlc-Daten berechnen. Ich habe ungefähr 134000 Kerzen mit einem Symbol dafür

  1. Option 1 In Python/Node usw. ausführen
  2. Option 2 Tun Sie es in SQL selbst!

Welches ist besser?  

  • Wenn ich dies in Python tun müsste, müsste ich im Wesentlichen alle gespeicherten Datensätze abrufen, die Berechnung durchführen und alles zurückspeichern, was meiner Meinung nach eine große Verschwendung von IO ist
  • Der gewichtete gleitende Durchschnitt ändert sich jedes Mal, wenn Sie eine neue Kerze bekommen, was bedeutet, dass ich regelmäßig IO in regelmäßigen Abständen machen würde, was in meinem Zeichen keine gute Meinung ist
  • In SQL muss ich wahrscheinlich nur einen Trigger schreiben, der alles berechnet und speichert, so dass ich nur ab und zu die endgültigen WMA-Werte für jedes Paar abrufen muss, und das ist viel effizienter

Bedarf

  • Wenn ich WMA für jede Kerze berechnen und speichern musste, würde ich es auf Python tun
  • Aber da ich nur den letzten Wert brauche, ist SQL viel schneller als Python

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

WMA erfolgt durch 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, wenn Sie einen GEWICHTETEN BEWEGUNGSVERKEHR durchführen !!! Ich habe diese Abfrage Schritt für Schritt 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

0
PirateApp

Aus Sicht der Leistung: Dies ist eine sehr einfache arithmetische Operation, die höchstwahrscheinlich viel schneller ausgeführt werden kann, als tatsächlich die Daten von den Festplatten abzurufen, die der Datenbank zugrunde liegen. Die Berechnung der Werte in der where-Klausel ist wahrscheinlich zu jeder Laufzeit sehr schnell. Zusammenfassend sollte der Engpass die Festplatten-E/A sein und nicht die Berechnung der Werte.

Ich denke, wenn Sie einen ORM verwenden, sollten Sie dies in Ihrer App-Server-Umgebung tun, da Sie mit ORM sehr einfach mit den zugrunde liegenden Daten arbeiten können, indem Sie set-basierte Vorgänge verwenden. Wenn Sie sowieso Raw-SQL schreiben, ist die Berechnung dort nicht falsch. Ihre SQL-Datei würde auch etwas schöner aussehen und besser lesbar sein, wenn sie richtig formatiert ist.

0
Johannes Gehrs

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

  1. Ist es besser, in der Abfrage auf Cents zu setzen? Ich glaube nicht, dass der Cast etwas in Ihre Anfrage einfügt.
  2. Ist es besser, jetzt () in der Abfrage zu verwenden? Ich würde es vorziehen, Datumsangaben in die Abfrage zu übergeben, anstatt sie in der Abfrage zu berechnen.

Weitere Informationen: Für Frage 1 möchten Sie sicher sein, dass die Fraktionen funktioniert ohne Rundungsfehler. Ich denke numerisch 19,2 ist vernünftig für Geld und im zweiten Fall sind die ganzen Zahlen in Ordnung. Aus diesem Grund ist die Verwendung eines Floats für Geld falsch.

Für die zweite Frage möchte ich als Programmierer volle Kontrolle darüber haben, was Datum wird als "jetzt" betrachtet. Es kann schwierig sein, eine automatische Einheit zu schreiben testet bei Verwendung von Funktionen wie now (). Auch wenn Sie eine längere Zeit haben Transaktionsskript Es kann sinnvoll sein, eine Variable gleich now () zu setzen und die Variable also .__ zu verwenden. dass alle Logik den gleichen Wert verwendet.

0
Chris Schoon

Entscheidend ist "Leistung" nicht definiert.

Das Wichtigste für mich 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 bewerten und Ihre Entscheidung basierend auf realen Daten treffen, die für Ihr Setup relevant sind (Hardware und welchen Stack Sie gerade verwenden).

0
user2757750

Ich glaube nicht, dass die Leistungsunterschiede ohne konkrete Beispiele und Benchmarks begründet werden können, aber ich habe noch einen anderen Ansatz:

Was kannst du besser behaupten? Sie möchten beispielsweise Ihr Frontend von Java auf Flash oder HTML5 oder C++ oder etwas anderes umstellen. Eine große Anzahl von Programmen hat eine solche Änderung durchgemacht oder existiert sogar in mehr als einer Sprache, weil sie auf mehreren Geräten arbeiten müssen.

Selbst wenn Sie über eine richtige mittlere Ebene verfügen (aus dem angegebenen Beispiel scheint das nicht der Fall zu sein), kann sich diese Ebene ändern und JBoss kann zu Ruby/Rails werden.

Andererseits ist es unwahrscheinlich, dass Sie das SQL-Backend durch etwas ersetzen, das keine relationale Datenbank mit SQL ist, und selbst wenn Sie es tun, müssen Sie das Frontend trotzdem neu schreiben, so dass es umstritten ist.

Meine Idee ist, dass, wenn Sie Berechnungen in der Datenbank durchführen, es viel einfacher ist, später ein zweites Frontend oder eine mittlere Schicht zu schreiben, da Sie nicht alles neu implementieren müssen. In der Praxis denke ich jedoch, "wo kann ich das mit Code tun, den die Leute verstehen werden", der wichtigste Faktor ist.

0
Kajetan Abt

Ob Berechnungen im Frontend oder im Backend durchgeführt werden, ist sehr entschieden, wenn wir unser Ziel bei der Geschäftsimplementierung bestimmen können. Zur Zeit ist Java-Code möglicherweise besser als ein SQL-Code, der sowohl gut geschrieben ist als auch umgekehrt. Aber wenn Sie verwirrt sind, können Sie zunächst feststellen,

  1. Wenn Sie etwas über die Datenbank-SQL leicht erreichen können, sollten Sie dies besser tun, da db viel bessere Ergebnisse erzielt und Berechnungen dort und dann mit dem Ergebnisabruf vornimmt. Wenn jedoch die tatsächliche Berechnung von hier und da zu viel Berechnung erfordert, können Sie mit dem Anwendungscode fortfahren. Warum? Da Szenarien wie Schleifen in den meisten Fällen nicht optimal von SQL behandelt werden, sind die Front-End-Sprachen für diese Dinge besser geeignet.
  2. Für den Fall, dass eine ähnliche Berechnung von vielen Stellen aus erforderlich ist, ist es offensichtlich besser, den Berechnungscode am Ende der Datenbank zu platzieren.
  3. Wenn viele Berechnungen erforderlich sind, um das Endergebnis über viele verschiedene Abfragen zu erzielen, gehen Sie auch für db end, da Sie den gleichen Code in eine gespeicherte Prozedur einfügen können, um bessere Ergebnisse zu erzielen, als die Ergebnisse aus dem Backend abzurufen und diese dann vorne zu berechnen Ende.

Es gibt viele andere Aspekte, an die Sie denken können, bevor Sie sich entscheiden, wo der Code platziert werden soll. Eine Wahrnehmung ist völlig falsch - Alles lässt sich am besten in Java (App-Code) und/oder am besten mit dem DB (SQL-Code) erledigen.

0
Neo