我有三张桌子,看起来像这样
堡垒
|id|lat|lon|
fort_sightings
|id|fort_id|team|
fort_raids
|id|fort_id|raid_level|
我需要一个查询来从forts
获取所有行,然后从fort_sightings
和fort_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
个查询,但我不知道任何替代方案。
答案 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;