京东-优惠雷达
新人页面
精选商品
首月0月租体验,领12个月京东PLUS
自营热卖

Python学习日志16 - 数据库SQL查询

伤城离歌 24天前   阅读数 40 0

Python学习日志

RBHGO的主页欢迎关注

温馨提示:创作不易,如有转载,注明出处,感谢配合~

目录

Python学习日志16课 - 数据库SQL查询

SQL —> DQL —> select

这里只写了select,不代表只有select。在用SQL语句查询时,SQL语句它是多变的,同一条题目要求会有很多不同的写法,所有只有写的足够多,不管遇到什么样的查询,我们都能有方法写出来。本文以三个难度依次递增的查询题目主要分享MySQL但不仅限于MySQL的SQL查询

-- 1.查看SQL执行计划
-- 2.使用性能剖析系统
	-- explain select语句 ---> 得到搜索语句的性能
	-- type性能(坏->好) ALL -> index -> range -> ref -> ref eg -> const/svstem

就像Python学习日志15中提到的,我们首先要在数据库中建立多表,且表与表之间要具有联系。所以现在我们就来在以shool这个数据库为例,建立多个表。

DQL (数据查询语言)

  1. MySQL 中支持多种类型的运算符,包括:算术运算符(+-*/%)、比较运算符(=<><=><<=>>=BETWEEN...AND...、INIS NULLIS NOT NULLLIKERLIKEREGEXP)、逻辑运算符(NOTANDORXOR)和位运算符(&|^~>><<),我们可以在 DQL 中使用这些运算符处理数据。

  2. 在查询数据时,可以在SELECT语句及其子句(如WHERE子句、ORDER BY子句、HAVING子句等)中使用函数,这些函数包括字符串函数、数值函数、时间日期函数、流程函数等。

    函数不会用没关系,上面那一篇分享中也提到过,通过+函数名(?function查看所有函数)可以得到函数的使用方法。

    这里以ROUND函数为例子我们在命令提示符(Workbench中不行)中mysql -u root -p进入MySQL(前提是您装了MySQL),? round,我们就会得到反馈大致如下:

    round() —> 四舍五入,可以设定位数

    • round(1.298,2) -> 1.30
    • round(1.298,0) -> 1
    • round(211.298,-2) -> 200

    温馨提示: SQL语句在不同数据库间相通,但会有些许不同,意思就是每一种数据库都会有自己的”方言“,我这里前提是MySQL。

常用字符串函数
函数 功能
CONCAT 将多个字符串连接成一个字符串
FORMAT 将数值格式化成字符串并指定保留几位小数
FROM_BASE64 / TO_BASE64 BASE64解码/编码
BIN / OCT / HEX 将数值转换成二进制/八进制/十六进制字符串
LOCATE 在字符串中查找一个子串的位置
LEFT / RIGHT 返回一个字符串左边/右边指定长度的字符
LENGTH / CHAR_LENGTH 返回字符串的长度以字节/字符为单位
LOWER / UPPER 返回字符串的小写/大写形式
LPAD / RPAD 如果字符串的长度不足,在字符串左边/右边填充指定的字符
LTRIM / RTRIM 去掉字符串前面/后面的空格
ORD / CHAR 返回字符对应的编码/返回编码对应的字符
STRCMP 比较字符串,返回-1、0、1分别表示小于、等于、大于
SUBSTRING 返回字符串指定范围的子串
常用数值函数
函数 功能
ABS 返回一个数的绝度值
CEILING / FLOOR 返回一个数上取整/下取整的结果
CONV 将一个数从一种进制转换成另一种进制
CRC32 计算循环冗余校验码
EXP / LOG / LOG2 / LOG10 计算指数/对数
POW 求幂
RAND 返回[0,1)范围的随机数
ROUND 返回一个数四舍五入后的结果
SQRT 返回一个数的平方根
TRUNCATE 截断一个数到指定的精度
SIN / COS / TAN / COT / ASIN / ACOS / ATAN 三角函数
SUM 返回条件下的数值的和
AVG 返回条件下的数值的平均数
MAX / MIN 最大值 / 最小值
常用时间日期函数
函数 功能
CURDATE / CURTIME / NOW 获取当前日期/时间/日期和时间
ADDDATE / SUBDATE 将两个日期表达式相加/相减并返回结果
DATE / TIME 从字符串中获取日期/时间
YEAR / MONTH / DAY 从日期中获取年/月/日
HOUR / MINUTE / SECOND 从时间中获取时/分/秒
DATEDIFF / TIMEDIFF 返回两个时间日期表达式相差多少天/小时
MAKEDATE / MAKETIME 制造一个日期/时间
常用流程函数
函数 功能
IF 根据条件是否成立返回不同的值
IFNULL 如果为NULL则返回指定的值否则就返回本身
NULLIF 两个表达式相等就返回NULL否则返回第一个表达式的值
其他常用函数
函数 功能
MD5 / SHA1 / SHA2 返回字符串对应的哈希摘要
CHARSET / COLLATION 返回字符集/校对规则
USER / CURRENT_USER 返回当前用户
DATABASE 返回当前数据库名
VERSION 返回当前数据库版本
FOUND_ROWS / ROW_COUNT 返回查询到的行数/受影响的行数
LAST_INSERT_ID 返回最后一个自增主键的值
UUID / UUID_SHORT 返回全局唯一标识符

