Was ist effizienter, eine where-Klausel oder ein Join mit mehr als einer Million Zeilentabellen?

17

Wir betreiben eine Website mit 250-mm-Zeilen in einer Tabelle und in einer anderen Tabelle, mit der wir sie für die meisten Abfragen verknüpfen, mit knapp 15-mm-Zeilen.

Beispielstrukturen:

MasterTable (Id, UserId, Created, Updated...) -- 15MM Rows
DetailsTable (Id, MasterId, SomeColumn...) -- 250MM Rows
UserTable (Id, Role, Created, UserName...) -- 12K Rows

Für all diese Tabellen müssen wir regelmäßig einige Abfragen durchführen. Eine davon ist das Sammeln von Statistiken für freie Benutzer (~ 10.000 freie Benutzer).

Select Count(1) from DetailsTable dt 
join MasterTable mt on mt.Id = dt.MasterId 
join UserTable ut on ut.Id = mt.UserId 
where ut.Role is null and mt.created between @date1 and @date2

Das Problem ist, dass diese Abfrage manchmal verdammt lange dauert, da die Verknüpfungen lange vor dem Wo stattfinden.

Wäre es in diesem Fall sinnvoller, wheres anstelle von Joins zu verwenden oder möglicherweise where column in(...)?

Jeremy Boyd
quelle
1
Welche Datenbank und Version?
Leigh Riffel,
1
hast du beide wege ausprobiert
24.
Wenn dies Oracle wäre, würde ich einen funktionsbasierten Index für die UserTable auf NVL2 (Role, NULL, ID) erstellen, aber dies sieht aus wie eine andere DB.
Leigh Riffel

Antworten:

20

Bei modernen RDBMS gibt es hinsichtlich der Leistung und des Abfrageplans keinen Unterschied zwischen "Explicit JOIN" und "JOIN-in-the-WHERE" (wenn alle JOINS INNER sind).

Die explizite JOIN-Syntax ist klarer und weniger mehrdeutig (siehe Links unten).

Jetzt ist das JOIN-before-WHERE eine logische Verarbeitung, keine tatsächliche Verarbeitung, und die modernen Optimierer sind klug genug, dies zu realisieren.

Ihr Problem hier ist höchstwahrscheinlich die Indizierung.

Bitte zeigen Sie uns alle Indizes und Schlüssel in diesen Tabellen. Und die Abfragepläne

Hinweis: Diese Frage wäre bei StackOverflow in der Nähe gewesen, da es sich inzwischen um ein Duplikat handelt ... COUNT (1) vs COUNT (*) ist ebenfalls ein kaputter Mythos.

gbn
quelle
2
Es ist NICHT IMMER WAHR, dass es keinen Unterschied zwischen joinund gibt where. Ich optimiere ständig die Abfragen mit langer Laufzeit und manchmal sind die Abfragen mit der using- whereKlausel um den joinFaktor bis zum 70-fachen leistungsfähiger als die Abfragen mit der using- Klausel . Wenn es so einfach und unkompliziert wäre, wäre das Leben alles Regenbogen und Einhörner. Und hier geht es nicht um eine uralte undurchsichtige Engine - im Moment schaue ich auf den 70-fachen Vorteil der whereKlausel in SQL 2012.
Am
Außerdem beobachte ich oft die exakt gleichen Pläne aus beiden Ansätzen und isoliere, dass die Abfragen genau gleich ausgeführt werden, aber wenn die whereKlauselabfrage innerhalb des großen Stapels ausgeführt wird, in dem sie enthalten sein soll, übertrifft sie die joinAbfrage um ein Vielfaches. Die SQL-Abfragen werden nicht im luftleeren Raum ausgeführt - sie werden vom Rest der Servernutzlast beeinflusst, und häufig whereschneiden die Klauselabfragen recht gut ab, was ärgerlich ist, da die joinSyntax in der Tat viel sauberer ist.
ajeh
3
@ajeh: Ich würde vorschlagen, dass deine Erfahrung sehr untypisch ist. Sie haben größere Probleme mit Abfragen, wenn Sie x70-Unterschiede haben: So einfach ist das
gbn
5

