来自 5.7 的 mysql 8 查询回归(怎么了?)

Posted

技术标签:

【中文标题】来自 5.7 的 mysql 8 查询回归(怎么了?)【英文标题】:mysql 8 query regression coming from 5.7 (what's wrong?) 【发布时间】:2021-06-07 21:25:04 【问题描述】:

5.7 中的查询在 8.0.23 中不再有效。 针对 windows 8.0.23 x64 和 linux docker 容器 8.0.23 进行了测试。

当前的sql模式: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

这不是最聪明的 sql,但这不是重点。

TL;DR: 关键是 'bob' 行应该在最后一个查询中显示(如 5.7 中),但在 8 中不再显示。

详情:

在 4 个表设置和插入后,查询将逐步构建,并使用早期的查询来证明错误。最里面的选择应该什么也找不到。中间选择只是重复了这一点。最后一个查询将 uu 连接到那个“无”,哦,令人惊讶的是,什么都没有变成什么! (最外面的 where 子句失败了。

问题:

发生了什么变化?我们做错了什么?我们在 5.7 中假设什么不再正确?还是bug?

数据:

drop table if exists uu, ww, kk, aa;
CREATE TABLE uu (
  id int NOT NULL AUTO_INCREMENT,
  name varchar(30),
  PRIMARY KEY (`id`)
);
CREATE TABLE ww (
  id int NOT NULL,
  PRIMARY KEY (`id`)
);
CREATE TABLE kk (
  id int NOT NULL AUTO_INCREMENT,
  uid int,
  wid int,
  state int,
  PRIMARY KEY (`id`),
  CONSTRAINT `kk_uid` FOREIGN KEY (uid) REFERENCES uu (id),
  CONSTRAINT `kk_wid` FOREIGN KEY (wid) REFERENCES ww (id)
);
CREATE TABLE aa (
  id int NOT NULL AUTO_INCREMENT,
  uid int,
  wid int,
  PRIMARY KEY (`id`),
  CONSTRAINT `aa_uid` FOREIGN KEY (uid) REFERENCES uu (id),
  CONSTRAINT `aa_wid` FOREIGN KEY (wid) REFERENCES ww (id)
);
 
insert into uu (name) values ('alice'), ('bob'), ('chuck');
insert into ww (id) values (10),(11);
insert into kk (uid,wid,state) values (2,10,99);
insert into kk (uid,wid,state) values (2,11,99);
insert into aa (uid,wid) values (2,10), (2,11);
 
-- plain joins:
select u.*, "][", k.*, "][", a.*
     from uu u
    left join kk k on k.uid = u.id
    left join aa a on u.id = a.uid
    order by u.id,k.id;
 
select a2.*, '][', k3.* -- distinct a2.wid
                                     from aa a2
                                     left join kk k3 on a2.uid = k3.uid and a2.wid = k3.wid
                                     where k3.wid is null
;
SELECT a.*
                     FROM aa a
                     where a.wid in
                           (
                                select distinct a2.wid
                                     from aa a2
                                     left join kk k3 on a2.uid = k3.uid and a2.wid = k3.wid
                                     where k3.wid is null
                           )
;
 
select u.*, '][k', k.*, '][k1', k1.*,'][tmp', tmp.*
     FROM uu u
    LEFT JOIN kk k ON k.uid = u.id
    LEFT JOIN kk k1 ON k1.uid = u.id AND k1.state != 99
    LEFT JOIN
           (
                SELECT a.*
                     FROM aa a
                     where a.wid in
                           (
                                select distinct a2.wid
                                     from aa a2
                                     left join kk k3 on a2.uid = k3.uid and a2.wid = k3.wid
                                     where k3.wid is null
                           )
           ) tmp ON tmp.uid = u.id
-- ;
   WHERE tmp.wid IS NULL
           AND (
                k.uid IS NULL
                OR
            (k.state = 99 AND k1.id IS NULL)
           )
;

【问题讨论】:

确实看起来像一个错误; tmp 子查询本身什么也没找到,但是当加入时找到 bobs:dbfiddle.uk/… 仅在 mysql 8 中,而不是 5.7 或 mariadb 哇,这个小提琴网站真是太棒了。谢谢! 【参考方案1】:

这绝对是一个错误。好像是 8.0.17 引入的。

请在 bugs.mysql.com 上报告或让我知道,我会输入。

一种解决方法是像这样设置那些优化器开关:

set optimizer_switch='materialization=off';
set optimizer_switch='derived_merge=on';

感谢您使用 MySQL。

【讨论】:

在过去几年中,我遇到了 MySQL 8.0 的几个问题,包括在次要(语义)新版本中的重大更改。这很令人沮丧,特别是注意到文档很浅 re: 从 5.7 升级到 8.0... Lefred,请帮我举报,谢谢!很有可能它会被正确写入。我也在尝试减少一次性帐户,这很有帮助。当然,让我们知道错误链接。 工程师在我询问后报告了内部问题。 仍然不是公共错误,仍然没有在 8.0.26 中修复。

以上是关于来自 5.7 的 mysql 8 查询回归(怎么了?)的主要内容,如果未能解决你的问题,请参考以下文章

查询(HAVING 子句)在 Mysql 版本 5.7 和 8.0 之间的行为不同

MySQL 5.7与MySQL 8.0性能比拼,结果怎么样?

如何将 SQL 查询从 MySQL 8 转换为 MySQL 5.7

教你怎么解决MySQL 5.7及8.0版本数据库的root密码遗忘

用最新MySQL 8.0的源安装MySQL 5.7版本(CentOS 7环境下)

mysql 5.7 json 增删改查怎么写