正式开始

如果您看到这里并且对此感兴趣,那么请一条一条的和我写下来,我在题目上基本都有会提示,并且会 一 一 介绍碰到的知识。如果您能看懂每一条语句,并且您都可以独立写下来,那么您对SQL查询语句就已经基本掌握。后续如果能继续练习,那么基本上您以后遇到的SQL查询,都可以用以下的方法做出来。

建库建表一

第一个数据库是学校库,有学生表、老师表、课程表、学院表、选课记录表。表与表之间都存在着关系,分别是1对1、一对多和多对多,我们建表之间应该对他们的关系应该做到心里有数,不过增删改不是这篇分享的重点。

                    
                    -- 如果存在名为school的数据库就删除它
                    drop database if exists `school`;
                    
                    -- 创建名为school的数据库并设置默认的字符集和排序方式
                    create database `school` default character set utf8mb4;

                    -- 切换到school数据库上下文环境
                    use `school`;

                    -- 创建学院表
                    create table `tb_college`
                    (
                    `col_id` int unsigned auto_increment comment '编号',
                    `col_name` varchar(50) not null comment '名称',
                    `col_intro` varchar(500) default '' comment '介绍',
                    primary key (`col_id`)
                    ) engine=innodb auto_increment=1 comment '学院表';

                    -- 创建学生表
                    create table `tb_student`
                    (
                    `stu_id` int unsigned not null comment '学号',
                    `stu_name` varchar(20) not null comment '姓名',
                    `stu_sex` boolean default 1 not null comment '性别',
                    `stu_birth` date not null comment '出生日期',
                    `stu_addr` varchar(255) default '' comment '籍贯',
                    `col_id` int unsigned not null comment '所属学院',
                    primary key (`stu_id`),
                    constraint `fk_student_col_id` foreign key (`col_id`) references `tb_college` (`col_id`)
                    ) engine=innodb comment '学生表';

                    -- 创建教师表
                    create table `tb_teacher`
                    (
                    `tea_id` int unsigned not null comment '工号',
                    `tea_name` varchar(20) not null comment '姓名',
                    `tea_title` varchar(10) default '助教' comment '职称',
                    `col_id` int unsigned not null comment '所属学院',
                    primary key (`tea_id`),
                    constraint `fk_teacher_col_id` foreign key (`col_id`) references `tb_college` (`col_id`)
                    ) engine=innodb comment '老师表';

                    -- 创建课程表
                    create table `tb_course`
                    (
                    `cou_id` int unsigned not null comment '编号',
                    `cou_name` varchar(50) not null comment '名称',
                    `cou_credit` int not null comment '学分',
                    `tea_id` int unsigned not null comment '授课老师',
                    primary key (`cou_id`),
                    constraint `fk_course_tea_id` foreign key (`tea_id`) references `tb_teacher` (`tea_id`)
                    ) engine=innodb comment '课程表';

                    -- 创建选课记录表
                    create table `tb_record`
                    (
                    `rec_id` bigint unsigned auto_increment comment '选课记录号',
                    `stu_id` int unsigned not null comment '学号',
                    `cou_id` int unsigned not null comment '课程编号',
                    `sel_date` date not null comment '选课日期',
                    `score` decimal(4,1) comment '考试成绩',
                    primary key (`rec_id`),
                    constraint `fk_record_stu_id` foreign key (`stu_id`) references `tb_student` (`stu_id`),
                    constraint `fk_record_cou_id` foreign key (`cou_id`) references `tb_course` (`cou_id`),
                    constraint `uk_record_stu_cou` unique (`stu_id`, `cou_id`)
                    ) engine=innodb comment '选课记录表';

                    use school;

                    -- 插入学院数据
                    insert into `tb_college` 
                        (`col_name`, `col_intro`) 
                    values 
                        ('Python学院', 'Python 是由 Guido van Rossum 在八十年代末和九十年代初,在荷兰国家数学和计算机科学研究所设计出来的。Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。'),
                        ('Java学院', 'Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的高级程序设计语言。Java 可运行于多个平台,如 Windows, Mac OS 及其他多种 UNIX 版本的系统。后来 Sun 公司被 Oracle (甲骨文)公司收购,Java 也随之成为 Oracle 公司的产品。Java 语言是简单的、Java 语言是面向对象的、Java语言是分布式的、Java 语言是健壮的、Java语言是安全的、Java 语言是体系结构中立的、Java 语言是可移植的、Java 语言是解释型的、Java 是高性能的、Java 语言是多线程的、Java 语言是动态的'),
                        ('HTML学院', 'HTML 指的是超文本标记语言: HyperText Markup Language,它不是一种编程语言,而是一种标记语言,标记语言是一套标记标签 (markup tag),HTML 使用标记标签来描述网页,HTML文档包含了HTML标签及文本内容,HTML文档也叫做 web 页面');

                    -- 插入学生数据
                    insert into `tb_student` 
                        (`stu_id`, `stu_name`, `stu_sex`, `stu_birth`, `stu_addr`, `col_id`) 
                    values
                        (1001, '留一手', 1, '1990-3-4', '湖南长沙', 1),
                        (1002, '二麻子', 1, '1992-2-2', '台湾台北', 1),
                        (1033, '法外狂徒张三', 0, '1989-12-3', '北京', 1),
                        (1572, '四季度', 1, '1993-7-19', '陕西咸阳', 1),
                        (1378, '五条悟', 0, '1995-8-12', '四川成都', 1),
                        (1954, '小六子', 1, '1994-9-20', '福建莆田', 1),
                        (2035, '鬼脚七', 1, '1988-6-30', null, 2),
                        (3011, '老八', 1, '1985-12-12', '海南海口', 3),
                        (3755, '九妹', 1, '1993-1-25', null, 3),
                        (3923, '大十字', 0, '1985-4-17', '贵州贵阳', 3);

                    -- 插入老师数据
                    insert into `tb_teacher` 
                        (`tea_id`, `tea_name`, `tea_title`, `col_id`) 
                    values 
                        (1122, '萧十一郎', '教授', 1),
                        (1133, '十二月', '副教授', 1),
                        (1144, '十三姨', '副教授', 1),
                        (2255, '十四松', '副教授', 2),
                        (3366, '三五十五', default, 3);

                    -- 插入课程数据
                    insert into `tb_course` 
                        (`cou_id`, `cou_name`, `cou_credit`, `tea_id`) 
                    values 
                        (1111, 'Python程序设计', 3, 1122),
                        (2222, '信号与图像处理', 5, 1122),
                        (3333, '高等数学', 5, 1122),
                        (4444, '计算机网络', 3, 1133),
                        (5555, '离散数学', 3, 1144),
                        (6666, '数据结构', 5, 1144),
                        (7777, '数据挖掘', 3, 2255),
                        (8888, '大数据技术原理与应用', 2, 3366),
                        (9999, '人工智能导论', 3, 3366);

                    -- 插入选课数据
                    insert into `tb_record` 
                        (`stu_id`, `cou_id`, `sel_date`, `score`) 
                    values 
                        (1001, 1111, '2017-09-01', 95),
                        (1001, 2222, '2019-09-01', 87.5),
                        (1001, 3333, '2017-09-01', 100),
                        (1001, 4444, '2018-09-03', null),
                        (1001, 6666, '2018-09-02', 100),
                        (1002, 1111, '2017-09-03', 65),
                        (1002, 5555, '2018-09-01', 42),
                        (1033, 1111, '2017-09-03', 92.5),
                        (1033, 4444, '2019-09-01', 78),
                        (1033, 5555, '2017-09-01', 82.5),
                        (1572, 1111, '2020-09-02', 78),
                        (1378, 1111, '2020-09-05', 82),
                        (1378, 7777, '2020-09-02', 65.5),
                        (2035, 7777, '2018-09-03', 88),
                        (2035, 9999, '2019-09-02', null),
                        (3755, 1111, '2019-09-02', null),
                        (3755, 8888, '2019-09-02', null),
                        (3755, 9999, '2017-09-01', 92);
