SQL基础指南,在FAQ中学习MySQL

本文通过一系列的MySQL查询实例,详细介绍了SQL的基础知识,包括登录、数据操作、索引原理、联接查询、条件表达式和SQL注入等,并提供了丰富的练习题和答案,帮助读者深入理解和掌握MySQL。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文通过对MySQL进行表查询来学习MySQL的基本操作。

MySQL:

(MySQL的SQL不区分大小写)
登录:

mysql -u root -p

查看数据库

show databases;

选择数据库

use table_name;

查看选定数据库的表

show tables;

创建数据库(删除将create 改为drop)

create database 数据库名;

创建表(删除:DROP TABLE table_name ;)

create table table_name (column_name column_type);

删除数据(删除特定记录)

DELETE FROM table_name [WHERE Clause]
  • 如果没有指定 WHERE 子句,MySQL 表中的所有记录将被删除。
  • 你可以在 WHERE 子句中指定任何条件

删除,添加或修改表字段(alter)
比如:

ALTER TABLE test_tbl DROP id;
ALTER TABLE test_tbl ADD id INT FIRST;
ALTER TABLE test_tbl MODIFY name CHAR(10);
--  修改表名
ALTER TABLE test_tbl RENAME TO alter_tbl;
-- 修改存储引擎:修改为myisam
alter table tableName engine=myisam;
-- 删除外键约束:keyName是外键别名
alter table tableName drop foreign key keyName;

插入数据(通用),如果数据是字符型,必须使用单引号或者双引号,如:“value”。)

insert into table_name ( field1, field2,...fieldN )
                       VALUES
                       ( value1, value2,...valueN );

查询数据( [ ] 代表可选)

SELECT column_name,column_name
FROM table_name
[WHERE Clause]
[LIMIT N][ OFFSET M]
  • 查询语句中你可以使用一个或者多个表,表之间使用逗号(,)分割,并使用WHERE语句来设定查询条件。
  • SELECT 命令可以读取一条或者多条记录。
  • 你可以使用星号(*)来代替其他字段,SELECT语句会返回表的所有字段数据
  • 你可以使用 WHERE 语句来包含任何条件。
  • 你可以使用 LIMIT 属性来设定返回的记录数。
  • 你可以通过OFFSET指定SELECT语句开始查询的数据偏移量。默认情况下偏移量为0。
  • limit N,M : 相当于 limit M offset N , 从第 N 条记录开始, 返回 M 条记录

一些写在前面的知识(

(示例基于下面的表格)

  1. 索引是如何工作的?

简单来说,索引分为 hash 和 B-Tree 两种。 hash 查找的时间复杂度为O(1)。 B-Tree 其实是
B+Tree,一种自平衡多叉搜索数,自平衡代表每次插入和删除数据都会需要动态调整树高,以降低平衡因子。B+Tree
只有叶子节点会存储信息,并且会使用链表链接起来。因此适合范围查找以及排序,不过只能搜索最左前缀,如只能索引以a开头的姓名,却无法索引以a结尾的姓名。
另外,Everything is trade off。B+Tree的自平衡特性保证能够快速查找的同时也降低了更新的性能,需要权衡利弊。

  1. 如何联接多个行的字段?

在mysql中,可以使用group_concat

select group_concat(sname) from student;
  1. 如何在一个sql语句中插入多行数据?

values 使用逗号相隔,可以插入多行数据

insert into student(sid, sname) values (), (), ()
  1. 如何在select中使用条件表达式?

示例,在student表中,查询所有人成绩,小于60则显示为0

select sid, cid, if(score < 60, 0, score) score from sc;
  1. 如何找到重复项?
select sname, ssex, count(*) times from student
group by sname, ssex
having times > 1;
  1. 什么是SQL注入?

如有一条查询语句为

"select * from (" + table + ");"

当table取值 student); drop table student; -- 时,语句变为了以下,会删掉表,造成攻击。

