使用 group by 和条件优化 SQL 查询

Posted

技术标签:

【中文标题】使用 group by 和条件优化 SQL 查询【英文标题】:Optimising SQL query using group by and conditions 【发布时间】:2017-02-03 12:30:25 【问题描述】:

我有两个表fichesfiches_actionsfiches_actions 表中的条目是对fiches 表中的条目进行的操作,每个操作都有一个action_id 代表该操作。

这是两个表的架构

问题

对于每个 fiche,获取对其执行的最后一个操作 (action_id),然后获取执行的每个操作的计数。

另一个公式:获取对 fiches 所做的每个最后操作的计数

我通过将最后一个 id 插入到该 fiche fiches_actions table 中的 fiche max(fiches_actions.id) 来获得对 fiche 所做的最后一个操作

这些文件必须验证一些条件

`fiches`.`created_at` >= '2016-01-01 00:00:00'
    AND `fiches`.`created_at` <= '2017-02-01 00:00:00'
    AND `fiches`.`status` = 0 
    AND `fiches`.`agent_id` = '51'

我的解决方案

我确实通过这种方法得到了结果:

首先,我创建了一个视图来获取对它所做的每张照片的操作

 CREATE VIEW v_fiches_actions AS 
  SELECT max(fiches_actions.id) as id, 
    `fiches_actions.action_id`,
     fiches_actions.fiche_id 
 FROM fiches_actions group by fiche_id;

然后,我从这个视图中选择计数

select v_fiches_actions.action_id, count(*) from v_fiches_actions where fiche_id in 
( select `fiches`.`id` from fiches where
    `fiches`.`created_at` >= '2016-01-01 00:00:00'
    AND `fiches`.`created_at` <= '2017-02-01 00:00:00'
    AND `fiches`.`status` = 0 
    AND `fiches`.`agent_id` = '51'
) group by action_id;

这是我得到的结果:这似乎是正确的

    | action_id | count(*)
    | 3         | 6
    | 7         | 1

我的问题

1- 我的方法是否正确,我得到了正确的结果

2- 有没有办法在单个查询中执行此操作(不使用视图)

【问题讨论】:

一次查询的大致思路..from v_fiches_actions fa JOIN fiches f ON f.id=fa.fiche_id WHERE.. 我没有在我的查询中加入,或者我不明白你的评论 【参考方案1】:

您的方法并没有错,但它更冗长并且需要比必要的更多工作。

这是另一种方法:

select fa.action_id, count(*)
from fiches_actions fa join
     fiches f
     on fa.fiche_id = f.id
where f.created_at >= 2016-01-01' and
      f.created_at <= '2017-02-01' and
      f.status = 0 and
      f.agent_id = 51 and
      fa.created_at = (select max(fa2.created_at)
                       from fiches_actions fa2
                       where fa2.fiche_id = f.id
                      )
group by action_id;

为了性能,索引fiches(agent_id, status, created_at, id)fiches_actions(fiche_id, created_at)

相关的子查询(尤其是正确的索引)应该比聚合快得多。为什么?相关子查询仅在过滤器之后剩余的行上运行。另一方面,聚合必须聚合fiche_actions 表中的所有行。

注意事项:

您不需要查询视图。 对于日期常量,您不需要时间。 表别名使查询更易于编写和阅读。 不要对51 使用单引号,假设id 是一个数字。仅对字符串和日期常量使用单引号。

【讨论】:

是的,这是正确的答案,我在结果中弄错了。谢谢你的笔记:)【参考方案2】:

如果 id 是可用于连接表的主键,则无需使用两个选择即可。

你可以在没有第二次选择的情况下完成它:

CREATE VIEW col1, col2, count(*) as new_col AS SELECT FROM Table1 INNER JOIN OtherTable ON ID = ID WHERE created_at.table1 BETWEEN 2016-01-01 AND 2017-02-01 AND status.table1 =o AND agent_id.table1 = 51 AND..

【讨论】:

感谢 Raphael 为您解答,但您的回答中没有按 action_id 分组,我不想与 views 合作

以上是关于使用 group by 和条件优化 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

mysql优化 之 group by索引松散扫描和紧凑扫描

利用 group by 的 Loose Index Scan 优化 sql

优化 SQL:如何重写此查询以提高性能? (使用子查询,摆脱 GROUP BY?)

MySql学习 —— where / having / group by / order by / limit 简单查询

在SQL中分组查询 Group by 的存在条件是啥

SQL中where和group by可以连用吗?having算是对检索条件的补充吗?