第一套:有关学习的SQL查询

这一套题包含了大部分的查询基础知识,我们平时用的多的语句,在里面都有所展现,如果您没有基础,那就请认真看看吧;如果您学过,并且发现了我的错误,也可以在评论区与我讨论或者私信我,感谢。

-- 查询所有学生信息
	-- *有表示所有信息的作用,但在此不建议使用* ,最好是要多少列写多少列
	select * from tb_student;
	select stu_id, stu_name, stu_sex, stu_birth, stu_addr, col_id from tb_student;
    
-- 查询所有课程名称及学分(投影和别名) ---> 别名用as(alias),别名可以是中文
	select cou_name as 课程名称, cou_credit as 学分 from tb_course;

-- 查询所有女学生的姓名和出生日期(筛选)
	select stu_name, stu_birth from tb_student where stu_sex=0;

-- 查询所有80后学生的姓名、性别和出生日期(筛选)
	select stu_name, stu_sex, stu_birth from tb_student 
    where stu_birth between '1980-1-1' and '1989-12-31';
	
	select stu_name, stu_sex, stu_birth from tb_student 
	where stu_birth >= '1980-1-1' and stu_birth <= '1989-12-31';
	
-- 对列进行变换,SQL语句用关键字case、when、then、else、end实现
	select 
		stu_name as 姓名, 
		case stu_sex when 1 then '男' else '女' end as 性别, 
		stu_birth as 出生日期 
        from tb_student 
		where stu_birth between '1980-1-1' and '1989-12-31';
    -- MaSQL方言 if() 函数,例如在Oracle中使用 decode() 函数 
		select 
			stu_name as 姓名, 
			if(stu_sex,'男','女') as 性别, 
			stu_birth as 出生日期 
			from tb_student 
			where stu_birth between '1980-1-1' and '1989-12-31';