"select * from (student); drop table student; --);"
  1. sql执行顺序
    (1)from
    (3) join
    (2) on
    (4) where
    (5)group by(开始使用select中的别名,后面的语句中都可以使用)
    (6) avg,sum…
    (7)having
    (8) select
    (9) distinct
    (10) order by

接下来为练习题:

通过做题来学习查询技巧
数据表介绍

–1.学生表
Student(SId,Sname,Sage,Ssex)
–SId 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别

–2.课程表
Course(CId,Cname,TId)
–CId 课程编号,Cname 课程名称,TId 教师编号

–3.教师表
Teacher(TId,Tname)
–TId 教师编号,Tname 教师姓名

–4.成绩表
SC(SId,CId,score)
–SId 学生编号,CId 课程编号,score 分数

Student表
create table Student(SId varchar(10) primary key,Sname varchar(10),Sage datetime,Ssex varchar(10));
insert into Student values('01' , '赵雷' , '1990-01-01' , '男');
insert into Student values('02' , '钱电' , '1990-12-21' , '男');
insert into Student values('03' , '孙风' , '1990-12-20' , '男');
insert into Student values('04' , '李云' , '1990-12-06' , '男');
insert into Student values('05' , '周梅' , '1991-12-01' , '女');
insert into Student values('06' , '吴兰' , '1992-01-01' , '女');
insert into Student values('07' , '郑竹' , '1989-01-01' , '女');
insert into Student values('09' , '张三' , '2017-12-20' , '女');
insert into Student values('10' , '李四' , '2017-12-25' , '女');
insert into Student values('11' , '李四' , '2012-06-06' , '女');
insert into Student values('12' , '赵六' , '2013-06-13' , '女');
insert into Student values('13' , '孙七' , '2014-06-01' , '女');
课程表
create table Course(CId varchar(10),Cname nvarchar(10),TId varchar(10));
insert into Course values('01' , '语文' , '02');
insert into Course values('02' , '数学' , '01');
insert into Course values('03' , '英语' , '03');
教师表 Teacher
create table Teacher(TId varchar(10),Tname varchar(10));
insert into Teacher values('01' , '张三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');
成绩表 SC
create table SC(SId varchar(10),CId varchar(10),score decimal(18,1));
insert into SC values('01' , '01' , 80);
insert into SC values('01' , '02' , 90);
insert into SC values('01' , '03' , 99);
insert into SC values('02' , '01' , 70);
insert into SC values('02' , '02' , 60);
insert into SC values('02' , '03' , 80);
insert into SC values('03' , '01' , 80);
insert into SC values('03' , '02' , 80);
insert into SC values('03' , '03' , 80);
insert into SC values('04' , '01' , 50);
insert into SC values('04' , '02' , 30);
insert into SC values('04' , '03' , 20);
insert into SC values('05' , '01' , 76);
insert into SC values('05' , '02' , 87);
insert into SC values('06' , '01' , 31);
insert into SC values('06' , '03' , 34);
insert into SC values('07' , '02' , 89);
insert into SC values('07' , '03' , 98);

直观的看一下:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
练习题目

  1. 查询" 01 “课程比” 02 “课程成绩高的学生的信息及课程分数
    1.1 查询同时存在” 01 “课程和” 02 "课程的情况

    1.2 查询存在" 01 “课程但可能不存在” 02 "课程的情况(不存在时显示为 null )

    1.3 查询不存在" 01 “课程但存在” 02 "课程的情况

  2. 查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩

  3. 查询在 SC 表存在成绩的学生信息

  4. 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )
    4.1 查有成绩的学生信息

  5. 查询「李」姓老师的数量

  6. 查询学过「张三」老师授课的同学的信息

  7. 查询没有学全所有课程的同学的信息

  8. 查询至少有一门课与学号为" 01 "的同学所学相同的同学的信息

  9. 查询和" 01 "号的同学学习的课程 完全相同的其他同学的信息

  10. 查询没学过"张三"老师讲授的任一门课程的学生姓名

  11. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩

  12. 检索" 01 "课程分数小于 60,按分数降序排列的学生信息

  13. 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩

  14. 查询各科成绩最高分、最低分和平均分:

