2009-08-26 9 views
2

geändert wird Ich habe folgende SQL-Code:MySQL SELECT läuft immer verbinden, wenn

select val.PersonNo, 
     val.event_time, 
     clg.number_dialed 
    from vicidial_agent_log val 
     join 
     call_log   clg on date_add('1970-01-01 02:00:00', interval clg.uniqueid second) = val.event_time 
order by val.event_time desc 
limit 100; 

, die aus und gibt Zeilen in weniger als 1 Sekunde. Allerdings, wenn ich zu einem left outer kommen die gerade ändern:

select val.PersonNo, 
     val.event_time, 
     clg.number_dialed 
    from vicidial_agent_log val 
     left outer join 
     call_log   clg on date_add('1970-01-01 02:00:00', interval clg.uniqueid second) = val.event_time 
order by val.event_time desc 
limit 100; 

die Abfrage ausgeführt wird für immer und verwendet ~ 100% der CPU des Servers.

Ich lief explain auf beiden Abfragen und die erste trifft den event_time Index auf vicidial_agent_log, während der zweite alle Indizes ignoriert. Es gibt einen Index für call_log.uniqueid.

vicidial_agent_log enthält ~ 41.000 Zeilen, call_log enthält ~ 43.000.

Meine Frage ist also - warum trifft MySQL nicht die Indizes, die ich definiert habe, gibt es eine Möglichkeit, dies zu erzwingen, und wenn nicht, wie kann ich diese Abfrage mit einer akzeptablen Geschwindigkeit ausführen lassen?

bearbeiten

Voll Lösung:

select val.PersonNo, 
     val.event_time, 
     cl.number_dialed 
    from vicidial_agent_log val 
     left outer join 
     (select date_add('1970-01-01 02:00:00', interval clg.uniqueid second) as 'converted_date', 
       number_dialed 
      from call_log clg) cl ON cl.converted_date = val.event_time 
order by val.event_time desc 
limit 100; 

Antwort

1

Die Verwendung von Funktionen in JOIN- oder WHERE-Klauseln führt immer zu Verwüstungen bei Indizes. Beispiel:

DATE_ADD('1970-01-01 02:00:00', INTERVAL clg.uniqueid SECOND) 

Die Datenbank des uniqueid Index die Werte für die Umwandlung zum Nachschlagen verwendet, nicht um den Vergleich zu der event_time Spalte in Ihrem Fall. Wenn Oracle mit PLW-Fehlern aktiviert wäre, würden Sie über eine mögliche Entfernung vom Datentyp informiert werden.

Solche Situationen sollten immer vor dem Vergleich behandelt werden, was bedeutet, eine Inline-Ansicht zu verwenden, um die Konvertierung durchzuführen und dann zu dieser Ergebnisspalte zu gelangen. IE:

JOIN (SELECT DATE_ADD('1970-01-01 02:00:00', INTERVAL clg.uniqueid SECOND) 'converted_date' 
     FROM CALL_LOG clg) cl ON cl.converted_date = val.event_time 
+0

Dies zu akzeptieren als die Antwort lesen, da dieser Code am schnellsten läuft und noch intuitiv zu lesen. Aber Quassnois Antwort und Kommentare sind auch sehr nützlich. –

-1

können Sie verwenden FORCE INDEX

+0

FORCE INDEX wird nicht die Dinge verbessern, weil die äußere Verknüpfung muss noch alle Zeilen aus der vicidial_agent_log Tabelle –

1

ich die äußere vorstellen join zwingt einen Table-Scan, weil es alle Datensätze umfassen muss, die passen und Geben Sie Nullen für diejenigen an, die dies nicht tun.

mck89s Lösung kann sehr gut funktionieren, obwohl ich nie einen Grund hatte, es zu benutzen ... Ich bin gespannt, wie das ausgehen wird.

1

Die erste, den Index, da in einem inneren Verknüpfung verwenden, können Sie die Ergebnismenge der Verknüpfungs basierte Filterung auf dem Wert der Spalte (EVENT_TIME), dass der Index auf Basis ...

Im zweiten Query, wo Sie einen Outer Join verwenden, filtern Sie NICHT die Ausgabe, daher müssen alle Datensätze in der Ergebnismenge unabhängig vom Wert von event_time enthalten sein, sodass sie einen vollständigen Tabellenscan durchführen muss ...

3

Wenn Sie LEFT JOIN verwenden, wird die LEFT Tabelle in MySQL immer führt.

In Ihrer ersten Abfrage konnte MySQL wählen, welche Tabelle zu führen, und es wählte clg.

Jetzt kann es nicht wählen, und diese Bedingung: date_add('1970-01-01 02:00:00', interval clg.uniqueid second) ist nicht sargable.

Es gibt keinen Index zu date_add('1970-01-01 02:00:00', interval clg.uniqueid second), den MySQL verwenden könnte, um den Wert val.event_time zu finden.

Ihre Abfrage wie diese Rewrite:

SELECT val.PersonNo, 
     val.event_time, 
     clg.number_dialed 
FROM vicidial_agent_log val 
LEFT OUTER JOIN 
     call_log clg 
ON  clg.uniqueid = UNIX_TIMESTAMP(val.event_time) - 7200 
ORDER BY 
     val.event_time desc 
LIMIT 100 
+0

Unterstützt MySQL funktionale Indizes? –

+0

'@ rexem': nein. Weder unterstützt es 'HASH JOIN' oder' MERGE JOIN'. Die einzige Möglichkeit, diese Abfrage schnell auszuführen, besteht darin, sie neu zu schreiben, um eine sargable-Bedingung zu verwenden. – Quassnoi

+0

Wie wäre es mit einer materialisierten Ansicht? –