web-dev-qa-db-fra.com

Quels sont les avantages et les inconvénients d'effectuer des calculs en SQL par rapport à votre application

La table shopkeeper a les champs suivants:

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

Disons que j'ai le tableau ci-dessus. Je veux obtenir les enregistrements d’hier et Générer un rapport en imprimant le montant en cents.

Une façon de faire est d'effectuer des calculs dans mon application Java et d'exécuter une requête simple

Date previousDate ;// $1 calculate in application

Date todayDate;// $2 calculate in application

select amount where createddate between $1 and $2 

puis parcourez les enregistrements et convertissez le montant en centimes dans mon application Java et générez le rapport

Une autre façon est comme effectuer des calculs dans une requête SQL elle-même:

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

puis parcourez les enregistrements et générez le rapport

D'une manière, tous mes traitements sont effectués dans une application Java et une requête simple est lancée . Dans un autre cas, toutes les conversions et tous les calculs sont effectués dans une requête SQL.

Le cas d'utilisation ci-dessus n'est qu'un exemple. Dans un scénario réel, une table peut comporter de nombreuses colonnes nécessitant un traitement similaire.

Pouvez-vous s'il vous plaît me dire quelle approche est la meilleure en termes de performance et d'autres aspects et pourquoi?  

132
hellojava

Cela dépend de nombreux facteurs, mais le plus crucial:

  • complexité des calculs (préférez effectuer des calculs complexes sur un serveur d'applications, car cela redimensionne out; plutôt qu'un serveur de base de données, qui redimensionne up)
  • volume de données (si vous avez besoin d'accéder/d'agréger un grand nombre de données, le faire sur le serveur de base de données permettra d'économiser de la bande passante, et disque io si les agrégats peuvent être réalisés à l'intérieur des index)
  • commodité (sql n'est pas le meilleur langage pour un travail complexe - surtout pas génial pour un travail procédural, mais très bon pour un travail basé sur un ensemble; traitement des erreurs moche, cependant)

Comme toujours, si vous faites ramener les données sur le serveur d'applications, la réduction du nombre de colonnes et de lignes vous avantage. S'assurer que la requête est réglée et indexée de manière appropriée aidera l'un ou l'autre scénario.

Re votre note:

puis parcourez les enregistrements 

Boucler dans les enregistrements est presque toujours la mauvaise chose à faire dans SQL - l'écriture d'une opération basée sur un ensemble est préférable.

En règle générale, je préfère garder le travail de la base de données au minimum "stocker ces données, récupérer ces données" - cependant, il existe toujours des cas de scénarios dans lesquels une requête élégante sur le serveur peut économiser beaucoup de bande passante. .

Pensez également: si cela coûte cher en calcul, peut-il être mis en cache quelque part?

Si vous voulez un précis "ce qui est mieux"; codez-le dans les deux sens et comparez-le (en notant qu'un premier brouillon n'est probablement pas réglé à 100%). Mais tenez compte de l’utilisation typique: si, en réalité, il est appelé 5 fois (séparément) à la fois, simulez cela: ne comparez pas un seul "1 de ces éléments à 1 de ceux-ci".

187
Marc Gravell

Permettez-moi d’utiliser une métaphore: si vous voulez acheter un collier en or à Paris, l’orfèvre pourrait siéger au Cap ou à Paris, c’est une question de compétence et goût. Mais vous voudriez jamais expédier des tonnes de minerai d'or d'Afrique du Sud en France pour cela. Le minerai est traité sur le site minier (ou au moins dans la région), seul l'or est expédié. La même chose devrait être vraie pour les applications et les bases de données.

En ce qui concerne PostgreSQL , vous pouvez faire presque tout sur le serveur, de manière très efficace. Le SGBDR excelle dans les requêtes complexes. Pour les besoins de procédure, vous pouvez choisir parmi une variété de langages de script côté serveur : tcl, python, Perl et bien d’autres. La plupart du temps j'utilise PL/pgSQL , cependant.

Dans le pire des cas , le scénario serait de consulter régulièrement le serveur pour chaque ligne d'un ensemble plus volumineux. (Ce serait comme expédier une tonne de minerai par heure.)

Deuxième ligne , si vous envoyez une cascade de requêtes, chacune en fonction de la précédente, alors que tout cela peut être fait en une seule requête ou procédure le le serveur. (C'est comme si vous expédiez l'or, et chacun des bijoux avec un navire séparé, de manière séquentielle.)

Aller et venir entre l'application et le serveur coûte cher. Pour le serveur and client. Essayez de réduire cela, et vous gagnerez - ergo: utilisez les procédures côté serveur et/ou le SQL sophistiqué si nécessaire.

Nous venons de terminer un projet dans lequel nous intégrons presque toutes les requêtes complexes dans les fonctions Postgres. L'application transmet les paramètres et récupère les jeux de données dont elle a besoin. Rapide, propre, simple (pour le développeur de l'application), I/O réduite au minimum ... un collier brillant avec une faible empreinte carbone.