以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率

(及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90)
要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列

  1. 按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺
    15.1 按各科成绩进行排序,并显示排名, Score 重复时合并名次

  2. 查询学生的总成绩,并进行排名,总分重复时保留名次空缺
    16.1查询学生的总成绩,并进行排名,总分重复时不保留名次空缺

  3. 统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[60-0] 及所占百分比

  4. 查询各科成绩前三名的记录

  5. 查询每门课程被选修的学生数

  6. 查询出只选修两门课程的学生学号和姓名

  7. 查询男生、女生人数

  8. 查询名字中含有「风」字的学生信息

  9. 查询同名同性学生名单,并统计同名人数

  10. 查询 1990 年出生的学生名单

  11. 查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列

  12. 查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩

  13. 查询课程名称为「数学」,且分数低于 60 的学生姓名和分数

  14. 查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)

  15. 查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数

  16. 查询不及格的课程

  17. 查询课程编号为 01 且课程成绩在 80 分以上的学生的学号和姓名

  18. 求每门课程的学生人数

  19. 成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩

  20. 成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩

  21. 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩

  22. 查询每门功成绩最好的前两名

  23. 统计每门课程的学生选修人数(超过 5 人的课程才统计)。

  24. 检索至少选修两门课程的学生学号

  25. 查询选修了全部课程的学生信息

  26. 查询各学生的年龄,只按年份来算

  27. 按照出生日期来算,当前月日 < 出生年月的月日则,年龄减一

  28. 查询本周过生日的学生

  29. 查询下周过生日的学生

  30. 查询本月过生日的学生

  31. 查询下月过生日的学生

答案

(个人做法,如有不当之处欢迎指正,如有不同意见欢迎交流)

  1. 查询" 01 “课程比” 02 "课程成绩高的学生的信息及课程分数
select a.*,b.score1,b.score2
from 
(select s1.SId,score1,score2
from 
(select SId,score as score1 from sc where sc.CId='01' ) as s1,
(select SId,score as score2 from sc where sc.CId='02' ) as s2
where s1.score1>s2.score2 and s1.SId=S2.SId) b inner join student a
on b.SId=a.Sid;

将内连接改一下跟以下用笛卡儿积的语句是等价的:(但是考虑到开销。当两张表的数据量比较大,又需要连接查询时,应该使用 FROM table1 JOIN table2 ON xxx的语法,避免使用 FROM table1,table2 WHERE xxx 的语法,因为后者会在内存中先生成一张数据量比较大的笛卡尔积表,增加了内存的开销。)

select a.*,b.score1,b.score2 
from 
(select s1.SId,score1,score2
from 
(select SId,score as score1 from sc where sc.CId='01' ) as s1,
(select SId,score as score2 from sc where sc.CId='02' ) as s2
where s1.score1>s2.score2 and s1.SId=S2.SId) b , student a
where b.SId=a.Sid;

在这里插入图片描述
SQL INNER JOIN 从多个表中返回满足 JOIN 条件的所有行,内连接示意图:
在这里插入图片描述
1.1 查询成绩表中同时存在" 01 “课程和” 02 "课程的情况

select s1.*,s2.score2
from
(select SId,score as score1 from sc where sc.CId='01' ) as s1,
(select SId,score as score2 from sc where sc.CId='02' ) as s2
where s1.sid=s2.sid;

在这里插入图片描述
1.2 查询存在" 01 “课程但可能不存在” 02 "课程的情况(不存在时显示为 null )

可能,就是说02课程有没有都需要查出来,这很自然得就可以想到左连接(或右连接)

select s1.*,s2.score2
from
(select SId,score as score1 from sc where sc.CId='01' ) as s1
left join
(select SId,score as score2 from sc where sc.CId='02' ) as s2
on s1.sid=s2.sid;

