一、编写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里面
日期字段 | 用户类型 | 用户账号 | 用户访问时间 |
---|---|---|---|
ds | user_type | user_id | log_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,之后的分别是2,3,4等。
- “登录天数排名”这一列:按天给出每个人的登录次数,同一天多次登录认为是同一次,最早标记为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,之后的分别是2,3,4等。
连续排序,无并列名次,ROW_NUMBER() OVER() 每个人:按照用户名name分组 登录时间越早排序越靠前:登录时间升序ASC,默认即升序
-
“登录天数排名”这一列:按天给出每个人的登录次数,同一天多次登录认为是同一次,最早标记为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)