2017-06-26 4 views
1

ich drei Tabellen haben, dieMySQL LEFT JOIN nur letzte Reihe quälend langsam

Forts

|id|lat|lon| 

fort_sightings

|id|fort_id|team| 

fort_raids

sieht wie folgt aus
|id|fort_id|raid_level| 

Ich brauche eine Abfrage, die die neuesten Informationen von fort_sightings und fort_raids, wenn überhaupt alle Zeilen aus forts und wählen Sie dann abruft. Es könnte mehrere Zeilen geben, in denen fort_id den gleichen Wert hat, also brauche ich die neuesten Informationen.

Derzeit habe ich das, was nicht der hübscheste

SELECT 
    * 
FROM 
    forts c 

LEFT JOIN fort_sightings o ON o.id = (
    SELECT 
     id 
    FROM 
     fort_sightings 
    WHERE 
     fort_id = c.id 
    ORDER BY 
     id DESC 
    LIMIT 1 
) 

LEFT JOIN fort_raids r ON r.id = (
    SELECT 
     id 
    FROM 
     fort_raids 
    WHERE 
     fort_id = c.id 
    ORDER BY 
     id DESC 
    LIMIT 1 
) 

sein könnte Aber es ist sehr langsam, die Abfrage dauert mehr als 10 Sekunden. Es gibt nur ~ 350 Reihen in forts, also sollte es wirklich nicht so lange dauern. Ich glaube, es ist von allen SELECT Abfragen in der JOIN, aber ich kenne keine Alternative.

EXPLAIN explain query

+0

Jede Frage, die mit langsamen Abfragen enthält die Ausgabe von 'EXPLAIN' beschäftigt. Bitte schau dich um, um zu sehen, wie man EXPLAIN ausgibt und poste es hier. – Mjh

+0

Sub-Abfragen sind immer langsam, und in diesem Fall machen Sie eine Unterabfrage für jede o.id und r.id, wenn ich mich nicht irre. Finden Sie immer einen Weg, um Unterabfragen zu vermeiden. – Goose

+0

Um herauszufinden, warum es so langsam ist, benutze 'explain' gefolgt von dieser Abfrage (in der mysql-Befehlszeile), die dir sagen wird, welche Indizes benutzt werden und wieviele Datensätze gelesen werden. Sie können entweder Ihren Code ändern, um Indizes zu verwenden, oder Index hinzufügen, wenn er wirklich benötigt wird. – Tamar

Antwort

0

Das ist Ihre Abfrage:

SELECT * 
FROM forts c LEFT JOIN 
    fort_sightings o 
    ON o.id = (SELECT fs2.id 
       FROM fort_sightings fs2 
       WHERE fs.fort_id = c.id 
       ORDER BY fs2.id DESC 
       LIMIT 1 
       ) LEFT JOIN 
    fort_raids r 
    ON r.id = (SELECT fr2.id 
       FROM fort_raids fr2 
       WHERE fr2.fort_id = c.id 
       ORDER BY fr2.id DESC 
       LIMIT 1 
       ); 

Ich finde es seltsam strukturiert. Aber sehen, ob das funktioniert:

  • fort_sightings(fort_id, id)
  • fort_raids(fort_id, id)

Es ist wichtig, dass die fort_id der erste Schlüssel in den Indizes sein.

Wenn dies nicht funktioniert, müssen Sie möglicherweise die Abfrage ändern.

+0

Der erste Schlüssel? Ich wusste nicht, dass das wichtig war. Ich werde versuchen, die Tasten umzuschalten und dann deine Anfrage auszuführen, danke! – kopa

+0

Ich habe zwei Primärschlüssel, "ID" und "Fort_id", wobei "ID" 'AUTO INCREMENT' verwendet. Wenn ich versuche, sie zu wechseln, also 'fort_id' ist Schlüssel 1, dann bekomme ich das:' 1075 - Falsche Tabellendefinition; es kann nur eine automatische Spalte geben und sie muss als Schlüssel definiert werden. – kopa

+0

Sie wollen keine zwei Primärschlüssel. Sie möchten die zusammengesetzten Indizes, die ich in der Antwort beschrieben habe. –

0

Abgesehen von der Indexempfehlung von Gordon, machst du eine korrelierte Unterabfrage, was bedeutet, dass du die Abfrage für jeden Datensatz in der Forts-Tabelle erneut auf die Sichtungen und Überfälle lenkst. Ich würde etwas von jedem DANN kommen alle max() pro fort zu ziehen, ändern ... wie

SELECT 
     c.id, 
     c.lat, 
     c.lon, 
     fs2.team, 
     fr2.raid_level 
    FROM 
     forts c 
     LEFT JOIN 
     (select fs.fort_id, max(fs.id) as MaxSightID 
       from fort_sightings fs 
       group by fs.fort_id) S 
      on c.id = s.fort_id 
      LEFT JOIN fort_sightings fs2 
       on s.MaxSightID = fs2.id 
     LEFT JOIN 
     (select fr.fort_id, max(fr.id) as MaxRaidID 
      from fort_raids fs 
      group by fr.fort_id) R 
      on c.ID = r.fort_id 
      LEFT JOIN fort_raids fr2 
      on r.MaxRaidID = fr2.id 

Der zweite Teil des links schließt sich an die ursprünglichen Razzia und Sichtung Tabellen geht das entsprechende Team zu ziehen und Raid-Level in den Endergebnissen, wenn solche gefunden werden.

0

Wenn Sie die effizienteste Abfrage benötigen, sollten Sie Spalten latest_fort_sighting_idlatest_fort_raid_id Tabelle forts hinzuzufügen.MySQL hat keine leistungsstarken Funktionen wie Materialized Ansichten oder Hash verbindet wie PostgreSQL, wir müssen sie manuell behandeln. Vergessen Sie nicht, die Transaktion für Updates zu verwenden.

Wenn Sie den Bereich von forts begrenzen, können Sie alternativ eine optimierte Abfrage nur mit LEFT JOIN ausführen.

select - SQL join: selecting the last records in a one-to-many relationship - Stack Overflow

SELECT forts.*, fs1.team, fr1.raid_level FROM forts 
LEFT JOIN fort_sightings fs1 ON fs1.fort_id = forts.id 
LEFT JOIN fort_sightings fs2 ON fs2.fort_id = forts.id AND fs1.id < fs2.id 
LEFT JOIN fort_raids fr1 ON fr1.fort_id = forts.id 
LEFT JOIN fort_raids fr2 ON fr2.fort_id = forts.id AND fr1.id < fr2.id 
WHERE fs2.id IS NULL AND fr2.id IS NULL AND forts.id > 5 ORDER BY forts.id LIMIT 5; 

enter image description here