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

Posted

技术标签:

【中文标题】查询(HAVING 子句)在 Mysql 版本 5.7 和 8.0 之间的行为不同【英文标题】:Query (HAVING Clause) does not behave the same between Mysql Version 5.7 and 8.0 【发布时间】:2021-11-26 10:25:37 【问题描述】:

所以最近我被要求将 mysql 版本从 5.7 升级到 Mysql 8。升级后,我注意到一个奇怪的行为,两个版本产生的结果完全不同。下面是结构和示例数据。

创建表

DROP TABLE IF EXISTS `Schema`.`Table`;
CREATE TABLE  `Schema`.`Table` (
  `ExchNo` int NOT NULL,
  `StkID` int NOT NULL,
  `Date` date NOT NULL DEFAULT '1899-12-31',
  `Price` decimal(18,10) NOT NULL DEFAULT '0.0000000000',
  PRIMARY KEY (`ExchNo`,`StkID`,`Date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

插入数据

INSERT INTO `Schema`.`Table` (`ExchNo`,`StkID`,`Date`,`Price`) 
VALUES ('1','1','2014-12-16 00:00:00','0.3200000000'),('1','1','2014-12-17 00:00:00','0.3200000000'),('1','1','2014-12-18 00:00:00','0.3350000000'),('1','1','2014-12-19 00:00:00','0.3400000000'),('1','1','2014-12-22 00:00:00','0.4200000000'),('1','1','2014-12-23 00:00:00','0.4100000000'),('1','1','2014-12-24 00:00:00','0.4250000000'),('1','1','2014-12-26 00:00:00','0.4000000000'),('1','1','2014-12-29 00:00:00','0.4150000000'),('1','1','2014-12-30 00:00:00','0.4150000000'),('1','1','2014-12-31 00:00:00','0.4250000000'),('1','6','2014-12-16 00:00:00','9.5500000000'),('1','6','2014-12-17 00:00:00','9.4300000000'),('1','6','2014-12-18 00:00:00','9.6200000000'),('1','6','2014-12-19 00:00:00','9.7500000000'),('1','6','2014-12-22 00:00:00','9.6300000000'),('1','6','2014-12-23 00:00:00','9.8500000000'),('1','6','2014-12-24 00:00:00','9.8900000000'),('1','6','2014-12-26 00:00:00','10.0000000000'),('1','6','2014-12-29 00:00:00','10.0800000000'),('1','6','2014-12-30 00:00:00','10.1600000000'),('1','6','2014-12-31 00:00:00','10.0000000000'),('1','8','2014-12-16 00:00:00','2.6900000000'),('1','8','2014-12-17 00:00:00','2.8100000000'),('1','8','2014-12-18 00:00:00','2.8000000000'),('1','8','2014-12-19 00:00:00','2.9700000000'),('1','8','2014-12-22 00:00:00','2.9400000000'),('1','8','2014-12-23 00:00:00','2.9400000000'),('1','8','2014-12-24 00:00:00','2.9500000000'),('1','8','2014-12-26 00:00:00','2.9700000000'),('1','8','2014-12-29 00:00:00','2.9100000000'),('1','8','2014-12-30 00:00:00','2.9900000000'),('1','8','2014-12-31 00:00:00','2.9000000000');

在 Mysql 5.7 中,使用下面的查询(使用用户变量),如果数据很大(100 万行以上),我可以极快地获得每个StkID 的最新Price

SELECT  *,(@Count0:= if(@TempID0 = `StkID`, @Count0 +1, 1)) Counter,
            (@TempID0:=`StkID`) Tempid
    FROM  `Schema`.`Table`, 
          ( SELECT  @Count0:=0,@TempID0:=0 ) sqlvar
    WHERE  `Date` <= '2018-12-31 00:00:00'
    having  Counter<=1
    ORDER BY  `StkID`,`Date` DESC;

输出:

ExchNo StkID Date Price @Count0:=0 @TempID0:=0 Counter Tempid
1 1 2014-12-31 0.42500 0 0 1 1
1 6 2014-12-31 10.0000 0 0 1 6
1 8 2014-12-31 2.90000 0 0 1 8

在 Mysql 8.0 版上尝试相同的查询时。它只是返回整个表而不考虑Counter &lt;= 1 的Having 子句。

我可以将整个查询包装成一个子查询并获得所需的结果,但它确实会大大影响性能。

Select  *
    From  
        ( SELECT  *,
                  (@Count0:= if(@TempID0 = `StkID`, @Count0 +1, 1)) Counter,
                  (@TempID0:=`StkID`) Tempid
            FROM  `Schema`.`Table`, 
                  ( SELECT  @Count0:=0,@TempID0:=0 ) sqlvar
            WHERE  `Date` <= '2018-12-31 00:00:00'
            ORDER BY  `StkID`,`Date` DESC
        ) a
    Where  Counter<=1 

所以我的问题是,这是一个错误吗?如果不是,有没有办法在不影响性能的情况下有效地实现结果?还出现了一条警告消息,我不确定这是否与结果有关。

不推荐在表达式中设置用户变量,并将 在未来的版本中删除。考虑替代方案:'SET variable=expression, ...' 或 'SELECT 表达式 INTO 变量'。

我希望有人能解决我的问题,提前谢谢你!

【问题讨论】:

对于更快的“groupwise-maximum”,请参阅我添加的标签。 【参考方案1】:

我相信会话/用户变量自 MySQL 8+ 起已被弃用。但是,你不再需要它们了,因为 MySQL 8+ 已经引入了分析函数。你可以在这里使用ROW_NUMBER

WITH cte AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY StkID ORDER BY Date DESC) rn
    FROM yourTable
    WHERE Date <= '2018-12-31'
)

SELECT *
FROM cte
WHERE rn = 1
ORDER BY stkID;

【讨论】:

谢谢蒂姆,我一直是你的粉丝。并对你所拥有的广博知识感到惊讶。我还是 CTE 的新手,但我会尽力学习。您对我可以在哪里了解有关您使用的子句/功能的更多信息有什么建议吗?例如。 ROW_Number(), Over, Partition By.. @RyanTan 老实说,我通过参加 Stack Overflow 磨练了我的分析功能技能。作为一名 Java 开发人员,我经常只使用 ANSI SQL 的东西,所以 SO 是学习这些东西的好地方。

以上是关于查询(HAVING 子句)在 Mysql 版本 5.7 和 8.0 之间的行为不同的主要内容,如果未能解决你的问题,请参考以下文章

MySQL的having子句

剑指架构师系列-MySQL常用SQL语句

mysql Having子句的力量

mysql 查询的所有操作

mysql查询子查询连接查询

从带有 HAVING 子句的 MySQL 视图中选择返回空结果集