Sie müssen die Abfrage insgesamt umgestalten

Versuchen Sie, die WHERE-Klauseln früher und die JOINs später auszuführen

Select Count(1) from DetailsTable dt
join (Select UserId,Id FROM MasterTable where
created between @date1 and @date2) mt on mt.Id = dt.MasterId 
join (Select Id FROM UserTable WHERE Role is NULL) ut
on ut.Id = mt.UserId;

Selbst wenn Sie einen EXPLAIN-Plan für diese überarbeitete Abfrage ausführen und es schlechter aussieht als Ihr Original, versuchen Sie es trotzdem. Die intern erstellten temporären Tabellen führen kartesische Verknüpfungen durch, die Tabellen, mit denen gearbeitet werden soll, sind jedoch kleiner.

Ich habe diese Idee aus diesem YouTube-Video .

Ich habe die Prinzipien des Videos in einer sehr komplexen Frage in StackOverflow ausprobiert und bekam 200 Punkte Kopfgeld.

@gbn erwähnte, dass sichergestellt werden muss, dass die richtigen Indizes vorhanden sind. In diesem Fall indizieren Sie bitte die erstellte Spalte in MasterTable.

Versuche es !!!

UPDATE 2011-06-24 22:31 EDT

Sie sollten diese Abfragen ausführen:

SELECT COUNT(1) AllRoles FROM UserTable;
SELECT COUNT(1) NullRoles FROM UserTable WHERE Role is NULL;

Wenn NullRoles X 20 <AllRoles (mit anderen Worten, wenn NullRoles weniger als 5% der Tabellenzeilen beträgt), sollten Sie einen nicht eindeutigen Index für die Rolle in UserTable erstellen. Andernfalls würde eine vollständige Tabelle mit UserTable ausreichen, da das Abfrageoptimierungsprogramm möglicherweise die Verwendung eines Indexes ausschließt.

UPDATE 2011-06-25 12:40 EDT

Da ich ein MySQL-DBA bin, erfordert meine Methode, dem MySQL-Abfrageoptimierer durch positiven Pessimismus nicht zu vertrauen und konservativ zu sein. Daher werde ich versuchen, eine Abfrage umzugestalten oder die erforderlichen Abdeckungsindizes zu erstellen, um die verborgenen schlechten Gewohnheiten des MySQL Query Optimizer zu überwinden. Die Antwort von @ gbn scheint vollständiger zu sein, da SQL Server bei der Bewertung von Abfragen möglicherweise mehr "Verstandeskraft" besitzt.

RolandoMySQLDBA
quelle
0

Wir hatten eine [Detail] -Tabelle mit etwa 75 Millionen Zeilen; Eine [Master] -Tabelle mit ca. 400 KB Zeilen und eine zugehörige [Item] -Tabelle mit 7 Zeilen - immer und für immer. Es speicherte den kleinen Satz von „Artikelnummern“ (1-7) und modellierte ein Papierformular, von dem jeden Monat Millionen gedruckt und verteilt wurden. Die schnellste Abfrage war die, an die Sie am wenigsten wahrscheinlich zuerst gedacht haben, und zwar unter Verwendung eines kartesischen Joins. IIRC, es war so etwas wie:

SELECT m.order_id, i.line_nr, d.Item_amt
FROM Master m, Item i 
INNER JOIN Detail d ON m.order_id = d.order_id

Obwohl es eine logische Verknüpfung zwischen [Item] und [Detail] gibt, hat CROSS JOIN besser funktioniert als INNER JOIN.

Das RDBMS war Teradata mit seiner MPP-Technologie und IDR, wie das Indexierungsschema lautete. Die 7-Zeilen-Tabelle hatte keinen Index, da TABLE SCAN immer die beste Leistung erbrachte.

Timothy Oleary
quelle