SQL join und count VS. column für die Anzahl, was ist effizienter?

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Zoja
User
Beiträge: 133
Registriert: Freitag 28. Februar 2014, 14:04

Montag 26. März 2018, 14:33

Situation:

Ich habe eine table Projekte. Projekte können eine bestimmte Anzahl Videos besitzen. Um die Anzahl zu erfahren könnte ich die Projekte mit der videos table joinen und einen count() ausführen, dann wüsste ich wieviele Videos ein Projekt besitzt.

Ich könnte aber auch bei Projekte eine neue column anlegen "anzahl videos" und diese += 1 rechnen sobald ein neues video dazukommt. Ich spare mir den join und count, habe aber eine column mehr, die ich sonst hätte ausrechnen können.

Welche dieser beiden Ansätze ist besser? Was sollte man beachten?

Danke
Sirius3
User
Beiträge: 8414
Registriert: Sonntag 21. Oktober 2012, 17:20

Montag 26. März 2018, 14:54

@zoja: besser ist es immer, etwas zu berechnen, statt es händisch pflegen zu müssen.
Zoja
User
Beiträge: 133
Registriert: Freitag 28. Februar 2014, 14:04

Montag 26. März 2018, 14:57

Sirius3 hat geschrieben:@zoja: besser ist es immer, etwas zu berechnen, statt es händisch pflegen zu müssen.
Aber wie sieht es mit der Performance aus? Jedesmal wenn man die Projektseite aufrufen würde, würde das System einen join machen und count ausführen, um zu berechnen wieviele Videos zum Projekt gehören?
__deets__
User
Beiträge: 3479
Registriert: Mittwoch 14. Oktober 2015, 14:29

Montag 26. März 2018, 15:15

Hast du ein Performance Problem, oder antizipierst du eines? Wenn letzteres - warte ab.

Eine SQL-Abfrage verlaesst deinen Prozess via Netzwerk, landet in einem anderen Prozess, wird dort auseinander gepult, durch den Execution-Planer der DB-Engine geschoben, und erst dann wird da ueber irgendwelchen Tabellen gearbeitet. Und ggf. befinden die sich im Speicher, so das das recht flott geht. Und dann wieder zurueck durch die ganzen Schichten.

Das nun ausgerechnet dieser join da der Strohalm ist, der den Kamelruecken zerbricht - das ist unwahrscheinlich.

Alles in allem ist es eine schlechte Vorgehensweise, Performance-Probleme vorhersagen zu wollen. Und wenn kann man das auch nur anhand von klar definierten Kriterien. Denn schneller geht immer, nur wird irgendwann der Aufwand dafuer nicht mehr rechtfertigbar.

Hast du zB ueberhaupt fest definierte Ziele fuer die Laufzeit von Anfragen? Und wenn du die hast, muessen es *garantiert* weniger als x Millisekunden sein, oder duerfen zB 5% aller Anfragen darueber liegen?
Zoja
User
Beiträge: 133
Registriert: Freitag 28. Februar 2014, 14:04

Montag 26. März 2018, 15:34

__deets__ hat geschrieben:Hast du ein Performance Problem, oder antizipierst du eines? Wenn letzteres - warte ab.

Eine SQL-Abfrage verlaesst deinen Prozess via Netzwerk, landet in einem anderen Prozess, wird dort auseinander gepult, durch den Execution-Planer der DB-Engine geschoben, und erst dann wird da ueber irgendwelchen Tabellen gearbeitet. Und ggf. befinden die sich im Speicher, so das das recht flott geht. Und dann wieder zurueck durch die ganzen Schichten.

Das nun ausgerechnet dieser join da der Strohalm ist, der den Kamelruecken zerbricht - das ist unwahrscheinlich.

Alles in allem ist es eine schlechte Vorgehensweise, Performance-Probleme vorhersagen zu wollen. Und wenn kann man das auch nur anhand von klar definierten Kriterien. Denn schneller geht immer, nur wird irgendwann der Aufwand dafuer nicht mehr rechtfertigbar.

Hast du zB ueberhaupt fest definierte Ziele fuer die Laufzeit von Anfragen? Und wenn du die hast, muessen es *garantiert* weniger als x Millisekunden sein, oder duerfen zB 5% aller Anfragen darueber liegen?
Nein, das ist eher eine allgemeine Frage, das schwirt schon länger in meinem Kopf rum. Also ist es abhängig von der Situation.
__deets__
User
Beiträge: 3479
Registriert: Mittwoch 14. Oktober 2015, 14:29

Montag 26. März 2018, 17:34

Bei Performance immer. Es gibt zwei grundlegende Regeln:

- premature optimisation is the root of all evil. Optimiere nicht fuer ein Problem, von dem du noch nicht mal weisst, das du es hast. Natuerlich muss man sich deswegen nicht stulle bloed anstellen, aber wie ich gerade auch ganz akut auf arbeit merke: woher ploetzlich Performance-Probleme auftauchen weiss man gar nicht, und sollte dann auf sie reagieren, wenn sie wirklich auftreten.
- man normalisiert Datenbanken moeglichst weitgehend. Der Wikipedia-Eintrag dazu erklaert das ganz gut: https://de.wikipedia.org/wiki/Normalisi ... Datenbank)

Dein Vorgehen wuerde die 3. Normalform verletzen, denn die Anzahl der Videos laesst sich ja auch aus der Untertabelle bestimmen.
DasIch
User
Beiträge: 2465
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Montag 26. März 2018, 22:12

