MySQL在子查询中没有使用INDEX

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 ?

ype*_*eᵀᴹ 4

您的查询:

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)

的:SQL-fiddle

复合索引(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)