关于分组后排序的sql查询问题
在很多场景, 遇到需要对数据进行分组,排序 ,然后取出前几数据,常见取出前1数据等等,在不同的数据库,不同版本,sql书写也会有不同,故记录一下.
脚本准备:
CREATE TABLE `user` (
`id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`age` varchar(20) DEFAULT NULL,
`crt_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`dynasty` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 数据准备
INSERT INTO `test`.`user`(`id`, `name`, `age`, `crt_time`, `dynasty`) VALUES ('1', '杜甫', '20', '2022-11-11 11:48:39', '唐');
INSERT INTO `test`.`user`(`id`, `name`, `age`, `crt_time`, `dynasty`) VALUES ('2', '李白', '30', '2022-11-11 11:48:45', '唐');
INSERT INTO `test`.`user`(`id`, `name`, `age`, `crt_time`, `dynasty`) VALUES ('3', '白居易', '40', '2022-11-11 11:48:49', '唐');
INSERT INTO `test`.`user`(`id`, `name`, `age`, `crt_time`, `dynasty`) VALUES ('4', '苏轼', '30', '2022-11-11 11:50:01', '宋');
INSERT INTO `test`.`user`(`id`, `name`, `age`, `crt_time`, `dynasty`) VALUES ('5', '欧阳修', '40', '2022-11-11 13:54:39', '宋');
Mysql数据库
第一种
不太推荐写法: 在Mysql低版本才可以使用
SELECT
T.*
FROM
( SELECT * FROM USER ORDER BY AGE LIMIT 1000) T
GROUP BY
T.DYNASTY
问题:
1 sql查询直接报错, Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'T.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
sql的ONLY_FULL_GROUP_BY模式不支持非分组字段在查询列表出现. 否则mysql认为是非法,报上述错误.
2 子查询中限制了limit 1000, 如果数据量过大,可能导致结果不准确
第二种
分组加聚合函数
SELECT
T.DYNASTY, ANY_VALUE(T.id) , ANY_VALUE(T.age)
FROM
( SELECT * FROM USER ORDER BY AGE LIMIT 1000 ) T
GROUP BY
T.DYNASTY
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u2DylFYS-1674994687156)(assets/image-20221111142432829.png)]
使用聚合函数优化:
SELECT
DYNASTY,
ANY_VALUE ( ID ) ,
MIN( AGE )
FROM
USER
GROUP BY
DYNASTY
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7I0g0p0k-1674994687157)(assets/image-20221111144452512.png)]
第三种
使用嵌套子查询
SELECT
t1.DYNASTY,
t1.ID ,
t1.AGE
FROM
USER t1
LEFT JOIN
(SELECT
MIN( AGE ) AS age,
DYNASTY
FROM
USER
GROUP BY
DYNASTY
) t2 ON t1.age = t2.age AND t1.DYNASTY = t2.DYNASTY
WHERE t2.DYNASTY IS NOT NULL
第四种
over(partition …)函数 在Mysql高版本才可以,如mysql8
select t.*
from (select a.*, row_number() over(partition by 需要分组的字段 order by 排序的字段 desc) rn
from 表 a) t
where t.rn = 1
先把数据分组,然后排序,给组内的数据进行编号,最后取出编号为1的数据
select t.*
from (select a.*, row_number() over(partition by DYNASTY order by age ) rn
from USER a) t
where t.rn = 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ktJD49o-1674994687158)(assets/image-20221111171139001.png)]
Oracle数据库
over(partition …)函数
select t.*
from (select a.*, row_number() over(partition by 需要分组的字段 order by 排序的字段 desc) rn
from 表 a) t
where t.rn = 1
先把数据分组,然后排序,给组内的数据进行编号,最后取出编号为1的数据
select t.*
from (select a.*, row_number() over(partition by DYNASTY order by age ) rn
from USER a) t
where t.rn = 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B7H4GlbN-1674994687159)(assets/image-20221111171139001.png)]