每个联接的计数 - 优化

时间:2016-05-13 01:06:05

标签: mysql join optimization

结果: 我使用了三种方法:

  1. 三个子查询,每个(我的)加入1个
  2. 三个子查询,没有连接,用where过滤(SlimsGhost)
  3. 三联(Solarflare)
  4. 我已经用"解释"制作了一些统计数据。和"剖析"这解释了每个查询必须完成的工作,以下结果并不令人惊讶:stats

    相对结果:

    1. 100%
    2. 79%
    3. 1715%

      three sub queries with simple join three sub queries with where clause one query with triple join

    4. 原始邮寄

      想法是连接4个表,每次使用相同的PK,然后计算每个连接分别给出的行数。

      显而易见的答案是每次加入......与子查询分开。

      但是有可能用一个查询来做吗?它会更有效率吗?

      select "LES CIGARES DU PHARAON" as "Titre",
                (select count( payalb.idPays)
                from album alb
                           left join pays_album payalb using ( idAlb )
                where alb.titreAlb = "LES CIGARES DU PHARAON") as "Pays",
                (select count( peralb.idPers)
                from album alb
                           left join pers_album peralb using ( idAlb )
                where alb.titreAlb = "LES CIGARES DU PHARAON") as "Personnages",
                (select count( juralb.idJur)
                from album alb
                           left join juron_album juralb using ( idAlb )
                where alb.titreAlb = "LES CIGARES DU PHARAON") as "Jurons"
      ; 
      +------------------------+------+-------------+--------+
      | Titre                  | Pays | Personnages | Jurons |
      +------------------------+------+-------------+--------+
      | LES CIGARES DU PHARAON |    3 |          13 |     50 |
      +------------------------+------+-------------+--------+
      

      表专辑行:22

      表pays_album行:45

      table personnage_album rows:100

      表juron_album行:1704

      以下是我的尝试:

      select alb.titreAlb as "Titre",
               sum(case when alb.idAlb=payalb.idAlb then 1 else 0 end) "Pays",
               sum(case when alb.idAlb=peralb.idAlb then 1 else 0 end) "Personnages",
               sum(case when alb.idAlb=juralb.idAlb then 1 else 0 end) "Jurons"
      from album alb
                left join pays_album payalb using ( idAlb )
                left join pers_album peralb using ( idAlb )
                left join juron_album juralb using ( idAlb )
      where alb.titreAlb = "LES CIGARES DU PHARAON"
      group by alb.titreAlb
      ;
      +------------------------+------+-------------+--------+
      | Titre                  | Pays | Personnages | Jurons |
      +------------------------+------+-------------+--------+
      | LES CIGARES DU PHARAON | 1950 |        1950 |   1950 |
      +------------------------+------+-------------+--------+
      

      但它计算完整联接表的总行数,...(1950 = 3 * 13 * 50)

      架构:https://github.com/LittleNooby/gbd2015-2016/blob/master/tintin_schema.png

      表内容:https://github.com/LittleNooby/gbd2015-2016/blob/master/tintin_description

      如果你想玩它玩:

      db_init:https://github.com/LittleNooby/gbd2015-2016/blob/master/tintin_ok.mysql

2 个答案:

答案 0 :(得分:2)

出于优化目的,一个好的经验法则是加入更少,而不是更多。实际上,您应该尝试尽可能少地加入尽可能少的行。使用任何其他连接,您将增加成本而不是增加成本。因为mysql基本上只会生成一个大的乘法矩阵。其中很多都被索引和其他东西所优化。

但是要回答你的问题:假设桌子有唯一的密钥,而idalb是专辑的唯一密钥,那么实际上只有一个大连接可以计算。然后,只有这样,你才能像你的代码那样做:

select alb.titreAlb as "Titre",
       count(distinct payalb.idAlb, payalb.PrimaryKeyFields) "Pays",
       count(distinct peralb.idAlb, peralb.PrimaryKeyFields) "Personnages",
       count(distinct juralb.idAlb, juralb.PrimaryKeyFields) "Jurons"
from album alb
left join pays_album payalb using ( idAlb )
left join pers_album peralb using ( idAlb )
left join juron_album juralb using ( idAlb )
where alb.titreAlb = "LES CIGARES DU PHARAON"
group by alb.titreAlb

其中PrimaryKeyFields代表连接表的主键字段(您必须查找它们)。

Distinct将删除其他联接对计数的影响。但遗憾的是,一般而言,distinct不会消除联接对成本的影响。

虽然,如果您的索引覆盖了表的所有(idAlb + PrimaryKeyFields)字段,那么它甚至可能与原始解决方案一样快(因为它可以优化distinct以不进行排序)并且将接近您的想法(只需遍历每个表/索引一次)。但是在正常或最坏情况下,它应该比合理的解决方案(比如SlimGhost&#39)更差 - 因为它会找到最佳策略是值得怀疑的。但是玩它并检查解释(并发布结果),也许mysql会做一些疯狂的事情。

答案 1 :(得分:1)

就“db的最少工作”而言,我认为以下内容对于您的架构来说都是正确的I / O逻辑I / O.但是要明白,除非你查看解释计划(预期和实际),否则你无法确定。

仍然,我建议尝试这一点 - 它只访问“alb”表一次,而原始查询需要访问它四次(一次获取“基本”专辑记录,然后三次查询三个子查询)。

select alb.titreAlb as "Titre",
     (select count(*) from pays_album t2 where t2.idAlb = alb.idAlb) "Pays",
     (select count(*) from pers_album t2 where t2.idAlb = alb.idAlb) "Personnages",
     (select count(*) from juron_album t2 where t2.idAlb = alb.idAlb) "Jurons"
from album alb
where alb.titreAlb = "LES CIGARES DU PHARAON"