SQL | 窗口函数 | 名次

一、编写sql语句实现每班前三名,分数一样并列,同时求出前三名按名次排序的分差

1、创建表

CREATE TABLE IF NOT EXISTS stu(
Stu_no int,
class int,
score int
);
alter table stu change column Stu_no stu_no int;

2、插入数据

INSERT INTO stu VALUES(1,1901,90);
INSERT INTO stu VALUES(2,1901,90);
INSERT INTO stu VALUES(3,1901,83);
INSERT INTO stu VALUES(4,1901,60);
INSERT INTO stu VALUES(5,1902,66);
INSERT INTO stu VALUES(6,1902,23);
INSERT INTO stu VALUES(7,1902,99);
INSERT INTO stu VALUES(8,1902,67);
INSERT INTO stu VALUES(9,1902,87);

3、逻辑SQL
(1)添加一列排序,分数一样的并列,因为用DENSE_RANK() OVER()

mysql> SELECT stu_no,class,score,DENSE_RANK() OVER(PARTITION BY class ORDER BY score DESC) as drk
    ->  FROM stu;
+--------+-------+-------+-----+
| stu_no | class | score | drk |
+--------+-------+-------+-----+
|      1 |  1901 |    90 |   1 |
|      2 |  1901 |    90 |   1 |
|      3 |  1901 |    83 |   2 |
|      4 |  1901 |    60 |   3 |
|      7 |  1902 |    99 |   1 |
|      9 |  1902 |    87 |   2 |
|      8 |  1902 |    67 |   3 |
|      5 |  1902 |    66 |   4 |
|      6 |  1902 |    23 |   5 |
+--------+-------+-------+-----+
9 rows in set (0.01 sec)

(2)使用LEAD() OVER()函数,按照班级分组名次排序,求出该名次的前一名分数。
使用IFNULL(expr1,expr2)函数(功能类似于Oracle的NVL(),如果不为NULL返回expr1,如果为NULL返回expr2),将空值转换为其他值
添加WHERE条件删选出前3名

mysql> SELECT stu_no,class,score,drk,score-IFNULL(LAG(score) OVER(PARTITION BY class ORDER BY drk),0) AS dev
    -> FROM (SELECT stu_no,class,score,DENSE_RANK() OVER(PARTITION BY class ORDER BY score DESC) as drk
    ->       FROM stu) t1
    -> WHERE drk<4;
+--------+-------+-------+-----+------+
| stu_no | class | score | drk | dev  |
+--------+-------+-------+-----+------+
|      1 |  1901 |    90 |   1 |   90 |
|      2 |  1901 |    90 |   1 |    0 |
|      3 |  1901 |    83 |   2 |   -7 |
|      4 |  1901 |    60 |   3 |  -23 |
|      7 |  1902 |    99 |   1 |   99 |
|      9 |  1902 |    87 |   2 |  -12 |
|      8 |  1902 |    67 |   3 |  -20 |
+--------+-------+-------+-----+------+
7 rows in set (0.00 sec)
二、某APP每天访问数据存放在表access_log里面
日期字段用户类型用户账号用户访问时间
dsuser_typeuser_idlog_time

1、每天整体的访问UV、PV
UV为访问人数,去UV时需按照用户去重
PV为访问次数(同一用户访问10次,PV加10)
如果ds为年月日时分秒的显示方式

SELECT DATE(ds),COUNT(DISTINCT userid) AS UV,COUNT(1) AS PV
FROM access_log
GROUP BY DATE(ds);

2、每天每个类型的访问UV、PV

SELECT DATE(ds),user_type,COUNT(DISTINCT userid) AS UV,COUNT(1) AS PV
FROM access_log
GROUP BY DATE(ds),user_type;

3、每天每个类型中最早访问时间和最晚访问时间
使用FRIST() OVER() \ LAST() OVER()窗口函数

SELECT DATE(ds),user_type
	   FIRST(log_time) OVER(PARTITION BY ds,user_type ORDER BY log_time) AS '最早访问时间'LAST(log_time) OVER(PARTITION BY ds,user_type ORDER BY log_time) AS '最晚访问时间'
FROM access_log;

4、每天每个类型中访问次数最高的10个用户
(1)每天每个类型中 各用户的访问次数

SELECT DATE(ds),user_type,user_id,COUNT(1) AS co
FROM access_log
GROUP BY DATE(ds),user_type,user_id;

(2)取出每天每个类型中访问次数为前10的用户
使用ROW_NUMBER() OVER()
使用WHERE条件筛选出排序<=10的

SELECT user_id,rn
FROM (SELECT *,ROW_NUMBER() OVER(PARTITION BY DATE(ds),user_type ORDER BY co) AS rn
	  FROM (SELECT DATE(ds),user_type,user_id,COUNT(1) AS co
	 		FROM access_log
	  		GROUP BY DATE(ds),user_type,user_id) t1 ) t2
WHERE rn<=10;
三、部门前3高的工资
四、男女各自第一名

(0:男,1:女)
1、创建表

CREATE TABLE IF NOT EXISTS score(
id INT,
sex INT,
chinese_s INT,
math_s INT
);