在这里插入图片描述
LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行,左连接示意图:
在这里插入图片描述
1.3 查询不存在" 01 “课程但存在” 02 "课程的情况

思路:相同学号下,用’02’课程的成绩表➖(减去)存在‘01’课程的表,MySQL中求差集可以采用not in和左连接,由于not in的效率不如连接查询,在表很大的时候推荐采用左连接

select * 
from sc
where sc.sid not in(
				select sid 
				from sc s1
				where s1.cid='01')
	 and sc.cid='02';

(更推荐以下这种方式)

select s1.*
from (select * from sc where sc.CId='02' )as s1
left join
(select SId from sc where sc.CId='01' ) as s2
on s1.sid=s2.sid
where s2.sid is NULL;

在这里插入图片描述

  1. 查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩

AVG() 函数
AVG() 函数返回数值列的平均值。
SQL AVG() 语法

SELECT AVG(column_name) FROM table_name

GROUP BY 语句
GROUP BY 语句用于结合聚合函数,根据一个或多个列对结果集进行分组。

select sid,avg(score) as avg_score
from sc
group by sid;

HAVING 子句
在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与聚合函数一起使用。

HAVING 子句可以让我们筛选分组后的各组数据。

以下查询可得到平均成绩>60的学生id和平均成绩

select sid,avg(score) as avg_score
from sc
group by sid
having avg(score)>60;

知道以上用法后剩下的就很简单,用上表与student表连接即可

select stu.sid,stu.sname,sc_t.avg_sc
from student stu
inner join
(select sid,avg(score) as avg_sc
from sc
group by sid
having avg(score)>60) as sc_t
on stu.sid=sc_t.sid;

在这里插入图片描述

  1. 查询在 SC 表存在成绩的学生信息
    这题比较简单,筛选出在sc有信息的学生就行,不过最后结果要进行去重
select distinct stu.*
from student stu, sc
where stu.sid=sc.sid;

在这里插入图片描述
4. 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )

跟上面的题大差不差,先从sc表中用count,sum选出需要的信息,再与学生表左连接

select a.sid,a.sname,b.c_num as course_num,b.t_s as total_score
from student a
left join (select sid,count(cid) as c_num,sum(score) as t_s
from sc
group by sid) b
on a.sid=b.sid;

在这里插入图片描述
这里可以讲一下对NULL值的处理,比如这里的课程总数不想显示为NULL想改成0(MySQL可以采用ifnull关键字):

select a.sid,a.sname,ifnull(b.c_num,0) as course_num,b.t_s as total_score
from student a
left join (select sid,count(cid) as c_num,sum(score) as t_s
from sc
group by sid) b
on a.sid=b.sid;

在这里插入图片描述
4.1 查有成绩的学生信息
EXISTS 运算符
EXISTS 运算符用于判断查询子句是否有记录,如果有一条或多条记录存在返回 True,否则返回 False。

select * from A where id in (select id from B);
select * from A where exists (select 1 from B where A.id=B.id);
in是在内存里遍历比较,而exists需要查询数据库,所以当B表数据量较大时,exists效率优于in。

select *
from student
where exists(select sid from sc where student.sid=sc.sid);
select *
from student
where sid in(select sid from sc where student.sid=sc.sid);

在这里插入图片描述
5. 查询「李」姓老师的数量

SQL LIKE 操作符
LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式。
在 SQL 中,通配符与 SQL LIKE 操作符一起使用。
SQL 通配符用于搜索表中的数据。
在 SQL 中,可使用以下通配符:
% 替代 0 个或多个字符
_ 替代一个字符

select count(*) 
from teacher
where Tname like '李%';

在这里插入图片描述
6. 查询学过「张三」老师授课的同学的信息

先查张三的授课的课程号,再与成绩表筛选,再和学生表连接

select * from student where sid in(
select sid from sc where cid in 
(select tid from teacher where tname='张三')
);

