优化此SQL查询的最佳方法是什么?

时间:2015-12-24 14:45:25

标签: php mysql sql doctrine

我试图获取每页5个作业的结果(我使用带有doctrine的silex),但页面加载大约需要30-40秒。

  $sql = "
            SELECT jobs.ID, jobs.date_added, jobs.user, jobs.title, jobsmeta.meta_value, jobs_locations_rel.county, jobs_locations_rel.city, usersmeta.meta_value, attachments.title
            FROM jobs
                JOIN jobsmeta
                    ON jobsmeta.parent_id = jobs.ID
                JOIN jobs_locations_rel
                    ON jobs_locations_rel.parent_id = jobs.ID
                JOIN usersmeta
                    ON usersmeta.parent_id = jobs.user
                JOIN attachments
                    ON attachments.ID = (SELECT meta_value FROM usersmeta WHERE parent_id = jobs.ID AND meta_key = 'user_avatar' LIMIT 1)
                ORDER BY jobs.date_added DESC LIMIT 5";
            $data = $app['db']->fetchAll($sql);

这将是解释计划: enter image description here 如何优化此查询以在更短的时间内获得结果?

3 个答案:

答案 0 :(得分:1)

正确的索引通常是数据库设计良好时的解决方案。但在您的情况下,我在查询构造中看到(可能的)问题。由于我不是MySQL专家,我认为其他用户可能会证实我的怀疑。

实际上,你加入了 ALL 与(显然)ONE to MANY关系中的其他表的作业,这可能会产生更大的结果集ORDER BY只是为了获得前五个记录最近才加入。

我认为您应该在WHERE子句中过滤掉第一个最相关的工作,以减少JOINORDER BY处理。试试这段代码:

WHERE jobs.id IN (SELECT id FROM jobs ORDER BY date_added DESC LIMIT 5)

并检查您要加入的外键列和“date_added”列是否具有正确的索引。

答案 1 :(得分:1)

执行计划显示只使用了两个(主键)索引。这应该改进。除主键索引外,您还应尝试使用额外的索引:

  • 作业(用户)
  • jobsmeta(PARENT_ID)
  • jobs_locations_rel(PARENT_ID)
  • usersmeta(parent_id,meta_key)

查询可能也会因子查询而变慢,该查询具有特殊的LIMIT 1。这意味着它检索的记录可能是几个中的随机记录。这或者意味着您的数据库设计需要,或者您不关心显示附件表的所有相关记录。使用不IN (SELECT ...)的{​​{1}}可确保您获得所有附件记录。

但是比使用LIMIT更好,将其转换为额外的IN,如下所示:

JOIN

另一个特殊的事情:SELECT jobs.ID, jobs.date_added, jobs.user, jobs.title, jobsmeta.meta_value, jobs_locations_rel.county, jobs_locations_rel.city, m1.meta_value, attachments.title FROM jobs JOIN jobsmeta ON jobsmeta.parent_id = jobs.ID JOIN jobs_locations_rel ON jobs_locations_rel.parent_id = jobs.ID JOIN usersmeta m1 ON m1.parent_id = jobs.user JOIN usersmeta m2 ON m2.parent_id = jobs.ID AND m2.meta_key = 'user_avatar' JOIN attachments ON attachments.ID = m2.meta_value ORDER BY jobs.date_added DESC LIMIT 5 或者引用usersmeta.parent_idjobs.ID值。这看起来像您可能想要查看的数据库设计中的另一个缺陷。

如果'job.id'值的顺序与jobs.user的顺序相同,那么您可以考虑执行jobs.date_added。如果没有,请考虑在ORDER BY jobs.id DESC上创建索引。

最后,您的jobs(date_added)不够具体,因为它允许共享相同order by的记录的任意排序顺序。这与性能无关,但我预计它会使分页变得困难(取决于你如何实现它)。最好的方法是,如果您可以在jobs.id子句中列出的列组合在一起唯一地标识结果中的记录。

答案 2 :(得分:0)

您可以启用mysql查询缓存以加快重复查询。