-- 查询姓”十“的学生姓名和性别(模糊) 
-- 在SQL语句的通配符(wildcard) ---> %可以匹配零个或任意多个字符
	select stu_name, stu_sex from tb_student where stu_name like '十%';

-- 查询姓”十“名字两个字的学生姓名和性别(模糊) 
-- 在SQL语句的通配符 ---> _可以精确匹配一个字符
-- 也可以使用正则表达式(略有不同)---> 关键字regexp(regular expression)

select stu_name, stu_sex from tb_student where stu_name like '十_ _';

select stu_name, stu_sex from tb_student where stu_name regexp '^十.{2}$';

-- 查询名字中有“五”字或“一”字的学生的姓名(模糊) 
	-- 在前面加%,效率低 和 or
    -- 可以使用union求并集(不重复), unnion all (重复)
		select stu_name from tb_student where stu_name like '%五%' or stu_name like '%一%';
		
	select stu_name from tb_student where stu_name like '%五%'
	union
	select stu_name from tb_student where stu_name like '%一%';


-- 查询没有录入家庭住址的学生姓名(空值) ---> 小知识:<> 是不等号 <=>是与空值比较的等号(两者间没有关系)
	-- 使用 is / is not 判断,不能使用 = 判断空值
		select stu_name from tb_student where stu_addr is null;
		select stu_name from tb_student where stu_addr<=>null;
	-- 不与空值做判断可以使用 = <>
		select stu_name from tb_student 
		where stu_addr is null or stu_addr='';

-- 查询录入了家庭住址的学生姓名(空值)
	select stu_name from tb_student 
    where stu_addr is not null and stu_addr<>'';

-- 查询学生选课的所有日期(去重) ---> 关键字distinct
	select distinct sel_date from tb_record;

-- 查询学生的家庭住址(去重)
	select distinctrow stu_addr from tb_student 
    where stu_addr is not null;

-- 查询男学生的姓名和生日按年龄从大到小排列(排序) 
	-- 使用order by
	-- 不写的话默认是asc(ascending 升序), desc(descending降序),建议还是写可读性高一点
		select stu_name, stu_birth from tb_student
		where stu_sex=1 order by stu_birth asc;
    
    -- 查询女同学的名字和生日按年龄从小到大排序
		select stu_name, stu_birth from tb_student
		where stu_sex=0 order by stu_birth desc;

    -- 用函数算年龄
		select stu_name, 
		stu_birth,
		floor(datediff(curdate(), stu_birth)/365) as stu_age 
        from tb_student
		where stu_sex=1 order by stu_age desc;
		
	-- 查询年龄最大的学生的出生日期(聚合函数)
		select min(stu_birth) from tb_student;

	-- 查询年龄最小的学生的出生日期(聚合函数)
		select max(stu_birth) from tb_student;
		
-- 查询编号为1111的课程考试成绩的最高分
	select max(score) from tb_record where cou_id=1111;

-- 查询学号为1001的学生考试成绩的最低分
	select min(score) from tb_record where stu_id=1001;

-- 查询课程编号为1111的课程的平均成绩(筛选和聚合函数) ---> 自动去除空值
	select avg(score) from tb_record where cou_id=1111;

-- 查询学号为1001的学生所有课程的平均分(筛选和聚合函数)
	select avg(score) from tb_record where stu_id=1001;
    