在这里插入图片描述
7. 查询没有学全所有课程的同学的信息

没有学全直接选择比较困难,我们可以选择学全的然后求差集。从课程表选出所有课程号,与成绩表匹配学了全部课的,最后与学生表求差集

由于IN操作符需要进行确切地比较,而EXISTS只需要验证存不存在,所以使用IN将会比使用EXISTS花费更多的成本,因此能使用EXISTS替代IN的地方,应该尽量使用EXISTS。另外,尽量使用NOT EXISTS替代NOT IN,使用EXISTS替代DISTINCT。

select * from student a
left join
(select sid as sid from sc
group by sid
having count(cid)=(select count(cid) from course)
) b
on a.sid=b.sid
where b.sid is NULL;

在这里插入图片描述
8. 查询至少有一门课与学号为" 01 "的同学所学相同的同学的信息

选出‘01’同所学的课,题目所求的是至少一门,那么这个人只要存在一门课在‘01‘同学选的课中即可。

select * from student where sid in(
select distinct sid from sc 
where cid in
(select cid from sc where sid='01'))
and sid!='01';

在这里插入图片描述
9. 查询和" 01 "号的同学学习的课程完全相同的其他同学的信息

思路:选择’01’同学选的课,与sc表匹配, 不过完全相同有难度,因为我们不会逐个去比较是否相同,我们先看一个函数的用法:

concat(str1,str2,...) #作用是将多个字符串合并为一个字符串

比如:当我们直接选择时是这样:
在这里插入图片描述在这里插入图片描述
加上concat函数,用’,'连接后是这样:(当然你可以自定义连接符)
在这里插入图片描述
2、那如果需要将单个字段直接合并呢(比如直接将cname课程名变为一行字符串)?
可以使用:GROUP_CONCAT() 函数
GROUP_CONCAT函数返回一个字符串结果,该结果由分组中的值连接组合而成。
在这里插入图片描述
所以我们可以用一个返回值得到‘01’同学选的课:
在这里插入图片描述

select sid,group_concat(cid order by cid desc) from sc group by sid;

这样我们就可以将各个同学的选课聚合成单个值进行比较了(由于实际上课程不一定有序排列,所以应该加上order by子句,这里为了明显一点看出来用了降序排列(desc)):
在这里插入图片描述
参考答案:

select * from student where sid in
(select sid from sc where sid!='01' 
group by sid 
having group_concat(cid order by cid) = 
			(select group_concat(cid order by cid) from SC where sid = '01'));

在这里插入图片描述
10. 查询没学过"张三"老师讲授的任一门课程的学生姓名

思路:可以反过来思考,选出‘张三’所教课程,选出学过‘张三’所教的任意一门课的同学,最后对学生求差集

select a.sname from student a left join
	(select distinct sid from sc where cid in
		(select cid from course where tid=
			(select tid from teacher where tname='张三'
			))) b
			on a.sid=b.sid
where b.sid is null;

在这里插入图片描述
11. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩

思路:先选出存在不及格的课程的同学,再选出≥2的

select a.sid,a.sname,b.avg from student a
inner join(
	select sid,avg(score) as avg from sc 
	where sid in
	(select sid from sc where score<60 group by sid having count(*)>=2)
	group by sid) b 
on a.sid=b.sid;

在这里插入图片描述
12. 检索" 01 "课程分数小于 60,按分数降序排列的学生信息

ORDER BY 关键字默认按照升序对记录进行排序。如果需要按照降序对记录进行排序,您可以使用 DESC 关键字。

SQL ORDER BY 语法

SELECT column_name,column_name
FROM table_name
ORDER BY column_name,column_name ASC|DESC;

desc 或者 asc 只对它紧跟着的第一个列名有效,其他不受影响,仍然是默认的升序。

这题直接用where筛选就行

select a.*,b.score from student a
inner join
	(select sid,score from sc where cid='01' and score<60) as b
on a.sid=b.sid
order by b.score desc;

