我想知道为什么子查询不能像上面那样得到想要的结果:
select * from quote group by code having date=max(date);
开始:
select * from quote group by code
从 SQL 标准的角度来看,此查询本身不是有效的。
如果所有其他列在功能上都依赖于code,则可能是这样,而基于表定义的情况并非如此(代码不是唯一的,也不是主键)。相关阅读:Group by clause in mySQL and postgreSQL, why the error in postgreSQL?
查询的行为类似于ANY_VALUE:
select code, ANY_VALUE(id), ANY_VALUE(`date`), ANY_VALUE(`open`)...
from quote
group by code
关于第二部分:
having date=max(date);
--
having any_value(date) = max(date) -- sidenote: it will work for single row per `code`
这里 HAVING 中的条件在聚合后应用,这意味着比较是在每个代码的 MAX(date) 与“未指定”日期之间进行比较。
举例说明(此代码仅在only_full_group_by 关闭时才有效):
CREATE TABLE `quote` (
`id` int(8) unsigned NOT NULL AUTO_INCREMENT,
`code` text COLLATE utf8mb4_unicode_ci,
`date` date DEFAULT NULL,
`open` double DEFAULT NULL,
PRIMARY KEY (`id`)
) ;
INSERT INTO quote(`code`, `date`, `open`)
VALUES ('a', '2020-01-01',10),
('a', '2021-01-01',20),
('a', '2022-01-01',30);
还有查询:
SELECT * FROM quote;
+-----+-------+-------------+------+
| id | code | date | open |
+-----+-------+-------------+------+
| 1 | a | 2020-01-01 | 10 |
| 2 | a | 2021-01-01 | 20 |
| 3 | a | 2022-01-01 | 30 |
+-----+-------+-------------+------+
select * from quote group by code;
-- this part is unspecified, id/date/open are arbitrary
+-----+-------+-------------+------+
| id | code | date | open |
+-----+-------+-------------+------+
| 1 | a | 2020-01-01 | 1 |
+-----+-------+-------------+------+
select *, MAX(date) from quote group by code;
-- MAX(date) is stable, date is arbitrary, comparison does not make sense at this point
+-----+-------+-------------+-------+------------+
| id | code | date | open | MAX(date) |
+-----+-------+-------------+-------+------------+
| 1 | a | 2020-01-01 | 10 | 2022-01-01 |
+-----+-------+-------------+-------+------------+
select * from quote group by code having date=max(date);
-- empty
+-----+-------+-------+------+
| id | code | date | open |
+-----+-------+-------+------+
db<>fiddle demo
这么说,为了得到所有列 ranking functions MySQL 8.0+ 可以使用:
本节描述非聚合窗口函数,对于查询中的每一行,使用与该行相关的行执行计算
SELECT *
FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY `code` ORDER BY `date` DESC) AS rn
FROM `quote`) s --RANK() if `date` is not unique per code
WHERE rn = 1;
db<>fiddle demo 2