-- 查询学号为1001的学生所有课程的平均分,如果有null,null算0分
	select sum(score) / count(cou_id) from tb_record where stu_id=1001;
	-- MySQL方言
	select avg(ifnull(score,0)) from tb_record where stu_id=1001;
    
-- 查询学号为1001的学生成绩的方差(variance) / 标准差(standard deviation) ---> 看数据的离散表现
	select var_pop(score) from tb_record where stu_id=1001;
	select std(score) from tb_record where stu_id=1001;

-- 查询男女学生的人数(分组和聚合函数)
	select stu_sex, count(*) from tb_student
    group by stu_sex;
    
    select if(stu_sex,'男','女') as 性别,count(*) as 人数
	from tb_student group by stu_sex;

-- 查询每个学院男女生人数
	select col_id as 学员编号,
    if(stu_sex,'男','女') as 性别,count(*) as 人数
	from tb_student group by col_id, stu_sex;
	
-- 查询每个学生的学号和平均成绩(分组和聚合函数)
	select stu_id as 学号,round(avg(score),2) as 平均成绩
    from tb_record group by stu_id ;

-- 查询平均成绩大于等于90分的学生的学号和平均成绩 
-- 分组以前的数据筛选使用 where 子句,分组以后的数据筛选使用 having 子句
	select stu_id as 学号,round(avg(score),2) as 平均成绩
    from tb_record group by stu_id having avg(score)>=90;
    -- 查询课程号为1111,2222二门平均成绩大于等于90分的学生的学号和平均成绩
		select stu_id as 学号, round(avg(score),2) as 平均成绩
		from tb_record where cou_id in (1111,2222)
        group by stu_id having avg(score)>=90;


-- 查询年龄最大的学生的姓名(子查询) 
-- 可以先定义一个变量 ---> set @变量名=值 (select @变量名:=值)
	set @a=(select min(stu_birth) from tb_student);
	select stu_name from tb_student where stu_birth=@a;
	
-- 嵌套查询:把一个select的结果作为另一个select的一部分来使用
-- 嵌套查询通常也称之为子查询,在查询语句中有两个或多个select
select stu_name from tb_student 
	where stu_birth=(
		select min(stu_birth) from tb_student
    );
    

-- 查询年龄最大的学生姓名和年龄(子查询+运算)
	select stu_name as 姓名,floor(datediff(curdate(), stu_birth)/365) as 年龄 from tb_student 
		where stu_birth=(
			select min(stu_birth) from tb_student
        );

-- 查询选了两门以上的课程的学生姓名(子查询/分组条件/集合运算)
	select stu_name from tb_student 
    where stu_id in(
    select stu_id from tb_record group by stu_id having count(*)>2
    );


-- 查询学生的姓名,出生日期,所属学院
-- 直接查两个表,没有条件就变成了笛卡尔积
	select stu_name, stu_birth, col_name
	from tb_student, tb_college
    where tb_student.col_id=tb_college.col_id;
    
    
        
-- 内连接 inner join on
	select stu_name, stu_birth, col_name
	from tb_student inner join tb_college
	on tb_student.col_id=tb_college.col_id;

-- 自然连接 natural join,需要具有外键限制 / 表名字相同
	select stu_name, stu_birth, col_name
	from tb_student natural join tb_college;
	
-- 查询学生姓名、课程名称以及成绩(连接查询) 
	select stu_name, cou_name, score
	from tb_student, tb_course, tb_record
    where tb_student.stu_id=tb_record.stu_id and tb_course.cou_id=tb_record.cou_id
    having score is not null;
    
    select stu_name, cou_name, score
    from tb_student inner join tb_course inner join tb_record
    on tb_student.stu_id=tb_record.stu_id and tb_course.cou_id=tb_record.cou_id
    having score is not null;

-- 分页查询
-- MySQL分页方言,limit取的数量,offset跳过的数量 / limit num1(跳的数),num2(取的数)
	-- 分页之前最好先进行排序
	select stu_name,cou_name,score
	from tb_student natural join tb_record natural join tb_course
	where score is not null
	order by score desc limit 5 offset 10;

	select stu_name,cou_name,score
	from tb_student natural join tb_record natural join tb_course
	where score is not null
	order by score desc limit 10,5;
	
	
-- 查询选课学生的姓名和平均成绩(子查询和连接查询)
	select stu_name, avg_score
	from tb_student t1,
    (	select stu_id,round(avg(score),2) as avg_score
	from tb_record group by stu_id	) t2
	where t1.stu_id=t2.stu_id;
	
	select stu_name, avg_score
    from tb_student as t1 inner join
    (	select stu_id,round(avg(score),1) as avg_score
    from tb_record group by stu_id	) as t2
    on t1.stu_id=t2.stu_id;
    
