MySQL LEFT JOIN只在最后一行痛苦地缓慢

时间:2017-06-26 15:07:03

标签: mysql

我有三张桌子,看起来像这样

堡垒

|id|lat|lon|

fort_sightings

|id|fort_id|team|

fort_raids

|id|fort_id|raid_level|

我需要一个查询来从forts获取所有行,然后从fort_sightingsfort_raids中选择最新信息,如果有的话。可能有几行fort_id具有相同的值,因此我需要最新的信息。

目前,我有这个,可能不是最漂亮的

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
)

但是它的速度很慢,查询需要10秒钟。 forts中只有~350行,所以它真的不应该花这么长时间。我相信它来自SELECT中的所有JOIN个查询,但我不知道任何替代方案。

EXPLAIN explain query

3 个答案:

答案 0 :(得分:0)

这是您的查询:

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
               );

我发现它结构奇怪。但是,看看这是否有效:

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

重要的是fort_id是索引中的第一个键。

如果这不起作用,那么您可能需要修改查询。

答案 1 :(得分:0)

除了Gordon的索引建议之外,您正在执行相关子查询,这意味着对于forts表中的每个记录,您将重新运行查询到目击和突袭。我会略微改变以从每个THEN连接中拉出每个堡垒的所有max()...就像

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

左连接的第二部分返回到原始raid和目标表,以便在最终结果中拉出相应的团队和raid级别(如果发现任何此类结果)。

答案 2 :(得分:0)

如果您需要最有效的查询,则应将 latest_fort_sighting_id latest_fort_raid_id 列添加到表forts。 MySQL没有强大的功能,如物化视图哈希联接,如PostgreSQL,我们需要手动处理它们。不要忘记使用事务进行更新。

如果您限制forts的范围,则只能使用LEFT JOIN运行优化查询。

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