2016-07-31 3 views
7

Wenn ich die folgenden beiden Tabellen:Beschleunigung wählen, wo Spalte Zustand ohne Duplikate in einer anderen Tabelle existiert

  1. Tabelle "a" mit 2 Spalten: id (int) [Primary Index], column1 [Indiziert]
  2. Tabelle "b" mit 3 Spalten: id_table_a (int), condition1 (int), condition2 (int) [alle Spalten als Primärindex]

ich kann die folgende Abfrage ausführen Zeilen aus der Tabelle einer auszuwählen wobei Tabelle b Bedingung1 ist 1

SELECT a.id FROM a WHERE EXISTS (SELECT 1 FROM b WHERE b.id_table_a=a.id && condition1=1 LIMIT 1) ORDER BY a.column1 LIMIT 50 

Mit ein paar hundert Millionen Zeilen in beiden Tabellen ist diese Abfrage sehr langsam. Wenn ich das tue:

SELECT a.id FROM a INNER JOIN b ON a.id=b.id_table_a && b.condition1=1 ORDER BY a.column1 LIMIT 50 

Es ist so ziemlich sofort, aber wenn es mehr passenden Zeilen in der Tabelle b übereinstimmen id_table_a dann Duplikate zurückgegeben. Wenn ich eine SELECT DISTINCT oder GROUP BY a.id mache, um Duplikate zu entfernen, wird die Abfrage extrem langsam.

Hier zeigt ein SQLFiddle die Beispielabfragen: http://sqlfiddle.com/#!9/35eb9e/10

Gibt es eine Möglichkeit ein, ohne Duplikate schnell in diesem Fall kommen zu machen?

* zeigen Edited dass INNER statt LEFT JOIN nicht viel Unterschied machen hat

* Herausgegeben zeigen Zustand viel Unterschied

* LIMIT hinzufügen Herausgegeben hat noch keinen beizutreten bewegen

* Herausgegeben ORDER BY hinzufügen

+0

Ungefähr wie viele Zeilen werden von der rohen (schnellen) Join-Version zurückgegeben? – Bohemian

+0

Entschuldigung, ich habe jetzt die LIMIT 50 zur Frage hinzugefügt. Es sollte 50 oder was auch immer kleines Limit von den Hunderten von Millionen von Zeilen zurückgeben. – JJJ

+0

Ist die 'Reihenfolge nach 'wirklich erforderlich? Werden 50 tun? – Bohemian

Antwort

0
SELECT id FROM a INNER JOIN b ON a.id=b.id_table_a AND b.condition1=1 

Nehmen Sie die Bedingung in die ON Klausel der Verbindung, auf diese Weise den Index Tabelle b kann verwendet werden, um zu filtern. Verwenden Sie auch INNER JOIN über LEFT JOIN

Dann sollten Sie weniger Ergebnisse haben, die gruppiert werden müssen.

+0

Danke für die Hilfe. Leider, wenn ich die Bedingung auf den inneren Join verschiebe, wird es immer noch Duplikate verursachen und wenn ich SELECT DISTINCT oder GROUP BY a.id hinzufügte, um Duplikate zu entfernen, war es zu langsam. – JJJ

+0

Können Sie uns ein kleines Beispiel geben, wie Ihre Daten aussehen und wie das Ergebnis aussehen sollte? Am besten über sqlfiddle. Ich stelle eins so auf, sieht es richtig aus? http://sqlfiddle.com/#!9/af1e68/3 – Philipp

+0

Ja, das ist perfekt, aber meine Tabellen haben> 100 Millionen Zeilen für jede Tabelle. Hier ist die Erklärung, wenn ich eine einfache Verbindung mit distinct habe: http://pasteboard.co/2VrzV0da6.png Hier ist die Erklärung ohne die eindeutige: http://pasteboard.co/2Vs4X1rmR.png Ich habe eine LIMIT 50 bei beiden Abfragen btw. – JJJ

1

können Sie versuchen, mit INNER JOIN und deutlich

SELECT distinct a.id 
FROM a INNER JOIN b ON a.id=b.id_table_a AND b.condition1=1 

aber bei der Auswahl der verschiedenen natürlichen * sicherzustellen, dass Sie nicht eindeutige ID zu tun, die

