2013-09-04 9 views
5

Simplified, ich habe zwei Tabellen, contacts und donotcallNutzen Sie Index, wenn JOIN'ing gegen mehrere Spalten

CREATE TABLE contacts 
(
    id int PRIMARY KEY, 
    phone1 varchar(20) NULL, 
    phone2 varchar(20) NULL, 
    phone3 varchar(20) NULL, 
    phone4 varchar(20) NULL 
); 
CREATE TABLE donotcall 
(
    list_id int NOT NULL, 
    phone varchar(20) NOT NULL 
); 
CREATE NONCLUSTERED INDEX IX_donotcall_list_phone ON donotcall 
(
    list_id ASC, 
    phone ASC 
); 

Ich mag würde, um zu sehen, was Kontakte entspricht der Telefonnummer in einer bestimmten Liste von DoNotCall Telefon. Für schnellere Suche, habe ich donotcall auf list_id und phone indiziert.

Wenn ich machen folgende JOIN es eine lange Zeit in Anspruch nimmt (zB 9 Sekunden.):

SELECT DISTINCT c.id 
FROM contacts c 
JOIN donotcall d 
    ON d.list_id = 1 
    AND d.phone IN (c.phone1, c.phone2, c.phone3, c.phone4) 

Screenshot of execution plan

Execution plan on Pastebin

Während, wenn ich auf jedem Telefonfeld es separat LEFT JOIN läuft viel schneller (z. B. 1,5 Sekunden):

SELECT c.id 
FROM contacts c 
LEFT JOIN donotcall d1 
    ON d1.list_id = 1 
    AND d1.phone = c.phone1 
LEFT JOIN donotcall d2 
    ON d2.list_id = 1 
    AND d2.phone = c.phone2 
LEFT JOIN donotcall d3 
    ON d3.list_id = 1 
    AND d3.phone = c.phone3 
LEFT JOIN donotcall d4 
    ON d4.list_id = 1 
    AND d4.phone = c.phone4 
WHERE 
    d1.phone IS NOT NULL 
    OR d2.phone IS NOT NULL 
    OR d3.phone IS NOT NULL 
    OR d4.phone IS NOT NULL 

Screenshot of execution plan

Execution plan on Pastebin

Meine Vermutung ist, dass der erste Schnipsel langsam läuft, weil es nicht den Index auf donotcall nicht nutzen.
Also, wie ein Join zu mehreren Spalten zu tun und immer noch den Index verwenden?

+0

Was Sie eigentlich tun müssen, ist Ihre Datenbankstruktur zu reparieren. Du solltest NIEMALS über phone1, phone2, phone3, phone4 verfügen - das bedeutet, dass du eine untergeordnete Tabelle benötigst. – HLGEM

+0

@HLGEM: Punkt notiert. Ich hätte es auch anders gemacht, wenn ich die Wahl hätte. Aber manchmal steckt man in einer Struktur, die andere geschaffen haben, ohne Hoffnungen auf Refactoring. – ANisus

Antwort

6

SQL Server könnte denken, IN (c.phone1, c.phone2, c.phone3, c.phone4) mit einem Index zu lösen ist zu teuer.

Sie können testen, ob der Index mit einem Hauch schneller sein würde:

SELECT c.* 
FROM contacts c 
JOIN donotcall d with (index(IX_donotcall_list_phone)) 
    ON d.list_id = 1 
    AND d.phone IN (c.phone1, c.phone2, c.phone3, c.phone4) 

Von der Abfragepläne Sie auf dem Laufenden, zeigt der erste Plan 40k Zeilen zu produzieren geschätzt wird, aber es gibt nur 21 Zeilen. Der zweite Plan schätzt 1 Reihe (und natürlich auch 21 zurück.)

Sind Ihre statistics auf dem neuesten Stand? Veraltete Statistiken können erklären, dass der Abfrageanalysator schlechte Entscheidungen trifft. Statistiken sollten automatisch oder in einem wöchentlichen Job aktualisiert werden. Überprüfen Sie das Alter Ihres Statistik mit:

select object_name(ind.object_id) as TableName 
,  ind.name as IndexName 
,  stats_date(ind.object_id, ind.index_id) as StatisticsDate 
from sys.indexes ind 
order by 
     stats_date(ind.object_id, ind.index_id) desc 

können Sie update them manuell mit:

EXEC sp_updatestats; 
+0

Guter Vorschlag. Ich habe versucht, den Hinweis hinzuzufügen. Es hat jedoch nichts geändert. Die LINKE JOIN-Version ist immer noch viel schneller. Es sieht so aus, als ob der SQL-Server den Code unter Verwendung des Index nicht auflösen kann. – ANisus

+2

Haben Sie versucht, die Abfragepläne zu vergleichen? (Menüabfrage, dann Tatsächlichen Ausführungsplan einbeziehen.) – Andomar

+0

Ich versuche das zu tun, aber ich habe sehr wenig Erfahrung in der Analyse von Abfrageplänen.Von dem, was ich sehen kann, ist die LINKE JOIN-Lösung ziemlich geradlinig und verwendet 4 Hash-Matches (rechte äußere Joins). Die einzelne JOIN-Lösung macht das völlig anders. Zwei verschachtelte Schleifen (Inner Joins), zuerst mit dem Index und dann mit der Kontakttabelle. – ANisus

0

Mit dieser schlechten Datenbankstruktur, eine UNION könnte ALL Abfrage sein am schnellsten.

+0

Während UNION ALL derzeit bessere Ergebnisse erzielt als die Single-JOIN-Lösung, ist es etwas langsamer als die LEFT JOIN-Lösung. – ANisus

Verwandte Themen