MySQL 连接 3 个表 运行 非常慢

MySQL join across 3 tables running very slowly

我正在显示一页日记事件以及客户详细信息和他们案例的最新记录。

我正在使用 Laravel 的查询生成器,我现在的查询是:

$data['events'] = DB::table('events')
    ->where('cancelled', 0)
    ->where('complete', 0)
    ->join('clients', 'events.clientid', '=', 'clients.id')
    ->join('notes', function ($join) {
    $join->on('events.clientid', '=', 'notes.clientid')
    ->on('notes.created_at', '=', DB::raw('(select created_at from notes where clientid = clients.id ORDER BY created_at DESC LIMIT 1)'));
        })
    ->select('events.*', 'clients.firstname', 'clients.surname', 'notes.note')
    ->orderBy('eventtime', 'asc')->paginate(75); //Load events sorted by time

这样输出的SQL是:

select `events`.*, `clients`.`firstname`, `clients`.`surname`, `notes`.`note` from `events`
inner join `clients` on `events`.`clientid` = `clients`.`id`
inner join `notes` on `events`.`clientid` = `notes`.`clientid`
and `notes`.`created_at` = (
select created_at from notes where clientid = clients.id ORDER BY created_at DESC LIMIT 1)
where `cancelled` = ? and `complete` = ? order by `eventtime` asc

查询运行完美,但随着 table 大小的增加,查询变得非常慢。完成大约需要 30 秒,并且是唯一的查询 运行 如此缓慢。

有没有更好的方法来组织联接?在单独的查询中获取最新的注释是否有意义?

非常感谢,

山姆

不确定,但您可以尝试下面的查询,因为如果注释 table 庞大 table-

,您可以避免在子查询中对其进行反向排序
SELECT `events`.*, `clients`.`firstname`, `clients`.`surname`, `notes`.`note` 
FROM `events` 
INNER JOIN `clients` ON `events`.`clientid` = `clients`.`id` 
INNER JOIN `notes` ON `events`.`clientid` = `notes`.`clientid` 
AND `notes`.`created_at` = 
(
SELECT MAX(created_at) 
FROM notes 
WHERE clientid = clients.id
) 
WHERE `cancelled` = ? AND `complete` = ? 
ORDER BY `eventtime` ASC

假设所有连接字段和 created_at 字段都有索引。

此外,如果事件 table 包含一些 text/blob 或 long varchar 类型的列,那么您应该排除它们(如果不需要,因为您正在使用事件)。*

这些索引会很有帮助

  1. events(clientid)
  2. clients(id)
  3. notes(clientid,created_at)
  4. notes(created_at,clientid)

注意,4 可能是 notes(created_at,clientid,cacelled,complete,eventtime) 我不会在其中添加注释,使其成为覆盖索引,因为注释会太宽。

求解步骤:

  1. 更改您的查询

这就是您的查询:

select `events`.*, `clients`.`firstname`, `clients`.`surname`, `notes`.`note` from `events`
inner join `clients` on `events`.`clientid` = `clients`.`id`
inner join `notes` on `events`.`clientid` = `notes`.`clientid`
and `notes`.`created_at` = (
select created_at from notes where clientid = clients.id ORDER BY created_at DESC LIMIT 1)
where `cancelled` = ? and `complete` = ? order by `eventtime` asc

让我们这样改:

select `events`.*, `clients`.`firstname`, `clients`.`surname`, `notes`.`note` from `events`
inner join `clients` on `events`.`clientid` = `clients`.`id`
inner join `notes` on `events`.`clientid` = `notes`.`clientid`
and `notes`.`id` = (SELECT MAX(id) FROM notes WHERE clientid = clients.id)
where `cancelled` = ? and `complete` = ? order by `eventtime` asc

如果您想从笔记中获取最新记录,则无需按 created_at 排序并获取最新记录。如果您不手动编辑 tables,那么 MAX(id) 和 latest created_at 都将指示最新记录。

  1. 添加索引

索引总是有助于数据库避免在搜索数据时获取整个 table。
因此,当您加入或进行查询时,它会在内存中收集所有具有相关字段条件的记录,然后加入数据数组。
添加索引将帮助数据库首先关闭所有快速找到所需的数据。
索引有助于数据库引擎在针数据存在的二进制文件中进行偏移

所以让我们添加索引:

ALTER TABLE `events` ADD INDEX `clientid` (`clientid`);
ALTER TABLE `notes` ADD INDEX `clientid` (`clientid`);
ALTER TABLE `events` ADD INDEX `cancelled` (`cancelled`);
ALTER TABLE `events` ADD INDEX `complete` (`complete`);
ALTER TABLE `events` ADD INDEX `cancelled_complete` (`cancelled`, `cancelled`);
ALTER TABLE `events` ADD INDEX `eventtime` (`eventtime`);

就这些了(:

p.s。大多数 laravel 开发人员的主要问题是他们在编写迁移模式时忘记设置索引。 http://laravel.com/docs/5.1/migrations#creating-indexes