2011-01-12 14 views
1

Während ich größere, fortschrittlichere Webanwendungen erstelle, stelle ich fest, dass ich extrem lange und komplexe Abfragen schreibe. Ich schreibe häufig Abfragen in Abfragen, weil ich glaube, dass ein Aufruf an die Datenbank von PHP besser ist, als mehrere zu machen und die Daten zu korrelieren.Abfragen in Abfragen: Gibt es einen besseren Weg?

Allerdings weiß jeder, der etwas über SQL weiß über JOIN s. Persönlich habe ich eine JOIN oder zwei zuvor verwendet, aber schnell gestoppt, als ich die Verwendung von Unterabfragen entdeckte, weil es sich leichter und schneller für mich zu schreiben und zu pflegen fühlte.

Normalerweise werde ich Unterabfragen tun, die eine oder mehrere Unterabfragen von relativen Tabellen enthalten können.
Betrachten Sie dieses Beispiel:

SELECT 
    (SELECT username FROM users WHERE records.user_id = user_id) AS username, 
    (SELECT last_name||', '||first_name FROM users WHERE records.user_id = user_id) AS name, 
    in_timestamp, 
    out_timestamp 
FROM records 
ORDER BY in_timestamp 

Selten, ich werde Subqueries nach der WHERE Klausel tun.
Betrachten Sie dieses Beispiel:

SELECT 
    user_id, 
    (SELECT name FROM organizations WHERE (SELECT organization FROM locations WHERE records.location = location_id) = organization_id) AS organization_name 
FROM records 
ORDER BY in_timestamp 

In diesen beiden Fällen würde ich sehen, jede Art von Verbesserung, wenn ich die Abfragen mit einem JOIN neu zu schreiben entschieden?

Wie eine allgemeine Frage, was sind die Vor-/Nachteile der Verwendung von Unterabfragen oder JOIN? Ist der eine Weg richtiger oder akzeptierter als der andere?

+0

Ich wollte nur erwähnen, dass das Lernen, wie Joins arbeiten zunächst verwirrend ist, aber wenn Sie sie verwenden, dauert es nicht lange, bis sie sich natürlich fühlen. –

Antwort

1

JOINs sind vorzuziehen, um [Sub-] Abfragen zu trennen.
Wenn der Subselect (AKA-Unterabfrage) nicht mit der äußeren Abfrage korreliert ist, ist es sehr wahrscheinlich, dass der Optimierer die Tabelle (n) im Subselect einmal durchsucht, da sich der Wert wahrscheinlich nicht ändert. Wenn Sie eine Korrelation haben, wie im Beispiel angegeben, wird die Wahrscheinlichkeit einer Single-Pass-Optimierung sehr unwahrscheinlich. In der Vergangenheit wurde geglaubt, dass korrelierte Unterabfragen ausgeführt werden, RBAR - Row By Agonizing Row. Mit einem JOIN kann das gleiche Ergebnis erzielt werden, während ein einzelner Durchlauf über die Tabelle sichergestellt wird.

Dies ist eine richtige Umschreiben der Abfrage zur Verfügung gestellt:

SELECT u.username, 
      u.last_name||', '|| u.first_name AS name, 
      r.in_timestamp, 
      r.out_timestamp 
    FROM RECORDS r 
LEFT JOIN USERS u ON u.user_id = r.user_id 
ORDER BY r.in_timestamp 

... weil die subselect NULL zurückgeben können, wenn der user_id nicht in der USERS Tabelle nicht vorhanden ist. Anderenfalls könnten Sie einen INNER JOIN verwenden:

SELECT u.username, 
     u.last_name ||', '|| u.first_name AS name, 
     r.in_timestamp, 
     r.out_timestamp 
    FROM RECORDS r 
    JOIN USERS u ON u.user_id = r.user_id 
ORDER BY r.in_timestamp 

Abgeleitete Tabellen/Inline-Ansichten sind auch mit der JOIN-Syntax möglich.

+2

PostgreSQL wird korrelierte Unterabfragen automatisch zu Joins optimieren. –

+0

Nachdem ich das gelesen hatte, beschloss ich, einige Tests mit den von Ihnen bereitgestellten Informationen durchzuführen. Ich habe eine bestehende Abfrage ausgeführt, die ~ 500 Ergebnisse dreimal zurückgibt. Die durchschnittliche Ausführungszeit betrug ** 604.361 ms **. Ich schrieb dann die Abfrage mit einem 'JOIN' identisch mit Ihrem Beispiel und führte es dreimal aus. Die durchschnittliche Ausführungszeit betrug ** 589,693 **. Das ist nur ein Unterschied von ** 14.668ms **.Wenn du in meinen Schuhen wärst, würde ~ 14ms eine Abfrage wert sein, durch meinen Code zurückzugehen und jeden zu ändern? –

+0

@miringo: Es hängt von der Menge der Daten ab, von den Indizes (falls vorhanden) und davon, wie die Daten gespeichert sind. Die Tatsache, dass es einen Unterschied gibt, zeigt, dass Peters Kommentar falsch ist. –