与下面语句是等价的,但我们应尽量避免使用笛卡儿积

select a.*,b.score 
from student a,
(select sid,score from sc where cid='01' and score<60) as b
where a.sid=b.sid
order by b.score desc;

在这里插入图片描述
13. 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩

select sc.*,b.avg from sc 
left join(
	select sid,avg(score) as avg from sc group by sid) b
on sc.sid=b.sid
order by b.avg desc;

在这里插入图片描述
14. 查询各科成绩最高分、最低分和平均分:
以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
(及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90)

Case具有两种格式。简单Case函数和Case搜索函数。
–简单Case函数
CASE sex
WHEN ‘1’ THEN ‘男’
WHEN ‘2’ THEN ‘女’
ELSE ‘其他’ END
–Case搜索函数
CASE WHEN sex = ‘1’ THEN ‘男’
WHEN sex = ‘2’ THEN ‘女’
ELSE ‘其他’ END

select b.*,a.highest,lowest,average,pass/num,midden/num,good/num,excellent/num 
from	
(select cid,max(score) as highest,min(score) lowest,avg(score) average, count(*) num,
				sum(case when score>=60 then
						1 else 0 end) as pass ,	-- 及格
				sum(case when score<80 and score>=70 then
						1 else 0 end) as midden , -- 中等
				sum(case when score<90 and score>=80 then
						1 else 0 end) as good , -- 良好					
				sum(case when score>=90 then
						1 else 0 end) as excellent  -- 优秀			
from sc
GROUP BY cid ) a
inner join
(select cid,cname from course) b
on a.cid=b.cid;

在这里插入图片描述

14.1 要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列

select cid,count(*) as num from sc group by cid order by num desc,cid;
  1. 按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺

(一)先看一个用法:比如说对‘01‘课程排名,直接1234排下来(同分按学号):

set @rownum :=0;
select *,(@rownum := @rownum +1) as rownum
from sc 
where cid='01'
order by score desc ,sid;

与下面语句是等价的:

select *,(@rownum := @rownum +1) as rownum
from (select @rownum := 0) t,sc 
where cid='01'
order by score desc ,sid;

在这里插入图片描述
(二)接下来修改,同分的我们选择并列名次,比如1,1,2,3这样排列,那么增加一个变量用于保存上一个人的分数,如果相同则同名次,否则名次加1

set @rownum :=0;
set @prescore := null;
select *, 
case @prescore when
			score then @rownum
			else (@rownum := @rownum +1)  end as rownum,
			@prescore :=score
from sc 
where cid='01'
order by score desc ,sid;

在这里插入图片描述
(三)同样的要空出名次的话,先像(一)那样记录全部的顺序排名,再利用(二)的思路即可。对于不同科目要从头计算排名,那么再增加一个变量,如果课程号和上一名的相同则继续增加名次,否则遇到一个新课程就重新从1开始计算名次

题目要求也就是说比如分数80,80,76的排名应该是1,1,3

select cid,sid,score,`rank`
from 
(
select sc.*,
@currank := if(@precid = cid,if(@prescore =score,@currank,@incrank),@incrank :=1) as `rank`,
@incrank := @incrank+1,
@prescore := score,
@precid := cid
from sc,
	(select @currank :=0,@prescore :=null,@incrank :=1,@precid = null) t
order by cid,score desc ) as tb2

在这里插入图片描述

15.1 按各科成绩进行排序,并显示排名, Score 重复时合并名次

也就是说比如分数80,80,76的排名应该是1,1,2

这里介绍另一种思路:

select sname,s.* from student
inner join
(
select s1.*,(
		select count(distinct score) from sc s2
		where s2.score >= s1.score
		and s2.cid=s1.cid
		) as 'rank'
		from sc s1
		) s
		on student.SId=s.sid
		order by cid,`rank`;

在这里插入图片描述
或者利用用户变量(根据上一题的思路修改一下):