SELECT distinct col1, col2, col3 .... 
FROM a INNER JOIN b ON a.id=b.id_table_a AND b.condition1=1 

Sie könnten in diesem Fall die Verwendung falschen Ergebnis zurück füge auch einen zusammengesetzten Index hinzu, benutze auch condtition1 zB: key (id, condition1)

wenn du kannst kannst du auch eine

durchführen 210
ANALYZE TABLE table_name; 

auf dem Tisch ..

und eine andere Technik ist versuchen, die Führung Tabelle zurückkehrt

SELECT distinct a.id 
FROM b INNER JOIN a ON a.id=b.id_table_a AND b.condition1=1 

Mit der selektivsten Tabelle für Blei die Abfrage

Mit dieser unterschiedlichen scheinen die Verwendung von Index http://sqlfiddle.com/#!9/35eb9e/15 (das letzte Unternehmen ein mit dem

)
# USING DISTINCT TO REMOVE DUPLICATES without col and order 
EXPLAIN 
SELECT DISTINCT a.id 
FROM a 
INNER JOIN b ON a.id=b.id_table_a AND b.condition1=1 
; 
+0

Ich habe versucht, mit DISTINCT, aber es verlangsamt die Abfrage um einen extremen Betrag. Aus irgendeinem Grund ist ein Join mit ein paar Duplikaten Tonnen schneller als ein Beitritt mit DISTINCT. Bei Verwendung von DISTINCT a.id – JJJ

+0

$ condition1 ist eine Spalte? Wenn ja, versuchen Sie diese Spalte (oder die zugehörige Spalte) zu einem Index hinzuzufügen zB: key (id, condition1) – scaisEdge

+0

ja condition1 ist eine Spalte und Teil des primären Index mit id_table_a weshalb die Join Abfrage mit Duplikaten sehr schnell ist – JJJ

0

Wickeln Sie die schnelle Version in einer Abfrage, die de-duping und Limit Griffe:

SELECT DISTINCT * FROM (
    SELECT a.id 
    FROM a 
    JOIN b ON a.id = b.id_table_a && b.condition1 = 1 
) x 
ORDER BY column1 
LIMIT 50 

Wir wissen, dass die innere Abfrage schnell. Das Deduping und Ordering muss irgendwo passieren. So geschieht es auf dem kleinsten möglichen Rowset.

Siehe SQLFiddle.


Option 2:

Versuchen Sie Folgendes:

Indizes erstellen wie folgt:

create index a_id_column1 on a(id, column1) 
create index b_id_table_a_condition1 on b(a_table_a, condition1) 

Diese werden abdeckt Indizes - diejenigen, die alle Spalten enthalten, die Sie benötigen für die Abfrage, was wiederum bedeutet, dass der Nur-Index-Zugriff auf Daten das Ergebnis erreichen kann.

? Dann versuchen:

SELECT * FROM (
    SELECT a.id, MIN(a.column1) column1 
    FROM a 
    JOIN b ON a.id = b.id_table_a 
    AND b.condition1 = 1 
    GROUP BY a.id) x 
ORDER BY column1 
LIMIT 50 
+0

Wow danke, ich denke, das ist das bisher Nächste. Wie würde ich jedoch ein Limit hinzufügen? Wenn ein Limit für die innere Abfrage gilt, werden Zeilen nicht berücksichtigt, und wenn die äußere Begrenzung begrenzt ist, wird die gesamte Abfrage extrem langsam. Einige Zeilen haben 100k + Duplikate. – JJJ

+0

Es gibt keine Ordnung in der Reihenfolge, die eine O (n log n) Zeit Komplexität hat. Ich denke, das ist dein (leider unvermeidliches) Problem hier. – Bohemian

+0

Versuchen Sie die veröffentlichte neue Abfrage. Es könnte gut funktionieren. 100K ist keine "große" Anzahl von zu sortierenden Zeilen. – Bohemian

1

Es sieht aus wie ich die Antwort gefunden.

SELECT a.id FROM a 
INNER JOIN b ON 
    b.id_table_a=a.id && 
    b.condition1=1 && 
    b.condition2=(select b.condition2 from b WHERE b.id_table_a=a.id && b.condition1=1 LIMIT 1) 