-- 查询选了课的学生的姓名和选课数量
	select stu_name as 学生姓名, 选课数量
    from tb_student as t3 inner join
    (	select stu_id,count(*) as 选课数量
    from tb_record group by stu_id	) as t4
	on t3.stu_id=t4.stu_id;

-- 查询每个学生的姓名和选课数量(左外连接和子查询)

-- 内连接:查询左右两表满足连接条件的数据
-- 外连接:
	-- 左外连接:确保左表(现在join前面的表)中的所有记录都能查出来,不满足连接条件的补充null
		-- left outer join on
	-- 右外连接:确保右表(现在join后面的表)中的所有记录都能查出来,不满足连接条件的补充null
		-- right outer join on
	-- 全外连接:确保左右表中的所有记录都能查出来,不满足连接条件的补充null
		-- full outer join on (MySQL不支持)
        -- 所以使用左外连接与右外连接求并集运算(union)得到全外连接的结果
        
           select stu_name as 学生姓名, 选课数量
            from tb_student as t3 left outer join
            (	select stu_id,count(*) as 选课数量
            from tb_record group by stu_id	) as t4
            on t3.stu_id=t4.stu_id;

        -- 处理空值
            select stu_name as 学生姓名, ifnull(选课数量,0) as 选课数量
            from tb_student as t3 left outer join
            (	select stu_id,count(*) as 选课数量
            from tb_record group by stu_id	) as t4
            on t3.stu_id=t4.stu_id;	


建库建表二

第二个库是有关于工作的,查询这里面的表是很可能会在实际中遇到的,无论是TOP排名还是薪资问题都很常见。

                    -- drop database if exists hrs;
                    create database hrs default charset utf8mb4;

                    use hrs;

                    create table tb_dept
                    (
                    dno int not null comment '编号',
                    dname varchar(10) not null comment '名称',
                    dloc varchar(20) not null comment '所在地',
                    primary key (dno)
                    );

                    insert into tb_dept values 
                        (10, '会计部', '北京'),
                        (20, '研发部', '成都'),
                        (30, '销售部', '重庆'),
                        (40, '运维部', '深圳');

                    create table tb_emp
                    (
                    eno int not null comment '员工编号',
                    ename varchar(20) not null comment '员工姓名',
                    job varchar(20) not null comment '员工职位',
                    mgr int comment '主管编号',
                    sal int not null comment '员工月薪',
                    comm int comment '每月补贴',
                    dno int comment '所在部门编号',
                    primary key (eno)
                    );


                    insert into tb_emp values 
                        (7800, '曾霜', '总裁', null, 20000, 1200, 20),
                        (2056, '无一烦', '分析师', 7800, 12800, 1500, 20),
                        (3088, '骆子翔', '设计师', 2056, 10800, 800, 20),
                        (3211, '前锋', '程序员', 2056, 8200, null, 20),
                        (3233, '兆威', '程序员', 2056, 8400, null, 20),
                        (3251, '麻陨', '程序员', 2056, 8600, null, 20),
                        (5566, '中场', '会计师', 7800, 7000, 1000, 10),
                        (5234, '皇亥伯', '出纳', 5566, 3000, null, 10),
                        (3344, '方朱铭', '销售主管', 7800, 5000, 800, 30),
                        (1359, '克正冬', '销售员', 3344, 3000, 200, 30),
                        (4466, '后卫', '销售员', 3344, 3500, null, 30),
                        (3244, '武宿博', '程序员', 3088, 5200, null, 20),
                        (3577, '成雨繁', '会计', 5566, 4200, null, 10),
                        (3588, '门将', '会计', 5566, 4500, null, 10);
第二套:有关工作的SQL查询

第二套题中在起到练习作用的基础上拓展了一些知识,里面有存在性判断、MySQL8引入的窗口函数这样的解决问题的SQL语句。

-- 查询月薪最高的员工姓名和月薪
	select sal,ename from tb_emp where sal in (select max(sal) from tb_emp);
    
    select sal,ename from tb_emp where sal>=all(select sal from tb_emp);
	
	-- 存在性判断,下面提到再回来看
    select sal,ename from tb_emp as t1 
    where not exists( select 'x' from tb_emp as t2 where t2.sal>t1.sal );

-- 查询员工的姓名和年薪((月薪+补贴)*13)
	select ename,(sal+ifnull(comm,0))*13 as a_sal from tb_emp;

-- 查询有员工的部门的编号和人数
	select dno as 部门编号,count(*) as 人数 from tb_emp 
    group by dno;