set @rownum := 0;
set @prerank := null;
set @precid := null;
select sid,
	if(@precid = cid,if(@prerank=score,@rownum,@rownum := @rownum+1),@rownum :=1) as `rank`,
	@prerank :=score as score,
	@precid :=cid as cid 
from sc
order by cid,score desc

在这里插入图片描述
15.2 查询各科排名第二的成绩

select t.* from 
(
select s1.*,(
		select count(distinct score) from sc s2
		where s2.score >= s1.score
		and s2.cid=s1.cid
		) as 'rank'
from sc s1) t
where t.`rank`=2	

在这里插入图片描述

  1. 查询学生的总成绩,并进行排名,总分重复时保留名次空缺

sql语句中,if(A,B,C)表示,如果A条件成立,那么执行B,否则执行C,如:
@abc := if(2>1,100,200)的结果是,abc的值为100。

set @rownum:=0;
set @prerank:=null;
select *,
	if(@prerank=total,@rownum,@rownum := @rownum+1) as `rank`,
	@prerank :=total
from (select sid,sum(score) as total from sc
group by sid
order by total desc
) s1
order by total desc

在这里插入图片描述

16.1查询学生的总成绩,并进行排名,总分重复时不保留名次空缺
select s1.*,(@rownum := @rownum+1) as rownum
from (select @rownum :=0) r,
(select sid,sum(score) as total from sc
group by sid
order by total desc
) s1

在这里插入图片描述

  1. 统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[60-0] 及所占百分比

14题改改百分数显示就行——concat ( left (数值1 / 数值2 *100,5),’%’)

select b.*,concat(left(midden/num *100,5),'%') as `[70-60]`,
	concat(left(good/num *100,5),'%') as `[85-70]`,
	concat(left(excellent/num *100,5),'%') as `[100-85]`,
	concat(left (fail/num *100,5),'%') as failed
from	
(select cid,max(score) as highest, count(*) num,
				sum(case when score<60 then
						1 else 0 end) as fail ,	-- 不及格
				sum(case when score<70 and score>=60 then
						1 else 0 end) as midden , -- 中等
				sum(case when score<85 and score>=70 then
						1 else 0 end) as good , -- 良好					
				sum(case when score>=85 then
						1 else 0 end) as excellent  -- 优秀			
from sc
GROUP BY cid ) a
inner join
(select cid,cname from course) b
on a.cid=b.cid;

在这里插入图片描述

  1. 查询各科成绩前三名的记录
select * from sc s1 where
    (select count(score) from sc s2
    where s2.cid=s1.cid
    and s2.score>=s1.score) <= 3
    order by s1.cid,score desc;

在这里插入图片描述
19. 查询每门课程被选修的学生数

select cid,count(cid) as num from sc group by cid;

在这里插入图片描述

  1. 查询出只选修两门课程的学生学号和姓名
select a.sid,sname from student a 
inner join
(select sid from sc group by sid having count(cid)=2) b
on a.sid=b.sid;

在这里插入图片描述
21. 查询男生、女生人数

select ssex,count(*) as num 
from student 
group by ssex;

在这里插入图片描述
22. 查询名字中含有「风」字的学生信息

select * 
from student 
where sname like'%风%';

在这里插入图片描述
23. 查询同名同性学生名单,并统计同名人数

select sname,count(*) as num 
from student
group by sname 
having count(*)>1;

在这里插入图片描述
24. 查询 1990 年出生的学生名单

MySQL 日期时间 Extract(选取) 函数。
这里看一下例子:
在这里插入图片描述

select * 
from student 
where year(sage)='1990';

在这里插入图片描述
25. 查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列

select cid,avg(score) as avg 
from sc 
group by cid 
order by avg(score) desc,cid;

在这里插入图片描述
26. 查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩

select a.sid,sname,b.avg from student a
inner join
(select sid,avg(score) as avg from sc group by sid having avg(score)>=85) b
on a.sid=b.sid;

在这里插入图片描述
32. 查询课程名称为「数学」,且分数低于 60 的学生姓名和分数