ORDER BY a.column1 
LIMIT 5; 

Ich weiß nicht, ob es einen Fehler in dieser ist oder nicht, lassen Sie es mich wissen, wenn dies der Fall. Wenn jemand eine Möglichkeit, diese zu komprimieren hat irgendwie werde ich gerne Ihre Antwort akzeptieren.

+0

Ich konnte nicht glauben, dass dieses schneller als die ursprüngliche EXISTS-Lösung war, bis ich es getestet habe. Der Leistungsunterschied ist enorm.Hier sind einige Variationen dieser Lösung: http://sqlfiddle.com/#!9/09ac84/15 - Die ersten beiden Bedingungen müssen keine Unterabfrage verwenden. Die letzte Bedingung ist diejenige, die die Duplikate entfernt. –

+0

Ja, ich konnte es auch nicht glauben, bis ich es getestet habe und es ist die schnellste Lösung von allen von ihnen (momentan) und am einfachsten (wenn komprimiert). Danke für deine komprimierte Version. Ich war zu müde, um zu bemerken, dass die 2 Unterabfragen unnötig waren, aber aus irgendeinem Grund, wahrscheinlich aufgrund des Optimizer-Cache, ist es genauso schnell wie die komprimierte Version, die Sie haben. Wenn Sie Ihre Antwort mit der komprimierten Version bearbeiten, die Sie haben, dann werde ich diese akzeptieren, da sie die korrekteste und leistungsfähigste von allen ist. – JJJ

+0

@jjj AFAICT dies ist nicht dasselbe wie Ihre ursprüngliche Abfrage, weil die neue Abfrage einschränkt condition2' zu einem einzelnen zufällig gewählten Wert aller möglichen Werte. Um dasselbe zu sein, müssten Sie 'b.condition2 = (wählen Sie b.condition2 von b WHERE b.id_table_a = a.id && b.condition1 = 1 LIMIT 1)' zu 'b.condition2 IN (wählen Sie b. Bedingung2 von b WHERE b.id_table_a = a.id && b.condition1 = 1) '- dh entferne die' LIMIT' – Bohemian

0

Ihre schnelle Abfrage in einem subselect verwenden und die Duplikate in den äußeren wählen entfernen:

SELECT DISTINCT sub.id 
FROM (
    SELECT a.id 
    FROM a 
    INNER JOIN b ON a.id=b.id_table_a && b.condition1=1 
    WHERE b.id_table_a > :offset 
    ORDER BY a.column1 
    LIMIT 50 
) sub 

Da Duplikate entfernen Sie könnten weniger als 50 Zeilen erhalten. Wiederholen Sie einfach die Abfrage, bis Sie genügend Zeilen erhalten. Beginnen Sie mit :offset = 0. Verwenden Sie die letzte ID aus dem letzten Ergebnis als :offset in den folgenden Abfragen.

Wenn Sie Ihre Statistiken kennen, können Sie auch zwei Grenzen verwenden. Die Grenze in der inneren Abfrage sollte hoch genug sein, um zurückzukehren 50 verschiedene Reihen mit einer Wahrscheinlichkeit, die für Sie hoch genug ist.

SELECT DISTINCT sub.id 
FROM (
    SELECT a.id 
    FROM a 
    INNER JOIN b ON a.id=b.id_table_a && b.condition1=1 
    ORDER BY a.column1 
    LIMIT 1000 
) sub 
LIMIT 50 

Zum Beispiel: Wenn Sie einen Durchschnitt von 10 Duplikaten pro ID haben, LIMIT 1000 in der inneren Abfrage wird durchschnittlich 100 verschiedene Zeilen zurück. Es ist sehr unwahrscheinlich, dass Sie weniger als 50 Zeilen erhalten.

Wenn die Spalte condition2 ein boolescher Wert ist, wissen Sie, dass Sie maximal zwei Duplikate haben können. In diesem Fall würde LIMIT 100 in der inneren Abfrage ausreichen.

+0

Der Durchschnitt variiert stark. Viele Zeilen haben 100k + Duplikate (wahrscheinlich eine Million für einige von ihnen, aber ich habe nicht überprüft). 100000 wäre also sehr langsam, da ich in der realen Welt viel mehr sammle als nur id. – JJJ