-- 查询所有部门的名称和人数
	select dname as 部门名称,ifnull(total,0) as 人数 from tb_dept
    left join (select dno,count(*) as total from tb_emp group by dno) as tb_temp
    on tb_dept.dno=tb_temp.dno;
    

-- 查询月薪最高的员工(Boss除外)的姓名和月薪
	select ename,sal from tb_emp 
    where sal in (select max(sal) from tb_emp where mgr is not null);
    
    select ename,sal from tb_emp 
	where sal in (select max(sal) from tb_emp where job<>'总裁');
	
-- 查询月薪第二的员工的姓名和月薪
	select ename,ifnull(sal,null) from tb_emp 
	where sal=(select sal from tb_emp order by sal desc limit 1,1);
	
	select ename,ifnull(sal,null) from tb_emp 
	where sal=(select max(sal) from tb_emp where sal<(select max(sal) from tb_emp));
	
-- 查询月薪超过平均月薪的员工的姓名和月薪
	select ename,sal from tb_emp 
    where sal > (select avg(sal) from tb_emp);

-- 查询月薪超过其所在部门平均月薪的员工的姓名、部门编号和月薪
	select ename,t1.dno,sal from tb_emp as t1 inner join
    (select dno,avg(sal) as avg_sal from tb_emp group by dno) as t2
    on t1.dno=t2.dno and sal>avg_sal;
    
-- 查询部门中月薪最高的人姓名、月薪和所在部门名称
	select ename,sal,dname from tb_emp as t1 inner join tb_dept as t2 inner join
    (select dno,max(sal) as max_sal from tb_emp group by dno) as t3
    on t1.dno=t2.dno and t1.dno=t3.dno and sal=max_sal;

    select ename,sal,dname from tb_emp as t1, tb_dept as t2,
    (select dno,max(sal) as max_sal from tb_emp group by dno) as t3
    where t1.dno=t2.dno and t1.dno=t3.dno and sal=max_sal;

-- 查询主管的姓名和职位
	select ename,job from tb_emp as t1,
    (select mgr from tb_emp where mgr is not null group by mgr) as t2
    where t1.eno=t2.mgr;


-- in / not in 集合运算和 distinct 去重的效率比较低
-- 可以使用存在性判断 exists / not exists 代替
    select ename,job from tb_emp 
    where eno=any(select distinct mgr from tb_emp where mgr is not null);
    
    select ename,job from tb_emp 
    where eno in (select distinct mgr from tb_emp where mgr is not null);

    select ename,job from tb_emp t1 
    where exists (select 'x'from tb_emp t2 where t1.eno=t2.mgr);

 

-- MySQL8有窗口函数:row_number() / rank() / dense_rank()
-- row_number() 无论值是否相同,序号连续
-- rank() 相同值的序号相同,序号按照数走(排名跳跃)
-- dense_rank() 相同值的序号相同,序号按照排名走(排名连续)
-- 查询月薪排名4~6名的员工排名、姓名和月薪
	select ename,sal, 
    row_number() over (order by sal desc) as rn,
    rank() over (order by sal desc) as rk,
    dense_rank() over (order by sal desc) as drn
    from tb_emp;
    
    select ename,sal,rn from 
    (select ename,sal,row_number() over (order by sal desc) as rn from tb_emp)
    tb_temp where rn limit 3 offset 3;
    
     -- 使用dense_rank()就不适合使用limit offset
		select ename,sal,rk from 
		( select ename,sal,dense_rank() over (order by sal desc) as rk from tb_emp )
		tb_temp where rk between 4 and 6;

    
	-- 没有窗口函数的写法, 因为用到临时表 set @a= 就不如 select @a:= 方便了
		select row_num,ename,sal from
		( select @a:=@a+1 as row_num,ename,sal
		from tb_emp,(select @a:=0) t1 order by sal desc ) t2
		where row_num between 4 and 6;
        
        
-- 查询每个部门月薪排名前两名的员工姓名、月薪和部门编号 
    select ename,sal,dno from tb_emp t1 
    where ( select count(*) from tb_emp t2
			where t1 .dno=t2.dno and t2.sal>t1.sal )<2 order by dno asc,sal desc;

	-- 用窗口函数实现
    -- group by是分组函数,partition by是分析函数(然后像sum()等是聚合函数)
    -- partition by相比较于group by,能够在保留全部数据的基础上,只对其中某些字段做分组排序
    -- 而group by则只保留参与分组的字段和聚合函数的结果。
    
        select ename,sal,dno from
        ( select rank() over ( partition by dno order by sal desc) as rk,ename,sal,dno 
        from tb_emp ) as tb_temp where rk<=2;
        
	

建库建表三

