查询(HAVING 子句)在 Mysql 版本 5.7 和 8.0 之间的行为不同
Query (HAVING Clause) does not behave the same between Mysql Version 5.7 and 8.0
所以最近我被要求将 Mysql 版本从 5.7 升级到 Mysql 8。升级后,我注意到一个奇怪的行为,即两个版本产生的结果完全不同。以下是结构和示例数据。
创建Table
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 中,使用下面的查询(使用用户变量),允许我获得每个 StkID
的最新 Price
考虑到数据是否很大( 100 万行+)
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 上尝试相同的查询时。它只是 returns 整个 table 而没有考虑 Counter <= 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
所以我的问题是,这是一个错误吗?如果不是,有没有一种方法可以在不影响性能的情况下有效地实现结果?还出现了一条警告消息,我不确定这是否与结果有关。
Setting user variables within expressions is deprecated and will be
removed in a future release. Consider alternatives: 'SET
variable=expression, ...', or 'SELECT expression(s) INTO
variables(s)'.
希望有人能解答我的问题,先谢谢了!
我相信从 MySQL 8+ 开始,session/user 变量已被弃用。但是,你真的不再需要它们了,因为 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;
所以最近我被要求将 Mysql 版本从 5.7 升级到 Mysql 8。升级后,我注意到一个奇怪的行为,即两个版本产生的结果完全不同。以下是结构和示例数据。
创建Table
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 中,使用下面的查询(使用用户变量),允许我获得每个 StkID
的最新 Price
考虑到数据是否很大( 100 万行+)
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 上尝试相同的查询时。它只是 returns 整个 table 而没有考虑 Counter <= 1
.
我可以将整个查询包装到子查询中并获得所需的结果,但这确实会显着影响性能。
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
所以我的问题是,这是一个错误吗?如果不是,有没有一种方法可以在不影响性能的情况下有效地实现结果?还出现了一条警告消息,我不确定这是否与结果有关。
Setting user variables within expressions is deprecated and will be removed in a future release. Consider alternatives: 'SET variable=expression, ...', or 'SELECT expression(s) INTO variables(s)'.
希望有人能解答我的问题,先谢谢了!
我相信从 MySQL 8+ 开始,session/user 变量已被弃用。但是,你真的不再需要它们了,因为 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;