Sind einzelne Abfragen schneller als Verknüpfungen?


44

Konzeptionelle Frage: Sind einzelne Abfragen schneller als Verknüpfungen? Oder: Soll ich versuchen, alle Informationen, die ich auf der Clientseite haben möchte, in einer SELECT-Anweisung zusammenzufassen, oder einfach so viele verwenden, wie es zweckmäßig erscheint?

TL; DR : Wenn meine verknüpfte Abfrage länger dauert als die Ausführung einzelner Abfragen, ist dies meine Schuld oder ist dies zu erwarten?

Erstens bin ich nicht sonderlich datenbanktauglich, daher kann es nur an mir liegen, aber ich habe festgestellt, dass es "oft" schneller ist, diese Informationen über mehrere Abfragen für einzelne Tabellen abzurufen (möglicherweise), wenn ich Informationen aus mehreren Tabellen abrufen muss eine einfache innere Verknüpfung enthalten) und patchen Sie die Daten auf der Clientseite, um zu versuchen, eine (komplexe) verknüpfte Abfrage zu schreiben, in der alle Daten in einer Abfrage abgerufen werden können.

Ich habe versucht, ein sehr einfaches Beispiel zusammenzustellen:

SQL-Geige

Schema-Setup :

CREATE TABLE MASTER 
( ID INT NOT NULL
, NAME VARCHAR2(42 CHAR) NOT NULL
, CONSTRAINT PK_MASTER PRIMARY KEY (ID)
);

CREATE TABLE DATA
( ID INT NOT NULL
, MASTER_ID INT NOT NULL
, VALUE NUMBER
, CONSTRAINT PK_DATA PRIMARY KEY (ID)
, CONSTRAINT FK_DATA_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (ID)
);

INSERT INTO MASTER values (1, 'One');
INSERT INTO MASTER values (2, 'Two');
INSERT INTO MASTER values (3, 'Three');

CREATE SEQUENCE SEQ_DATA_ID;

INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.5);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.7);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 2, 2.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.14);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.7);

Abfrage A :

select NAME from MASTER
where ID = 1

Ergebnisse :

| NAME |
--------
|  One |

Abfrage B :

select ID, VALUE from DATA
where MASTER_ID = 1

Ergebnisse :

| ID | VALUE |
--------------
|  1 |   1.3 |
|  2 |   1.5 |
|  3 |   1.7 |

Abfrage C :

select M.NAME, D.ID, D.VALUE 
from MASTER M INNER JOIN DATA D ON M.ID=D.MASTER_ID
where M.ID = 1

Ergebnisse :

| NAME | ID | VALUE |
---------------------
|  One |  1 |   1.3 |
|  One |  2 |   1.5 |
|  One |  3 |   1.7 |

Natürlich habe ich keine Leistung mit diesen gemessen, aber man kann beobachten:

  • Abfrage A + B gibt dieselbe Menge verwendbarer Informationen zurück wie Abfrage C.
  • A + B muss 1 + 2x3 == 7 "Datenzellen" an den Client zurückgeben
  • C muss 3x3 == 9 "Datenzellen" an den Client zurückgeben, da ich beim Join natürlich etwas Redundanz in die Ergebnismenge mit einbeziehe.

Verallgemeinern (so weit es geht):

Eine verknüpfte Abfrage muss immer mehr Daten zurückgeben als die einzelnen Abfragen, die dieselbe Informationsmenge erhalten. Da die Datenbank die Daten zusammenfügen muss, kann bei großen Datenmengen davon ausgegangen werden, dass die Datenbank mehr Arbeit an einer einzelnen verknüpften Abfrage als an den einzelnen Abfragen leisten muss, da (zumindest) mehr Daten an den Client zurückgegeben werden müssen.

Würde sich daraus ergeben, dass die Aufteilung einer clientseitigen Abfrage in mehrere Abfragen zu einer besseren Leistung führt, oder würde dies eher bedeuten, dass ich die verknüpfte Abfrage durcheinander gebracht habe?


Kommentare sind nicht für eine längere Diskussion gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Jack Douglas

1
Ich habe einen Benchmark durchgeführt und die Ergebnisse in einem Artikel auf Medium veröffentlicht . Ich hätte hier eine Antwort hinzugefügt, habe sie aber bereits für eine andere Frage eingegeben, und das Posten derselben Antwort auf mehrere Fragen ist verpönt .
Benjamin

Antworten:


45

Sind einzelne Abfragen schneller als Verknüpfungen? Oder: Soll ich versuchen, alle Informationen, die ich auf der Clientseite haben möchte, in einer SELECT-Anweisung zusammenzufassen, oder einfach so viele verwenden, wie es zweckmäßig erscheint?

In jedem Leistungs Szenario Sie müssen testen und messen die Lösungen zu sehen , welche schneller ist .

