mer*_*ius 6 mysql indexing join group-by subquery
我有sqlfiddle中定义的这些表和查询.
首先,我的问题是将人们显示为最近一年的LEFT JOINed访问行.我用子查询解决了.
现在我的问题是该子查询没有使用在visits
表上定义的INDEX .这导致我的查询几乎无限期地运行在每个约15000行的表上.
这是查询.目标是用访问表中最新(按年)的记录列出每个人一次.
不幸的是,在大型表格上它会变得真实,因为它不在子查询中使用INDEX.
SELECT *
FROM people
LEFT JOIN (
SELECT *
FROM visits
ORDER BY visits.year DESC
) AS visits
ON people.id = visits.id_people
GROUP BY people.id
Run Code Online (Sandbox Code Playgroud)
有谁知道如何强制MySQL使用已在visits
表上定义的INDEX ?
您的查询:
SELECT *
FROM people
LEFT JOIN (
SELECT *
FROM visits
ORDER BY visits.year DESC
) AS visits
ON people.id = visits.id_people
GROUP BY people.id;
Run Code Online (Sandbox Code Playgroud)
首先,使用非标准 SQL 语法(出现在列表中的项目SELECT
不属于GROUP BY
子句,不是聚合函数,也不依赖于分组项目)。这可能会给出不确定(半随机)的结果。
其次,(为了避免不确定的结果)您添加了一个ORDER BY
内部子查询,该子查询(无论是否非标准)在 MySQL 文档中的任何地方都没有记录,它应该按预期工作。因此,它现在可能可以工作,但在不久的将来,当您升级到 MySQL 版本 X 时,它可能无法ORDER BY
工作(其中优化器将足够聪明,能够理解派生表内部是多余的并且可以被消除)。
尝试使用此查询:
SELECT
p.*, v.*
FROM
people AS p
LEFT JOIN
( SELECT
id_people
, MAX(year) AS year
FROM
visits
GROUP BY
id_people
) AS vm
JOIN
visits AS v
ON v.id_people = vm.id_people
AND v.year = vm.year
ON v.id_people = p.id;
Run Code Online (Sandbox Code Playgroud)
复合索引(id_people, year)
将有助于提高效率。
一种不同的方法。如果您首先将人员限制在合理的范围内(例如 30 人),然后加入到表中,则效果很好visits
:
SELECT
p.*, v.*
FROM
( SELECT *
FROM people
ORDER BY name
LIMIT 30
) AS p
LEFT JOIN
visits AS v
ON v.id_people = p.id
AND v.year =
( SELECT
year
FROM
visits
WHERE
id_people = p.id
ORDER BY
year DESC
LIMIT 1
)
ORDER BY name ;
Run Code Online (Sandbox Code Playgroud)