MySQL加入3个表,运行速度非常慢

时间:2015-09-09 11:14:53

标签: php mysql laravel join inner-join

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

我正在使用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

查询工作正常,但随着表的大小增加,查询变得非常慢。它需要大约30秒才能完成,并且是唯一缓慢运行的查询。

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

非常感谢,

萨姆

3 个答案:

答案 0 :(得分:3)

这些索引会有所帮助

  1. 2*(...)
  2. events(clientid)
  3. clients(id)
  4. notes(clientid,created_at)
  5. 注意,4可以 notes(created_at,clientid)并且我不会添加注释,使其成为覆盖索引,因为注释太宽。

答案 1 :(得分: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 MAX(created_at) 
FROM notes 
WHERE clientid = clients.id
) 
WHERE `cancelled` = ? AND `complete` = ? 
ORDER BY `eventtime` ASC

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

此外,如果事件表包含一些text / blob或long varchar类型列,那么在使用事件时,如果不需要,则应排除它们。*

答案 2 :(得分:1)

解决方案步骤:

  1. 更改您的查询
  2. 那是您的查询:

    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排序并获取最新记录。如果您不手动编辑表格,那么MAX(id)和最新的created_at都将指示最新记录。

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

      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