Grundsätzlich solltest du bei deiner primären Datenbank konservative Entscheidungen fällen, falls du die Daten einmal vermurkst hast ist es schwierig da wieder zu korrigieren. Dementsprechend würde ich von so einer Optimierung abraten selbst wenn du ein Performance Problem hättest.

Eine bessere Alternative wäre z.b. das Ergebnis des Queries in einem Cache zu halten. Gerade sowas lässt sich sehr leicht cachen, weil man solche Statistiken einfach periodisch im Hintergrund aktualisieren kann.
IHack
User
Beiträge: 14
Registriert: Dienstag 13. März 2018, 11:17

Dienstag 27. März 2018, 01:05

Wenn du für echte DBs schreibst, kannst du getrost davon ausgehen, dass die schneller sind, als du deine zusätzliche Videospalte der Projekte schreiben kannst.
Lass es.

Es widerspricht dem hehren Ziel der Atomisierung von DBs.
(Keine Information mehr als einmal. Egal ob implizit, oder explizit)
DasIch
User
Beiträge: 2465
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Dienstag 27. März 2018, 03:46

IHack hat geschrieben:Wenn du für echte DBs schreibst, kannst du getrost davon ausgehen, dass die schneller sind, als du deine zusätzliche Videospalte der Projekte schreiben kannst.
Das ist offensichtlich falsch. Mit einer separaten Spalte für die Anzahl hat man bessere algorithmische Komplexität und auch in der Praxis wird es definitiv schneller sein, sobald man soviele Daten hat dass das zählen einen signifikanten Zeitraum beansprucht.

Normalisierung ist auch nicht immer ideal und es gibt viele Situationen wo es Sinn macht oder es sogar empfehlenswert ist zu denormalisieren. Datenbanken bieten mit (materialized) views teilweise auch Features um dies leicht zu ermöglichen.
Sirius3
User
Beiträge: 8414
Registriert: Sonntag 21. Oktober 2012, 17:20

Dienstag 27. März 2018, 09:51

@DasIch: mit dem passenden Index ist das Zählen eine O(1)-Operation.
__deets__
User
Beiträge: 3479
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 27. März 2018, 10:09

@Sirius3 das wuerde mich wundern. Der Index sollte doch normalerweise in Form eines B-Tree oder aehnlichem daherkommen. Auch der traversiert sich doch nicht in O(1)-Zeit?
Benutzeravatar
snafu
User
Beiträge: 5531
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dienstag 27. März 2018, 13:11

Zoja hat geschrieben:Ich könnte aber auch bei Projekte eine neue column anlegen "anzahl videos" und diese += 1 rechnen sobald ein neues video dazukommt. Ich spare mir den join und count, habe aber eine column mehr, die ich sonst hätte ausrechnen können.
Ich würde das berechnen, solange der Zeitaufwand dafür relativ gering ist. Und das ist er oft. Wenn das Ergebnis häufig benötigt wird, dann liegt es wahrscheinlich eh im Cache deines DBMS. Da musst du diese Abeit und das Mehr an Verwaltung nicht zusätzlich machen.
shcol (Repo | Doc | PyPi)
DasIch
User
Beiträge: 2465
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Dienstag 27. März 2018, 14:03

Sirius3 hat geschrieben:@DasIch: mit dem passenden Index ist das Zählen eine O(1)-Operation.
Nur wenn du eine Tabelle mit einer Zeile hast in der der Count steht.
Benutzeravatar
/me
User
Beiträge: 3204
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Dienstag 27. März 2018, 14:21

DasIch hat geschrieben:Nur wenn du eine Tabelle mit einer Zeile hast in der der Count steht.
Es kann aber durchaus sein, dass die DB das intern ohnehin mitführt. Das weiß man allerdings üblicherweise nicht.

Die entscheidende Frage ist: Will man lieber etwas Performanceverlust beim Ermitteln der Anzahl oder will man bei jedem INSERT und DELETE händisch einen Counter anpassen (Race-Conditions nicht vergessen!)? Das hängt im Endeffekt vom Anwendungsfall ab und dabei insbesondere von der Frage, wie häufig die jeweiligen Operationen erwartungsgemäß durchgeführt werden.
DasIch
User
Beiträge: 2465
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Dienstag 27. März 2018, 19:25

snafu hat geschrieben:Wenn das Ergebnis häufig benötigt wird, dann liegt es wahrscheinlich eh im Cache deines DBMS.
Postgres hat keine Cache für Ergebnisse. MySQL hat den abgeschafft.

Einen Cache hat man üblicherweise nur für Queries (wegen Parsing Overhead), Tabellen und Indizes (in beiden Fällen wegen Disk I/O).
/me hat geschrieben:Es kann aber durchaus sein, dass die DB das intern ohnehin mitführt. Das weiß man allerdings üblicherweise nicht.
Sowas ist sehr gut dokumentiert, wenn man Performance Probleme hat sollte man dies wissen.

So einen Counter zu aktualisieren lässt sich z.B. über einen Trigger relativ problemlos machen. Relationelle Datenbanken kommen mit Concurrency schon ziemlich gut klar.

Wir reden hier nicht über zwei O(1) Operationen, von denen eine ein bisschen langsamer ist. Selbst wenn man eine Tabelle hat in einer Größenordnung von 100M Zeilen, was noch problemlos in den Arbeitsspeicher passt, kann es durchaus Minuten dauern bis man die Zeilen gezählt hat. Das ist alles andere als "etwas Performanceverlust".
Antworten