我试图通过在 for each 嵌套循环找到一条记录时停止来优化联接.

MySQL版本为8.0.29.

我从MySQL JOIN with LIMIT 1 on joined table中发现,我可以使用单列相等比较来做到这一点,但我很难使用复合主键来做到这一点.

create table foo (
  id int unsigned, 
  fooValue int, 
  barValue binary(32), 
  primary key (id), 
  index fooV (fooValue)
);

create table bar (
  id int unsigned, 
  barValueChecksum int unsigned, 
  barValue binary(32), 
  primary key (id, barValueChecksum), 
  index checksum (barValueChecksum)
);

我想找出bar个S换foo个S有一定价值

explain select * from foo
    left join bar on (bar.id, bar.barValueChecksum)
        = (select b.id, b.barValueChecksum from bar b 
                   where b.barValueChecksum = crc32(foo.barValue)
                     and b.barValue = foo.barValue)
where foo.fooValue = 10;

但它又回来了

id|select_type       |table|partitions|type|possible_keys|key     |key_len|ref  |rows|filtered|Extra                                     |
--+------------------+-----+----------+----+-------------+--------+-------+-----+----+--------+------------------------------------------+
 1|PRIMARY           |foo  |          |ref |fooV         |fooV    |5      |const|   1|   100.0|                                          |
 1|PRIMARY           |bar  |          |ALL |             |        |       |     |   1|   100.0|Using where; Using join buffer (hash join)|
 2|DEPENDENT SUBQUERY|b    |          |ref |checksum     |checksum|4      |func |   1|   100.0|Using index condition; Using where        |

优化器try 使用散列联接,更糟糕的是,PRIMARY没有出现在表barpossible_keys节中.

通过分离比较,我可以获得与我想要的结果类似的结果,但它运行子查询两次

explain select * from foo
    left join bar on (bar.id 
                            = (select b.id from bar b 
                                where b.barValueChecksum = crc32(foo.barValue) 
                                      and b.barValue = foo.barValue))
                 and (bar.barValueChecksum 
                            = (select b.barValueChecksum from bar 
                                b where b.barValueChecksum = crc32(foo.barValue) 
                                        and b.barValue = foo.barValue))
where foo.fooValue = 10;

这导致了

id|select_type       |table|partitions|type  |possible_keys   |key     |key_len|ref      |rows|filtered|Extra                             |
--+------------------+-----+----------+------+----------------+--------+-------+---------+----+--------+----------------------------------+
 1|PRIMARY           |foo  |          |ref   |fooV            |fooV    |5      |const    |   1|   100.0|                                  |
 1|PRIMARY           |bar  |          |eq_ref|PRIMARY,checksum|PRIMARY |8      |func,func|   1|   100.0|Using where                       |
 3|DEPENDENT SUBQUERY|b    |          |ref   |checksum        |checksum|4      |func     |   1|   100.0|Using index condition; Using where|
 2|DEPENDENT SUBQUERY|b    |          |ref   |checksum        |checksum|4      |func     |   1|   100.0|Using index condition; Using where|

解决方案是什么?

推荐答案

从MySQL 8.0.14开始,您可以为此使用横向联接.有了正确的索引,它可能会非常高效.

例如,您可以执行以下操作:

select * 
from foo
left join lateral (
  select b.*
  from bar b 
  where b.barValueChecksum = crc32(foo.barValue)
    and b.barValue = foo.barValue
  limit 1
) on true
where foo.fooValue = 10;

以下索引可以加快对主表和联接的筛选速度:

create index ix1 on foo (fooValue); -- You already have this index

create index ix2 on bar (barValueChecksum, barValue); -- You don't have this one

Mysql相关问答推荐

mysql查询汇总数据

为什么IN子句中有GROUP BY会导致查询变得不可执行?

MySQL binlog事件上的并发事务行为

MySQL RDS ALTER TABLE ENUM短暂中断了我的数据库连接

MySQL:返回所有条件(但不满足其他条件)为真的所有结果

根据SQL中的平均范围对学生进行排名

基于多行从表中 Select

从insert语句获取新生成的虚拟UUID

PythonAnywhere中SSH到MySQL数据库无限期挂起,SSH正确,workbench可以完美连接

Mysql:使用like Select 查询

我是否需要在 N+1 列上建立索引,才能有效地在 N 列上执行 SELECT,并按其他列排序?

如何使用mysql更新列中的json数据

谁能帮我优化where子句

MYSQL REGEXP_REPLACE 在数字之后

总行大小不超过 65535,但我得到行大小太大.所用表类型的最大行大小,不包括 BLOB,是 65535错误

DECIMAL(m, n) 在 64 位系统中如何表示?

MySQL按连续值和计数分组

使用 MySQL LIMIT、OFFSET 进行分页

MySQL - 在 MySQL UPDATE 或 SELECT 查询中使用 If Then Else

MySQL 的最大并发连接数