mysql - 每个连接的计数 - 优化

标签 mysql join optimization

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

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

我用“explain”和“profiling”做了一些统计,解释了每个查询必须做的工作,以下结果并不令人惊讶: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 个表,每次使用相同的 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

表 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

如果你想玩就玩吧:

数据库初始化:https://github.com/LittleNooby/gbd2015-2016/blob/master/tintin_ok.mysql

最佳答案

出于优化目的,一个好的经验法则是加入更少,而不是更多。事实上,您应该尝试连接尽可能少的行和尽可能少的行。通过任何额外的连接,您将增加成本而不是增加成本。因为 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 会做一些疯狂的事情。

关于mysql - 每个连接的计数 - 优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37199736/

相关文章:

mysql - 创建定义器 =`root` @`%` 如果不存在则事件

mysql - sequelize left join 也返回关联表的数据

python - Pandas 强制对包含重复键的列进行一对一合并

mysql - 非套接字上的套接字操作

php - 如何捕获 MySQLi (PHP) 中的错误并继续执行?

mysql - 通过在同一表中选择具有另一个条件的行来更新具有 where 条件的行

mysql - SQL 根据时间戳和库存水平连接同一个表

Python:用于使用多边形裁剪光栅图像的函数的代码审查

c++ - 有效地找到最小值和最大值

optimization - Eigen :按行复制(广播)