【问题标题】:NOT IN subquery versus ON != OperationNOT IN 子查询与 ON != 操作
【发布时间】:2018-10-29 19:41:19
【问题描述】:

我有两个名为 ny_clean(3454602 个条目)和pickup_0_ids_temp_table(2739268 个条目)的表,它们都有一个 id CHAR(11) 列,这是一个主键,并且在它上面有一个 BTREE 索引(MySQL 5.7)。

pickup_0_ids_temp_table 中的“id”列是 ny_clean 的子集,我想得到一个 ny_clean 的结果,而没有来自pickup_0_ids_temp_table 的 id 值。

选项 1:

解释 选择 * FROMpick_0_ids_temp_table 作为 t 加入 ny_clean 作为 n ON n.id != t.id; +----+-------------+----------+------------+------ -+---------------+-------------------+---------+-- ----+----------+----------+------------ -----------------------------------------+ |编号 |选择类型 |表|隔断 |类型 |可能的键 |关键 | key_len |参考 |行 |过滤 |额外 | +----+-------------+----------+------------+------ -+---------------+-------------------+---------+-- ----+----------+----------+------------ -----------------------------------------+ | 1 |简单 |吨 |空 |索引 |空 |初级 | 11 |空 | 2734512 | 100.00 |使用索引 | | 1 |简单 | ny_clean |空 |索引 |空 | btree_pk_ny_clean | 11 |空 | 3445904 | 90.00 |使用哪里;使用索引;使用连接缓冲区(块嵌套循环)| +----+-------------+----------+------------+------ -+---------------+-------------------+---------+-- ----+----------+----------+------------ -----------------------------------------+

选项 2:

解释 选择 * FROM ny_clean 作为 n 在哪里 n.id 不在 ( 选择编号 从pickup_0_ids_temp_table); +----+--------------------+------------ --+------------+-----------------+---------------- --------+----------+---------+------+---------+---- ------+--------------+ |编号 |选择类型 |表|隔断 |类型 |可能的键 |关键 | key_len |参考 |行 |过滤 |额外 | +----+--------------------+------------ --+------------+-----------------+---------------- --------+----------+---------+------+---------+---- ------+--------------+ | 1 |初级 | n |空 |全部 |空 |空 |空 |空 | 3445904 | 100.00 |使用位置 | | 2 |依赖子查询 | Pickup_0_ids_temp_table |空 |唯一子查询 |初级,btree_pickup_0 |初级 | 11 |功能 | 1 | 100.00 |使用索引 | +----+--------------------+------------ --+------------+-----------------+---------------- --------+----------+---------+------+---------+---- ------+--------------+

然后我在这个更大的查询中使用其中一个选项

解释 插入到 y 选择 id、pickup_longitude、pickup_latitude 从 x 加入 (选项 1 或 2)作为 z 开 z.id = x.id;

当我在较大的查询中使用选项 1 时,它运行了两天,但没有完成。另一方面,选项 2 在不到 30 分钟的时间内完成了这项工作

我的问题:为什么会这样? 根据 MySQL 文档 (https://dev.mysql.com/doc/refman/5.7/en/subquery-materialization.html),我怀疑这是由于子查询的具体化,但我该如何检查呢?

我对 EXPLAIN 输出的解释有误吗?因为据此判断,我希望选项 1 更快,因为它在两个表上都使用索引

还是与更大的查询有关?

提前致谢

【问题讨论】:

  • 第一个解释有Using join buffer (Block Nested Loop) ,这意味着记录在两个嵌套循环中匹配。这当然不好
  • n.id != t.id 可能会被重写为 n.id < t.id AND n.id > t.id 然后优化器应该选择范围扫描
  • @RaymondNijland 我第一次听说这是一个优化技巧。
  • @JuanCarlosOropeza 是的,它应该是 OR 而不是 n.id < t.id OR n.id > t.id.. 不确定重写是否可以在没有示例数据的情况下开箱即用..
  • “我第一次听说这是一个优化技巧”@JuanCarlosOropeza 嗯,它接缝了现代 MySQL 5.7+ 版本优化!= 已经作为范围扫描它接缝。 db-fiddle.com/f/czMTmRHZxtSAXKdsGSWkrW/2

标签: mysql query-optimization


【解决方案1】:

您的选项 1 没有做您认为会做的事情。

如果你有两张桌子

      n.id            t.id
      1               1
      2               2 
      3               3

ON n.id != t.id;

你得到:

   1,2
   1,3
   2,1
   2,3
   3,1
   3,2

这几乎是一个笛卡尔积。所以 3.4 磨 x 2.7 磨 ~ 9.18 磨行

然后你尝试做一个 JOIN 并且因为那个物化表没有索引需要很长时间。

【讨论】:

  • 我怎么忘记感谢你解决了我的问题?我可能太用力了。
猜你喜欢
  • 2023-03-20
  • 2014-03-03
  • 2012-01-17
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多