select sname,score from student a
inner join
(select sid,score from sc where cid=
	(select cid from course where cname='数学') and score<60) b
on a.sid=b.sid;

在这里插入图片描述
33. 查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)

select a.sid,b.cid,b.score from student a
left join sc b
on a.sid=b.sid;
  1. 查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数
select student.sname, course.cname,sc.score from student,course,sc
where sc.score>70
and student.sid = sc.sid
and sc.cid = course.cid;
  1. 查询存在不及格的课程
select a.cid,cname from course a
inner join
(select distinct cid from sc where score<60) b
on a.cid=b.cid;

或者

select cid from sc where score <60 group by cid;

在这里插入图片描述
36. 查询课程编号为 01 且课程成绩在 80 分以上的学生的学号和姓名

select a.sid,sname from student a inner join
	(select sid from sc where cid='01' and score>=80) b
on a.sid=b.sid;

在这里插入图片描述
37. 求每门课程的学生人数

直接对课程分组,count

select cname,b.num from course a
inner join
(select cid,count(*) as num from sc group by cid) b
on a.cid=b.cid;

在这里插入图片描述
38. 成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩

select * from student a inner join
(select sid,score from sc where cid=
(select cid from course where tid=
(select tid from teacher where tname='张三'))
order by score desc 
limit 1) b
on a.sid=b.sid;

在这里插入图片描述
下面的是我一开学写错后得出的结果,,,,:虽然我也不知道为什么会有结果^^
在这里插入图片描述
39. 成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩

因为李四所教的‘01’课程才有两个重复的最高分(01和03都是80分),所以我们用李四来测试。看起来长了一点,逻辑还是很好理解的:选出老师的tid,根据Tid选出cid,知道cid后选出成绩=该课程的最高分的学生sid,与学生表连接。

select a.*,cid,score from student a inner join
( 
select sid,cid,score from sc where cid=
((select cid from course where tid=
(select tid from teacher where tname='李四')))
and score=
(select max(score) from sc where cid=
((select cid from course where tid=
(select tid from teacher where tname='李四')))) 
) b
on a.sid=b.sid;

在这里插入图片描述
40. 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩

  1. 查询每门课程成绩最好的前两名
select a.sid,a.cid,a.score 
from sc a 
left join sc b 
on a.cid = b.cid and a.score<b.score
group by a.cid, a.sid
having count(b.cid)<2
order by a.cid;
  1. 统计每门课程的学生选修人数(超过 5 人的课程才统计)。
    在这里插入图片描述
  2. 检索至少选修两门课程的学生学号
select sid from student where sid in
	(select sid from sc group by sid having count(cid)>=2);
  1. 查询选修了全部课程的学生信息
select * from student where sid in
	(select sid from sc group by sid having count(cid)=
		(select distinct count(*) from course)
	);

在这里插入图片描述
45. 查询各学生的年龄,只按年份来算

MySQL中查询当前日期:
在这里插入图片描述

select sid,sname,year(now())-year(sage) 
from student;

在这里插入图片描述
46. 按照出生日期来算,当前月日 < 出生年月的月日,则年龄减一

MySQL 时间戳(timestamp)转换、增、减函数:
计算相差的天数
在这里插入图片描述

在这里插入图片描述

select sid,sname,timestampdiff(year,sage,now()) 
from student;

在这里插入图片描述
47. 查询本周过生日的学生

MySQL中查询日期是相对位置:
在这里插入图片描述

select * 
from student 
where weekofyear(sage)=weekofyear(now());
  1. 查询下周过生日的学生
select * 
from student 
where weekofyear(sage)=weekofyear(now())+1;
  1. 查询本月过生日的学生
select * 
from student 
where month(sage)=month(now())+1;
  1. 查询下月过生日的学生
select * 
from student 
where month(sage)=month(now())+1;

待更
参考
13. MySQL 教程
14. 50道SQL练习题及答案与详细分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值