78
Erwin Brandstetter

Dans ce cas, probablement légèrement mieux vaut faire le calcul en SQL car le moteur de base de données aura probablement des routines arithmétiques décimales plus efficaces que Java.

En règle générale, il n'y a pas beaucoup de différence pour les calculs au niveau des lignes.

Voici ce qui fait la différence:

  • Calculs agrégés tels que SUM (), AVG (), MIN (), MAX (), dans ce cas, le moteur de base de données sera beaucoup plus rapide qu'une implémentation Java.
  • Partout où le calcul est utilisé pour filtrer les lignes. Filtrer au niveau de la base de données est beaucoup plus efficace que lire une ligne puis la jeter.
17
James Anderson

Il n'y a pas de noir/blanc quant aux parties de la logique d'accès aux données qui doivent être exécutées en SQL et aux parties qui doivent être exécutées dans votre application. J'aime Mark Gravell formulation, distinguant entre

  • calculs complexes
  • calculs gourmands en données

La puissance et l'expressivité de SQL sont fortement sous-estimées. Depuis l’introduction de fonctions de fenêtre , de nombreux calculs non strictement orientés sur les ensembles peuvent être effectués très facilement et avec élégance dans la base de données.

Trois règles empiriques doivent toujours être suivies, quelle que soit l'architecture globale de l'application:

  • garder la quantité de données transférée entre la base de données et l'application mince (au lieu de calculer des données dans la base de données)
  • garder la quantité de données chargée à partir du disque par la base de données mince (en faveur de laisser la base de données optimiser les instructions pour éviter un accès inutile aux données)
  • ne poussez pas la base de données dans les limites du nombre de processeurs avec des calculs complexes et simultanés (en faveur de l'extraction des données dans la mémoire de l'application et de l'exécution des calculs)

D'après mon expérience, avec un administrateur de base de données correct et une connaissance approfondie de votre base de données, vous ne rencontrerez pas très rapidement vos limites de processeurs de bases de données.

Quelques lectures supplémentaires où ces choses sont expliquées:

12
Lukas Eder

En règle générale, effectuez des opérations SQL s'il est probable que d'autres modules ou composants de projets identiques ou similaires devront également obtenir ces résultats. une opération atomique effectuée côté serveur est également préférable car il vous suffit d'appeler le processus stocké à partir de n'importe quel outil de gestion de base de données pour obtenir les valeurs finales sans traitement supplémentaire.

Dans certains cas, cela ne s'applique pas, mais quand c'est le cas, cela a du sens. aussi en général la boîte de dialogue a le meilleur matériel et les meilleures performances.

2
Davide Piras

Si vous écrivez sur ORM ou écrivez des applications occasionnelles peu performantes, utilisez le modèle qui simplifie l'application. Si vous écrivez une application hautes performances et réfléchissez bien à l’échelle, vous gagnerez en transférant le traitement vers les données. Je recommande fortement de déplacer le traitement vers les données. 

Pensons à cela en deux étapes: (1) Transactions OLTP (petit nombre d’enregistrements). (2) OLAP (analyses longues de nombreux enregistrements).

Dans le cas OLTP, si vous voulez être rapide (10 000 - 100 000 transactions par seconde), vous devez supprimer les conflits de verrouillage, verrouillé et verrou mort de la base de données. Cela signifie que vous devez éliminer les longs blocages dans les transactions: les allers-retours entre client et base de données pour transférer le traitement sur le client constituent un tel blocage. Vous ne pouvez pas avoir de transactions à long terme (rendre lecture/mise à jour atomique) et avoir un débit très élevé. 

Re: mise à l'échelle horizontale. Les bases de données modernes évoluent horizontalement. Ces systèmes implémentent déjà la haute disponibilité et la tolérance aux pannes. Tirez parti de cela et essayez de simplifier votre espace d'application.

Regardons OLAP - dans ce cas, il devrait être évident que ramener éventuellement des terrabytes de données dans l'application est une idée horrible. Ces systèmes sont spécialement conçus pour fonctionner de manière extrêmement efficace contre les données en colonnes compressées et pré-organisées. Les systèmes modernes OLAP évoluent également horizontalement et disposent de planificateurs de requêtes sophistiqués qui dispersent le travail horizontalement (déplacement interne du traitement vers les données).

1
Ryan

Pour simplifier la façon de répondre à cette question, il faudrait examiner l'équilibrage de la charge. Vous voulez placer la charge là où vous avez le plus de capacité (si cela a du sens). Dans la plupart des systèmes, c'est le serveur SQL qui devient rapidement un goulot d'étranglement. La réponse probable est donc que vous ne voulez pas que SQL fasse une once de travail plus que nécessaire. 

De plus, dans la plupart des architectures, ce sont le ou les serveurs SQL qui constituent le cœur du système et les systèmes extérieurs qui sont ajoutés. 

Mais le calcul ci-dessus est si trivial que, sauf si vous poussez votre système à l'extrême, le meilleur endroit pour le mettre est l'endroit où vous voulez le mettre. Si les calculs n'étaient pas triviaux, tels que le calcul de sin/cos/tan pour un calcul de distance, l'effort pourrait devenir non trivial et nécessiter une planification et des tests minutieux.

0
Donovanr

Permettez-moi de prendre un exemple concret pour répondre à cette question.

Je devais calculer une moyenne mobile pondérée sur mes données ohlc. J'ai environ 134 000 bougies avec un symbole pour chacune d'elles.

  1. Option 1 Faites-le en Python/Node, etc.
  2. Option 2 Faites-le en SQL même!

Quel est le meilleur?  

  • Si je devais le faire en Python, je devrais essentiellement récupérer tous les enregistrements stockés au pire des cas, effectuer le calcul et sauvegarder tout ce qui, à mon avis, est un énorme gaspillage d'E/S
  • La moyenne mobile pondérée change à chaque fois que vous recevez une nouvelle bougie, ce qui signifie que je ferais des quantités massives de IO à intervalles réguliers, ce qui n'est pas un Bonne opinion sur mon signe
  • En SQL, tout ce que j'ai à faire est probablement d'écrire un déclencheur qui calcule et stocke tout, il suffit donc d'extraire de temps en temps les valeurs WMA finales pour chaque paire, ce qui est bien plus efficace.

Exigences

  • Si je devais calculer WMA pour chaque bougie et la stocker, je le ferais sur Python
  • Mais comme je n’ai besoin que de la dernière valeur, SQL est beaucoup plus rapide que Python

Pour vous encourager, voici la version Python permettant de calculer une moyenne mobile pondérée.

WMA fait par 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 via 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()

Croyez-le ou non, la requête est plus rapide que la version Pure Python de la moyenne pondérée mobile !!! Je suis allé pas à pas dans la rédaction de cette requête, alors accrochez-vous et vous vous en sortirez très bien

La vitesse

0.42141127300055814 secondes Python

0.23801879299935536 secondes SQL

J'ai 134 000 faux enregistrements OHLC dans ma base de données, répartis sur 1 000 actions. Voici un exemple d'utilisation optimale de SQL par votre serveur d'applications.

0
PirateApp

Formez un point de vue des performances: il s'agit d'une opération arithmétique très simple qui peut presque certainement être effectuée beaucoup plus rapidement que d'extraire réellement les données des disques sous-jacents à la base de données. En outre, le calcul des valeurs de la clause where est susceptible d’être très rapide, peu importe l’exécution. En résumé, le goulot d'étranglement devrait être le disque IO, pas le calcul des valeurs.

En ce qui concerne la lisibilité, je pense que si vous utilisez un ORM, vous devriez le faire dans votre environnement de serveur d'applications, car l'ORM vous permettra de travailler très facilement avec les données sous-jacentes, à l'aide d'opérations basées sur les ensembles. De toute façon, si vous envisagez d'écrire du SQL brut, il n'y a rien de mal à faire le calcul ici. Votre SQL paraîtrait aussi un peu plus joli et plus facile à lire s'il est correctement formaté.

0
Johannes Gehrs

Les autres réponses à cette question sont intéressantes. Étonnamment, personne n'a répondu à votre question. Vous vous demandez:

  1. Est-il préférable de lancer Cents dans la requête? Je ne pense pas que la distributionto cents ajoute quoi que ce soit dans votre requête.
  2. Est-il préférable d'utiliser now () dans la requête? Je préférerais passer des dates dans la requête au lieu de les calculer dans la requête.

Plus d'infos: Pour la question un, vous voulez être sûr d’agréger les fractions fonctionne sans erreurs d'arrondi. Je pense que numeric 19,2 est raisonnable pour de l'argent et dans le second cas, les nombres entiers sont OK. Utiliser un flottant pour de l'argent est faux pour cette raison.

Pour la deuxième question, j'aime bien avoir le plein contrôle en tant que programmeur de quoi la date est considérée comme "maintenant". Il peut être difficile d’écrire une unité automatique teste lors de l'utilisation de fonctions comme now (). Aussi, quand vous avez un plus long script de transaction, il peut être utile de définir une variable égale à now () et d’utiliser la variable so que toute la logique utilise exactement la même valeur.

0
Chris Schoon

Surtout, la "performance" n'est pas définie.

Celui qui compte le plus pour moi est le temps consacré aux développeurs.

Écrivez la requête SQL. S'il est trop lent ou si la base de données devient un goulot d'étranglement, réexaminez-la. À ce moment-là, vous pourrez analyser les deux approches et prendre votre décision en fonction de données réelles pertinentes pour votre configuration (matériel et pile sur laquelle vous vous trouvez).

0
user2757750

Je ne crois pas que les différences de performances puissent être raisonnées sans exemples et points de repère spécifiques, mais j’en ai une autre:

Que pouvez-vous maintenir mieux? Par exemple, vous voudrez peut-être passer de Java à Flash, HTML5, C++ ou autre. Un grand nombre de programmes ont subi un tel changement, voire existent dans plusieurs langues, car ils doivent fonctionner sur plusieurs appareils.

Même si vous avez un calque intermédiaire approprié (d'après l'exemple donné, il semble que ce ne soit pas le cas), ce calque peut changer et JBoss peut devenir Ruby/Rails.

D'autre part, il est peu probable que vous remplaciez le backend SQL par quelque chose qui ne soit pas une base de données relationnelle avec SQL et même si vous le faites, vous devrez quand même réécrire le front-end à partir de zéro.

Mon idée est que si vous effectuez des calculs dans la base de données, il sera beaucoup plus facile d'écrire une deuxième couche frontale ou intermédiaire, car vous n'avez pas à tout ré-implémenter. En pratique cependant, je pense que "où puis-je faire cela avec du code que les gens comprendront" est le facteur le plus important.

0
Kajetan Abt

Que ce soit pour effectuer des calculs au début ou à l’arrière-plan est tout à fait décisif si nous pouvons déterminer notre objectif dans la mise en œuvre de l’entreprise. À l’époque, le code Java pourrait être plus performant qu’un code SQL bien écrit ou inversement. Mais quand même confus, vous pouvez essayer de déterminer d'abord -

  1. Si vous pouvez obtenir quelque chose de simple via la base de données SQL, il vaut mieux y aller, car DB fonctionnera beaucoup mieux et effectuera des calculs sur-le-champ et avec l'extraction du résultat. Cependant, si le calcul actuel nécessite trop de calculs ici et là, vous pouvez utiliser le code de l'application. Pourquoi? Parce que les scénarios, comme dans le cas d'une boucle, ne sont généralement pas mieux gérés par SQL, alors que les langues frontales sont mieux conçues pour ces choses.
  2. Dans le cas où un calcul similaire est requis pour de nombreux emplacements, il est préférable de placer le code de calcul à la fin de la base de données, il est préférable de conserver les éléments au même endroit.
  3. S'il y a beaucoup de calculs à faire pour atteindre le résultat final via de nombreuses requêtes différentes, optez pour db end, car vous pouvez placer le même code dans une procédure stockée pour obtenir de meilleurs résultats que de récupérer les résultats depuis le backend, puis de les calculer au premier plan fin.

Vous pouvez réfléchir à de nombreux autres aspects avant de décider où placer le code. Une perception est totalement fausse - tout peut être mieux fait en Java (code d'application) et/ou tout est préférable de le faire par la base de données (code SQL).

0
Neo