最后一套更加贴近生活,我们每个人应该都购物过吧,无论是不是在互联网时代,消费问题都会引出大量的数据,我们通过这些数据(货单价、客单价)掌握顾客好恶;我们甚至依靠这类数据的趋势,着眼于未来,通向发家致富的路。通过SQL语句的查询获取这类有用的数据也就变的尤为重要。

                    create database homework default charset utf8mb4;

                    use homework;

                    create table tb_product
                    (
                    prod_id varchar(50) not null comment '商品号',
                    category varchar(50) not null comment '种类',
                    color varchar(10) not null comment '颜色',
                    weight decimal(10, 2) not null comment '重量',
                    price integer not null comment '价格',
                    primary key (prod_id)
                    ) engine=innodb comment '产品表';

                    insert into tb_product values 
                        ('prodA', 'cateA', 'yellow', 5.6, 100),
                        ('prodB', 'cateB', 'red', 3.7, 200),
                        ('prodC', 'cateC', 'blue', 10.3, 300),
                        ('prodD', 'cateD', 'black', 7.8, 400);

                    create table tb_order
                    (
                    id integer not null auto_increment,
                    order_no varchar(20) not null comment '订单号',
                    user_id varchar(50) not null comment '用户号',
                    order_date date not null comment '下单日期',
                    store varchar(50) not null comment '店铺号',
                    product varchar(50) not null comment '商品号',
                    quantity integer not null comment '购买数量',
                    primary key (id)
                    ) engine=innodb comment '订单表';

                    insert into tb_order 
                        (order_no, user_id, order_date, store, product, quantity) 
                    values 
                        ('D001', 'customerA', '2018-01-01', 'storeA', 'prodA', 1),
                        ('D001', 'customerA', '2018-01-01', 'storeA', 'prodB', 1),
                        ('D001', 'customerA', '2018-01-01', 'storeA', 'prodC', 1),
                        ('D002', 'customerB', '2018-01-12', 'storeB', 'prodB', 1),
                        ('D002', 'customerB', '2018-01-12', 'storeB', 'prodD', 1),
                        ('D003', 'customerC', '2018-01-12', 'storeC', 'prodB', 1),
                        ('D003', 'customerC', '2018-01-12', 'storeC', 'prodC', 1),
                        ('D003', 'customerC', '2018-01-12', 'storeC', 'prodD', 1),
                        ('D004', 'customerA', '2018-01-01', 'storeD', 'prodD', 2),
                        ('D005', 'customerB', '2018-01-23', 'storeB', 'prodA', 1);

                    create table tb_store
                    (
                    store_id varchar(50) not null comment '店铺号',
                    city varchar(20) not null comment '城市',
                    primary key (store_id)
                    ) engine=innodb comment '店铺表';

                    insert into tb_store values 
                        ('storeA', 'cityA'),
                        ('storeB', 'cityA'),
                        ('storeC', 'cityB'),
                        ('storeD', 'cityC'),
                        ('storeE', 'cityD'),
                        ('storeF', 'cityB');
第三套:升级-订单SQL查询

这边是难度升级,更复合考题的标准,是有可能会在面试中遇到的SQL语句。虽然只有三道题,但是都很经典,思路不会有太大偏差,方法应该有很多种,如果您对上面的题都拿捏了,那就试一试下面的题吧。

-- 查出购买总金额不低于800的用户的用户号、总购买金额、总订单数和总购买商品数,订单号相同的算作一单。

    select
        user_id,
        sum(price * quantity) as 总购买金额,
        count(distinct order_no) as 总订单数,
        sum(quantity) as 总购买商品数
    from (  select order_no, user_id, quantity, price 
            from tb_order inner join tb_product 
            on product=prod_id  ) as temp 
    group by user_id having 总购买金额>=800;


-- 查出所有城市(包含无购买记录的城市)的总店铺数,总购买人数和总购买金额。

    select 
        city as 城市,
        count(distinct store_id) as 总店铺数,
        count(distinct user_id) as 总购买人数,
        ifnull(sum(quantity * price), 0) as 购买总金额
    from (  select store_id, city, user_id, quantity, price
            from tb_store left join tb_order on store_id=store 
            left join tb_product on prod_id=product  ) as temp
    group by city;


-- 查出购买过"cateA"产品的用户和他们的平均订单金额,订单号相同的算作一单。

    select 
        user_id,
        sum(price * quantity) / count(distinct order_no) as 平均订单金额
    from (  select order_no, user_id, quantity, price 
            from tb_order inner join tb_product 
            on product=prod_id  ) temp 
    where user_id in (  select user_id from tb_order 
            inner join tb_product on prod_id=product where category='cateA'  ) 
    group by user_id;

感谢学习陪伴,您的点赞,评论就是我更新的动力


注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: