关于分组后排序的sql查询问题

关于分组后排序的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)]

你可能感兴趣的:(Java学习,sql,数据库,mysql)