Trotzdem ist es fast immer so, dass eine verknüpfte Ergebnismenge aus einer ordnungsgemäß optimierten Datenbank schneller und skalierbarer ist als die Rückgabe der Quellzeilen an den Client und die anschließende Verknüpfung derselben. Denken Sie insbesondere bei großen Eingabesätzen und kleinen Ergebnissätzen an die folgende Abfrage im Kontext beider Strategien: Verbinden Sie zwei Tabellen mit jeweils 5 GB und einer Ergebnismenge von 100 Zeilen. Das ist ein Extrem, aber du verstehst, was ich meine.

Ich habe festgestellt, dass es "oft" schneller ist, wenn ich Informationen aus mehreren Tabellen abrufen muss, diese Informationen über mehrere Abfragen in einzelnen Tabellen abzurufen (möglicherweise mit einer einfachen inneren Verknüpfung) und die Daten auf der Clientseite zu patchen, um dies zu versuchen eine (komplexe) verknüpfte Abfrage zu schreiben, in der ich alle Daten in einer Abfrage erhalten kann.

Es ist sehr wahrscheinlich, dass das Datenbankschema oder die Indizes verbessert werden, um die Abfragen besser zu bedienen, die Sie darauf abzielen.

Eine verknüpfte Abfrage muss immer mehr Daten zurückgeben als die einzelnen Abfragen, die dieselbe Informationsmenge erhalten.

Normalerweise ist dies nicht der Fall. Meistens ist die Ergebnismenge auch bei großen Eingabesätzen viel kleiner als die Summe der Eingaben.

Je nach Anwendung werden sehr große Abfrageergebnismengen, die an den Client zurückgegeben werden, sofort als rote Fahne angezeigt. Was macht der Client mit einer so großen Datenmenge, die nicht näher an der Datenbank erstellt werden kann? Das Anzeigen von 1.000.000 Zeilen für einen Benutzer ist, gelinde gesagt, sehr verdächtig. Die Netzwerkbandbreite ist auch eine begrenzte Ressource.

Da die Datenbank die Daten zusammenfügen muss, kann bei großen Datenmengen davon ausgegangen werden, dass die Datenbank mehr Arbeit an einer einzelnen verknüpften Abfrage als an den einzelnen Abfragen leisten muss, da (zumindest) mehr Daten an den Client zurückgegeben werden müssen.

Nicht unbedingt. Wenn die Daten korrekt indiziert sind, ist es wahrscheinlicher, dass die Verknüpfungsoperation in der Datenbank effizienter ausgeführt wird, ohne dass eine große Datenmenge gescannt werden muss. Darüber hinaus sind relationale Datenbank-Engines auf niedriger Ebene speziell für den Beitritt optimiert . Client-Stacks sind nicht.

Würde sich daraus ergeben, dass die Aufteilung einer clientseitigen Abfrage in mehrere Abfragen zu einer besseren Leistung führt, oder würde dies eher bedeuten, dass ich die verknüpfte Abfrage durcheinander gebracht habe?

Da Sie sagten, Sie seien unerfahren, wenn es um Datenbanken geht, empfehle ich Ihnen, mehr über das Datenbankdesign und die Leistungsoptimierung zu erfahren. Ich bin mir ziemlich sicher, dass hier das Problem liegt. Ineffizient geschriebene SQL-Abfragen sind ebenfalls möglich, bei einem einfachen Schema ist dies jedoch weniger wahrscheinlich.

Das heißt nicht, dass es keine anderen Möglichkeiten gibt, die Leistung zu verbessern. Es gibt Szenarien, in denen Sie einen mittleren bis großen Datensatz scannen und an den Client zurückgeben können, wenn eine Art Caching-Mechanismus verwendet werden soll. Zwischenspeichern kann großartig sein, bringt jedoch Komplexität in Ihr Design. Das Zwischenspeichern ist möglicherweise nicht einmal für Ihre Anwendung geeignet.

Eine Sache, die nirgendwo erwähnt wurde, ist die Aufrechterhaltung der Konsistenz der von der Datenbank zurückgegebenen Daten. Wenn separate Abfragen verwendet werden, ist es (aufgrund vieler Faktoren) wahrscheinlicher, dass inkonsistente Daten zurückgegeben werden, es sei denn, für jeden Abfragesatz wird eine Form der Snapshot-Isolation verwendet.


+1 für Netzwerkbandbreite ist ebenfalls eine begrenzte Ressource.
Hari Harker

OP sagt, dass JOINed Data Resultsets immer größer sind. > Eine verknüpfte Abfrage muss immer mehr Daten zurückgeben als die einzelnen Abfragen. Ich denke, das ist objektiv wahr (für> =), zB die Ergebnismengen unterscheiden sich in der Größe, also mehr Daten über die Leitung. Haben Sie ein Beispiel, bei dem dies nicht zutrifft? Wenn ich Autoren beitrete -> Beiträge und Autoren ein Feld mit der Bezeichnung "Biografie" hat, das 1 MB JSON-Feld für einen Autor von 100 Beiträgen ist, werde ich über das Netzwerk 100 MB vs 1 MB übertragen. Ist das falsch?
Hytromo