2

In einfachen Fällen sollte der Abfrageoptimierer in der Lage sein, identische Pläne für einen einfachen Join oder einen einfachen Subselect zu erstellen.

Aber im Allgemeinen (und gegebenenfalls) sollten Sie Joins über Subselects bevorzugen.

Plus, sollten Sie korrelierte Unterabfragen vermeiden (eine Abfrage, in der der innere Ausdruck auf die äußere beziehen), da sie effektiv eine for-Schleife innerhalb einer for-Schleife sind). In den meisten Fällen kann eine korrelierte Unterabfrage als Join geschrieben werden.

+0

+1 für identische Pläne Punkt. Es wäre schön zu sehen, warum Joins besser sind als Subselects. –

+0

Nicht korrelierte Abfragen könnten geschrieben werden, um ein CROSS JOIN/kartesisches Produkt zu verwenden. Und während "EXISTS" -Klauseln typischerweise korreliert sind, sind sie so optimiert, dass sie anders arbeiten als das Ergebnis einer Unterabfrage oder die Verwendung der "IN" -Klausel. –

0

Joins in den meisten Fällen wird viel schneller sein.

Nehmen wir das mit einem Beispiel.

Hier können Sie Ihre erste Abfrage verwenden.

SELECT 
(SELECT username FROM users WHERE records.user_id = user_id) AS username, 
    (SELECT last_name||', '||first_name FROM users WHERE records.user_id = user_id) AS name, 
    in_timestamp, 
    out_timestamp 
FROM records 
ORDER BY in_timestamp 

Nun betrachten wir haben 100 Datensätze in Aufzeichnungen und 100 Datensätze in Benutzer (Angenommen, wir haben Index auf user_id nicht)

Also, wenn wir verstehen, Ihr Algorithmus es sagt: Für jeden Datensatz Scan alle 100 Datensätze in Benutzer Benutzername Scan alle 100 Datensätze in den Benutzern, um herauszufinden, Nachname, um herauszufinden, und Vornamen

seiner So wie wir gescannte Benutzer Tabelle 100 * 100 * 2 Mal. Ist es das wirklich wert? Wenn wir Index auf user_id betrachten, wird es das besser machen, aber es lohnt sich immer noch.

Betrachten Sie nun einen Join (geschachtelte Schleife wird fast das gleiche Ergebnis wie oben, aber einen Hash-Join): Es ist wie. Erstellen Sie eine Hash-Karte des Benutzers. Für jeden Datensatz Suchen Sie in Hasmap einen Zuordnungsdatensatz. Das wird sicherlich viel schneller sein als ein Looping und einen Rekord zu finden.

Also klar, Joins sollte günstig sein.

HINWEIS: Beispiel von 100 Datensatz verwendet möglicherweise identische Plan, aber die Idee ist zu analysieren, wie es die Leistung beeinflussen kann.

+0

PostgreSQL wird korrelierte Unterabfragen automatisch zu Joins optimieren. –

1

a) Ich würde damit beginnen, darauf hinzuweisen, dass die beiden nicht unbedingt austauschbar sind. Verschachteln, da Sie benötigen, dass es 0 oder 1 übereinstimmenden Wert gibt, sonst erhalten Sie einen Fehler. Ein Join stellt keine solche Anforderung und kann den Datensatz ausschließen oder mehr einführen, abhängig von Ihren Daten und dem Join-Typ.

b) In Bezug auf die Leistung müssen Sie die Abfragepläne überprüfen, aber Ihre verschachtelten Beispiele sind wahrscheinlich nicht effizienter als ein Tabellenbeitritt. In der Regel werden Unterabfragen einmal pro Zeile ausgeführt, aber das hängt sehr von Ihrer Datenbank, eindeutigen Einschränkungen, Foriegn Schlüsseln, nicht Null usw. ab. Vielleicht kann die DB effizienter umschreiben, aber Joins können eine breitere Vielfalt von Techniken verwenden, um die Daten von verschiedenen zu steuern Tabellen usw., weil sie verschiedene Dinge tun (obwohl Sie möglicherweise keinen Unterschied in Ihrer Ausgabe abhängig von Ihren Daten beobachten).

c) Die meisten DB-bewussten Programmierer, die ich kenne, würden sich Ihre verschachtelten Abfragen anschauen und sie mit Joins überschreiben, vorausgesetzt, die Daten sind entsprechend 'sauber'.

d) In Bezug auf "Korrektheit" - würde ich Joins bevorzugen, die bei Bedarf mit geeigneten Einschränkungen für Ihre Daten gesichert werden (z. B. eine eindeutige Benutzer-ID). Sie als Mensch können bestimmte Annahmen treffen, aber die DB-Engine kann nicht, es sei denn, Sie sagen es. Je mehr es weiß, desto besser kann es (und Sie) tun.

Verwandte Themen