2、插入数据

INSERT INTO score VALUES(0,0,70,50);
INSERT INTO score VALUES(1,0,90,70);
INSERT INTO score VALUES(2,1,80,90);

(3)逻辑SQL
(a)男女各自语文第一名
INNER JOIN 使用

mysql> SELECT s1.id,s1.sex,s1.chinese_s,s1.math_s
    -> FROM score s1
    -> INNER JOIN (SELECT sex,MAX(chinese_s) AS m_chinese_s
    -> 			   FROM score
    -> 			   GROUP BY sex) s2
    -> ON s1.sex=s2.sex
    -> AND s1.chinese_s=s2.m_chinese_s;
+------+------+-----------+--------+
| id   | sex  | chinese_s | math_s |
+------+------+-----------+--------+
|    1 |    0 |        90 |     70 |
|    2 |    1 |        80 |     90 |
+------+------+-----------+--------+
2 rows in set (0.01 sec)

(b)男生成绩语文大于80,女生数学成绩大于70
UNION使用

mysql> SELECT * FROM score WHERE sex=0 AND chinese_s>80
    -> UNION
    -> SELECT * FROM score WHERE sex=1 AND math_s>70;
+------+------+-----------+--------+
| id   | sex  | chinese_s | math_s |
+------+------+-----------+--------+
|    1 |    0 |        90 |     70 |
|    2 |    1 |        80 |     90 |
+------+------+-----------+--------+
2 rows in set (0.00 sec)
##或者
mysql> SELECT * FROM score WHERE (sex=0 AND chinese_s>80) OR (sex=1 AND math_s>70);
+------+------+-----------+--------+
| id   | sex  | chinese_s | math_s |
+------+------+-----------+--------+
|    1 |    0 |        90 |     70 |
|    2 |    1 |        80 |     90 |
+------+------+-----------+--------+
2 rows in set (0.00 sec)
五、时间/日期排序

用户登录时间表login2,

  • id
  • 姓名name
  • 邮箱地址mail
  • 最后登录时间logintime

生成一张临时表(表名:用户登录表),表中呈现四列数据分别为:姓名,最后登录时间,登录时间排名,登录天数排名

要求:

  1. “登录时间排名”这一列:按时间给出每个人的登录次数,登录时间最早的为1,之后的分别是2,3,4等。
  2. “登录天数排名”这一列:按天给出每个人的登录次数,同一天多次登录认为是同一次,最早标记为1,之后以此类推。

1、创建表

CREATE TABLE IF NOT EXISTS login2(
id INT,
name VARCHAR(10),
mail VARCHAR(50),
logindate TIMESTAMP
);

2、插入数据

INSERT INTO login2 VALUES(99,'name2','mail2@126.com','2020-10-20 16:00'),
						 (13,'name1','mail1@126.com','2020-03-20 15:00'), 
						 (20,'name1','mail1@126.com','2020-10-20 14:13'),
						 (43,'name1','mail1@126.com','2020-10-20 14:20'),
						 (45,'name3','mail3@126.com','2020-04-20 14:17'),
						 (49,'name3','mail3@126.com','2020-05-20 14:22');

3、逻辑SQL
要求分析:

  1. “登录时间排名”这一列:按时间给出每个人的登录次数,登录时间最早的为1,之后的分别是2,3,4等。

    连续排序,无并列名次,ROW_NUMBER() OVER()
    每个人:按照用户名name分组   
    登录时间越早排序越靠前:登录时间升序ASC,默认即升序
    
  2. “登录天数排名”这一列:按天给出每个人的登录次数,同一天多次登录认为是同一次,最早标记为1,之后以此类推。

    按天排序:DATE_FORMAT()对logindate类型进行转换 ‘%Y%m%d’ 
    连续排序,有并列名次,DENSE_RANK()  OVER() 
    每个人:按照用户名name分组 
    登录时间越早排序越靠前:登录时间升序ASC,默认即升序
    
mysql> SELECT name AS '姓名',
    ->    logindate AS '最后登录时间',
    ->    ROW_NUMBER() OVER(PARTITION BY name ORDER BY logindate) AS '登录时间排名',
    ->    DENSE_RANK() OVER(PARTITION BY name ORDER BY DATE_FORMAT(logindate,'%Y%m%d')) AS '登录天数排名'
    -> FROM login2;
+--------+---------------------+--------------------+--------------------+
| 姓名   | 最后登录时间        | 登录时间排名       | 登录天数排名       |
+--------+---------------------+--------------------+--------------------+
| name1  | 2020-03-20 15:00:00 |                  1 |                  1 |
| name1  | 2020-10-20 14:13:00 |                  2 |                  2 |
| name1  | 2020-10-20 14:20:00 |                  3 |                  2 |
| name2  | 2020-10-20 16:00:00 |                  1 |                  1 |
| name3  | 2020-04-20 14:17:00 |                  1 |                  1 |
| name3  | 2020-05-20 14:22:00 |                  2 |                  2 |
+--------+---------------------+--------------------+--------------------+
6 rows in set (0.00 sec)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值