6

Natürlich habe ich keine Leistung mit diesen gemessen

Sie haben einen guten Beispielcode zusammengestellt. Haben Sie sich das Timing in SQL Fiddle angesehen? Sogar einige kurze, unwissenschaftliche Leistungstests zeigen, dass die Ausführung von Abfrage drei in Ihrer Demonstration ungefähr genauso lange dauert wie die von Abfrage eins oder zwei getrennt. Die Kombination von eins und zwei dauert ungefähr doppelt so lange wie drei, bevor ein clientseitiger Join ausgeführt wird.

Wenn Sie die Datenmenge erhöhen, weichen die Abfragegeschwindigkeiten eins und zwei voneinander ab, der Datenbank-Join ist jedoch immer noch schneller.

Sie sollten auch überlegen, was passieren würde, wenn der innere Join Daten entfernt.


2

Das Abfrageoptimierungsprogramm sollte ebenfalls berücksichtigt werden. Ihre Aufgabe ist es, Ihr deklaratives SQL in prozedurale Schritte zu übersetzen. Um die effizienteste Kombination von Verfahrensschritten zu finden, werden Kombinationen aus Indexnutzung, Sortierungen, Zwischenspeichern von Zwischenergebnissen und allerlei anderem untersucht. Die Anzahl der Permutationen kann selbst bei recht einfachen Abfragen außerordentlich groß werden.

Ein Großteil der Berechnungen zur Ermittlung des besten Plans basiert auf der Verteilung der Daten in den Tabellen. Diese Verteilungen werden abgetastet und als Statistikobjekte gespeichert. Wenn diese falsch sind, veranlassen sie den Optimierer, schlechte Entscheidungen zu treffen. Schlechte Entscheidungen zu Beginn des Plans führen zu noch schlechteren Entscheidungen in einem Schneeballeffekt.

Es ist nicht ungewöhnlich, dass eine mittelgroße Abfrage, die bescheidene Datenmengen zurückgibt, Minuten benötigt, um ausgeführt zu werden. Durch eine korrekte Indizierung und eine gute Statistik wird dies auf Millisekunden reduziert.


-3

Mehrere Abfragen sind der richtige Weg. Wenn Sie mit einfachen Szenarien wie diesen umgehen, ist der Kostenaufwand des Abfrageoptimierers ein Faktor. Mit mehr Daten tritt die Netzwerkineffizienz des Joins (redundante Zeilen) ein. Nur mit viel mehr Daten ist die Effizienz da.

Am Ende sehen viele Entwickler, was Sie erleben. Die Datenbankadministratoren sagen immer "Nein, mach einen Join", aber die Realität ist: In diesem Fall ist es schneller, mehrere einfache Auswahlen zu treffen.


5
Es gibt keine "Netzwerkineffizienz" bei einem Join - alles geschieht auf dem Datenbankserver, es ist also kein Netzwerk beteiligt (es sei denn, Sie treten über eine DB-Verbindung bei!)
Chris Saxon

2
Möglicherweise möchten Sie überlegen, ob die Netzwerkschicht komprimiert ist oder nicht. Das SQL * Net von Oracle bewirkt, dass Werte, die sich in derselben Spalte wiederholen, effizient komprimiert werden.
David Aldridge

3
@TomTom Sie haben vielleicht einen Punkt oder nicht (wie David Aldridge zeigt, ist die Komprimierung wichtig), aber Ihre Formulierung ist verwirrend. "Netzwerkineffizienz des Joins" ? Beheben Sie das wirklich, damit klar ist, was Sie meinen.
ypercubeᵀᴹ

@ChrisSaxon sicher, dass es gibt, Bild Sie haben Tabellen für einen Bericht "Titel-> Basis-> Tabellenzeilen" und Sie benötigen alle Zeilen, damit Sie diese 3 Tabellen innerlich verbinden. Jede Tabelle hat lange varchars, was also passiert, ist für jede Zeile, die Sie diese langen varchars wiederholen. Die Anwendungsebene muss allen diesen Zeichenfolgen Speicher zuweisen und sie dann für Ihr Modell gruppieren. Also ich denke das ist was er meint, es werden mehr Daten gesendet
MIKE

@MIKE, das von den von Ihnen ausgewählten Ausdrücken abhängt, nicht vom Join. Und es kann Netzwerkkomprimierung geben. In der Oracle-Datenbank entfernt SQL * Net wiederholte doppelte Werte. Nicetheory.io/2018/01/11/…
Chris Saxon
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.