【问题标题】:a count for each join - optimisation每个连接的计数 - 优化
【发布时间】:2021-06-02 15:51:47
【问题描述】:

结果: 我用了三种方法:

  1. 三个子查询,每个查询 1 个(我的)
  2. 三个子查询,无join,用where过滤(SlimsGhost)
  3. 三重连接 (Solarflare)

我用“解释”和“分析”做了一些统计,解释了每个查询必须做的工作,以下结果并不令人惊讶:stats

相对结果:

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

原帖

这个想法是连接 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

table pays_album 行数:45

table personnage_album 行数: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

【问题讨论】:

  • 我想查看专辑表中的一些示例数据。为什么要这样做?
  • 为了优化和知道是否可以供以后使用。 (显然这个数据库是用来练习的)我编辑了帖子以提供指向“表格内容”的链接
  • 如果您制作了一个 sqlfiddle 而不是链接到文本文件,那将会很有帮助。
  • 如果您只是使用COUNT(*),有一些方法可以简化它。但我不确定它们是否适用于您进行这样的旋转。
  • sqlfiddle 似乎不喜欢我的数据库架构,反正我似乎有我的答案,是时候阅读了。

标签: mysql join optimization


【解决方案1】:

出于优化目的,一个好的经验法则是少加入,而不是多加入。事实上,您应该尝试用尽可能少的行来连接尽可能少的行。通过任何额外的连接,您将增加成本而不是增加成本。因为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 的解决方案)差 - 因为它是否会找到最佳策略是值得怀疑的。但是玩弄它并检查解释(并发布发现),也许 mysql 会做一些疯狂的事情。

【讨论】:

    【解决方案2】:

    至于“为 db 工作最少”,我认为以下对于您的架构来说既正确又不合逻辑 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"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-10
      • 2018-02-26
      相关资源
      最近更新 更多