转载于:https://zhuanlan.zhihu.com/p/113005033
仅用于学习,如有侵权,请联系我。
什么是数据库?什么是是sql?
数据库database:用于存放数据的容器(一个文件或者一组文件)。区别于数据库管理软件(DBMS-database management system)。
SQL:数据库查询语言,Structured Query Language的简称。SQL语言是用于操纵数据库软件的语言。
(为什么要使用数据库?数据库比excel有更多的优点:数据库可以存放比excel更多的数据,允许很多人同时使用数据。excel好比是移动硬盘,数据库好比是网盘很多人可以同时访问里面的数据。)
关系数据库:多张表+各表之间的关系
需要掌握:多张表里每一张表的结构是什么样?各表之间的关系是什么样的?
表的结构:表名;列;行
列名:知道这一列记录的是什么数据。每一行记录的是数据。每一行表示记录学生的信息。
列名学号是用来唯一标识一个学生的。为什么要用学号标识数据的唯一性?姓名无法唯一标识,可能有重名。
主键:表中用来表示数据唯一性的列,叫主键。是关系数据库的重要概念。
生活中,我们用主键来确定数据的唯一性,典型的例子:身份证证号。
各表之间的关系:关系数据库是由多张表组成的。
联结join:学生表和成绩表之间通过学号关联起来。关系就是数据能够对应的匹配。在关系数据库中,这种数据集的关系叫做“联结join”。(核心概念,在多表查询中经常用到。)
关系数据库管理系统:
关系数据库原理:设计草图;
实现数据库原理的建筑人员:数据库管理系统,用来管理数据库的计算机软件。mysql,oracle,sql service.
操作数据库的工具:sql,为了操作数据库而开发的语言。对数据库里的表进行操作,修改查找等。(sql是操作数据库的一种工具。)
把数据库比作一碗米饭,里面放的米就是数据,现在我们要吃碗里的米饭,怎么取出米饭,我们用筷子操作取出米饭。
mysql数据库(存放数据)
客户端navicat(用来操作数据库的软件)只要安装了客户端,都可以连接到数据库。
安装文档
如何用客户端navicat连接到mysql数据库?
步骤:点击连接;填写数据库连接信息;连接测试;确定。
安装好客户端后,在左上角有一个“链接”的按钮,点击弹出"连接属性“的客户端。
localhost,说明数据库在自己的电脑上。端口:默认端口。
创建数据库:
鼠标左键da;新建数据库;设置数据库信息(数据库名:school;字符集:utf8(可以同时支持英文和中文。如果这里创建不对,后面会出现乱码的情况);排序规则)
school是灰色的,说明没用打开数据库。右键:打开数据库;school图标变亮,说明打开。
表的创建:
选中数据库,表放在哪个数据库下。选中表;右键:新建表;输入列名(学号),类型(varchar),长度(255),不是null(✔)。
点击”添加栏位“,可以给表增加新的列,增加新的列;
添加”主键(黄色的 标记)
保存:输入表名(student),点击“确定”。
保存后的student的表,在school下找到,然后双击表名,打开表。
数据类型:
数据库的数据类型:3种。数据大小:字节。字节是计算机存储单位。表示数据大小。
字符串:类型:char;varchar;text;tinytext;mediumtext;longtext;blob;tinyblob;mediumblob;longblob。
数字:类型: int/integer;tinyint;smallint;mediumint;bigint;float;double;decimal。
日期:类型:date;datetime;timestamp;time;year。
例子:student表:长度(默认值:255)。设定了数值类型后,不能存放和所设置数值类型不一致的数据。
约束:是除了数据类型以外,对表中数据条件进行约束的一种功能。(根据业务需求设置:null:对应列中的数据可以包含空置。not null:对应列中的数据不可以包含空值(必须填)。primary key:主键(表示那一列的数值不能添加重复值。联合主键:多个列组合起来作为主键。)
表的更新:
选中表名student;设计表;添加栏位;删除栏位。
选中表名,删除表。
注意:删除表后很难恢复。
数据的插入:
选中表student;打开表;通过“增加行”,“删除行”添加和删除数据。然后保存。
也可以使用sql语句批量插入数据。
sql插入数据语句:
insert into <表名>(<列名1>,<列名2>,......)values(<值1>,<值2>,......);
蓝色的字是sql的关键字。
列名不能加单引号,值加单引号表示一个字符串。
在客户端navicat里哪里写sql呢?查询-新建查询-查询编辑器。在界面空白的地方写sql语句。这个界面叫查询编辑器,不是只能写查询语句,可以写任何的sql语句。
第一步写sql语句,第二步点击运行,第三步看运行结果。
结果信息:err报错。duplicate entry .....for key primary.(插入的值中有和现有数据重复的值,触发主键的约束条件(学号设置为主键。)解决办法:打开表,将重复值删除,然后运行结果。
空值(null);空字符串(' ')
SQL的基本语法:
DDL(数据定义语言):操作对象:数据库和表。create;drop;alter
DML(数据操纵语言):操作对象:表的数据。insert;delete;select;update
DCL(数据控制语言):用来取消或者控制数据的变更。对关系数据库的用户是否有权限操纵数据进行设置。commit;rollback;crant
SQL书写规则:
1.SQL语句逐条执行,以英文分号;结尾。
2.SQL语句不区分关键字的大小写。(关键字是数据库设定好的有特别意义的单词,在客户端写的SQL语句,关键字是蓝色标注的,比如insert into values,大小写都可以。表名和列名也不区分大小写;但是插入表中的值是区分大小写的,比如A和a。
3.输入符号的时候只能使用英文的。
写sql语句遇到错误,第一步先查有没有把符号写成中文的。列名不能加单引号。
select语句:
selec<列名1>,<列名2>,......from<表名>;
select子句:希望从表中查出列的名称(查询结果的列名);from子句:从哪个表查询。
整个查询语句的含义是:从表中查询出需要列名的数据。
查询结果中列的顺序和查询语句中select后的顺序是一样的。
查询多个列的时候,需要用英文逗号分开。
如果想查找出所有的列,用*表示。select * from student
运行原理:通过sql语句向数据库查询数据,通过客户端navicat展示查询内容。
为列设置别名as:
select 姓名 as s_name, 性别as '人类性别' from student
使用别名的目的:使语句查询结果更容易理解。英文转化为中文。
删除重复数据distinct:
在select语句中使用distinct
select distinct 姓名from student;
select distinct 学号,姓名 from student;将多个列的数据进行组合,这几个列的数据都相同的时候才算做重复数据。
distinct只能放在第一个查询的列名之前。
SQL书写规则:
sql语句逐条执行,运行完一条sql语句,再运行下一条语句。一条sql语句以;结尾。
SQL语句不缺分关键字的大小写。
列名不能加单引号,列名命名的时候不能有空格。(姓名✔;姓 名×)
符号只能使用英文符号。
where子句:限定条件的查询语句
查出姓名为“猴子”的学生的学号:select 姓名,学号 from student where 姓名='猴子';
where 姓名='猴子'指定条件。(注意子句中的中文值用英文单引号。但是列名不能用单引号。
SQL运行顺序:
1.运行from子句,从哪个表里查询;2.运行where子句,查询的条件是什么?查询列名为什么条件的行;3.运行select子句,从where子句查询的行中取出select语句指定的列。
书写顺序和运行顺序不一样。
注释方法:单行注释;多行注释。
(团队协作中方便其他人和历史记录方便查看。)
单行注释:
- - 查询出全部列
select * from student;
多行注释:
/*
查询姓名是猴子学生的学号
*/
select 姓名,学号
from student
where 姓名='猴子';
sql语句注意事项:符号的使用,英文符号。
算数运算符;比较运算符;逻辑运算符
算数运算符:+-*/;含义:加法运算;减法运算;乘法运算;除法运算。
5+Null=Null Null是空值,所有含有空值的计算,结果还是空值。
()内的表达式优先进行。(1+2)*3
例子:select 学号,成绩,成绩/100 as '成绩百分比' from score;
比较运算符:
=相等;<>不等于;>大于; >=大于等于; <小于;<=小于等于
select 姓名,学号 from student where 姓名= '猴子';
select 学号,成绩 from score where 成绩<60;
select 姓名,出生日期 from student where 出生日期< '1990-01-01';
字符串比较规则:
字符串的顺序是按照字典顺序。
10是以1开头的数字,因此10比2小。
'10' < '2'
如何查询出null空值的行?
is null/is not null
查询出教师姓名是空值的行?查询出教师姓名不是空值的行?
select 教师号,教师姓名 from teacher where 教师姓名 is null;
select 教师号,教师姓名 from teacher where 教师姓名 is not null;
逻辑运算符:可以将多个查询条件组合起来
not否定某一条件;
and并且;
between范围查找;
or或者;
in:or的简单写法。
案例:
not:
select 学号,成绩 from score where not 成绩 >= 60;和select 学号,成绩 from score where 成绩 < 60;一般不适用not语句。
and: 并且
业务场景:同时使用多个查询条件来查询所需要的数据。
文氏图。
select 学号,成绩 from score where 成绩>=60 and 成绩<=90;
select 姓名,性别 from student where 姓名= '男' and (姓名='猴子' or 姓名=‘马云');
()内先运行。
between:范围查询
select 学号,成绩 from score where 成绩 between 60 and 90;
select 学号,成绩 from score where 成绩 >=60 and 成绩 <=90;
or:或者。文氏图
两个条件中满足任何一个。
select 学号,成绩 from score where 成绩 < 60 or 成绩 > 90;
in:或者
是or的简便写法。
select 姓名,性别 from student where 姓名 in ('猴子','马云');
select 姓名,性别 from student where 姓名 not in ('猴子','马云');
like:字符串模糊查询。
%表示任意字符串。
猴%:
--查询姓“猴”的学生名单
/*
查询姓“猴”的学生名单
*/
select *
from student
where 姓名 like '猴%';
%猴:
--查询姓名中最后一个字是“猴”的学生名单
/*
查询姓名中最后一个字是“猴”的学生名单
*/
select *
from student
where 姓名 like '%猴';
%猴%:
--查询姓名中带“猴”字的学生名单
/*
查询姓名中带“猴”字的学生名单
*/
select *
from student
where 姓名 like '%猴%';
_1个下划线表示任意一个字符。
/*
查询姓”王“的学生名单,并且姓名是3个字的
*/
select *
from student
where 姓名 like '王__'
SQL运行顺序:
select子句最后运行,其他子句按书写顺序运行。
select查询结果
from从哪张表中查询数据
where查询条件(运算符,字符串模糊查询);
汇总函数:5个函数
count求某列的行数:count(*)包含空值。可以对任何数据类型进行计算。
sum对某列数据求和:只能对数值类型的列计算
avg求某列数据的平均值:只能对数值类型的列计算
max求某列数据的最大值
min求某列数据的最小值
函数:
三个功能:1.功能;2.输入(参数);3.输出(返回值)。
如何将函数写在SQL语句中:
count:
select count(教师姓名) from teacher;
count行数如果计算的是列名,会除去空值null计算。
select count(*) from teacher;
如果count行数计算的是空值,会计算所有的行数,包括空值的部分。
sum:
select sum(成绩) from score;
select max(成绩), min(成绩) from score;
select count (distinct 姓名) from student;如果不想计算重复值,在count前面加上distinct。
在列名前面加上distinct,去除重复值计算。
SQL分组:group by
在SQL中,用group by子句按某个条件进行分组。
经常和汇总函数进行使用,数据分析通用模板方法。分析方法:数据分组;应用函数;组合结果。
select 性别,count(*)
from student
where 出生日期 > '1990-01-01'
group by 性别;
SQL运行顺序:
1.from student; 通过from子句指定从哪张表中查找数据;
2.where 出生日期 > '1990-01-01';运行where子句,通过这个子句查询出符合条件的行;3.group by 性别;对第二步查出的数据进行分组;
4.select 性别,count(*);对分组后的每个组运用函数,并组合结果。并筛选出select中指定的列。
如何识别问题要分组:每次/每个/每门?
having: 为分组结果指定条件;
where子句只能指定行的条件;having子句可以指定分组以后的条件,比如在分出男生组和女生组的数据后,只需要男生组的数据。
select 性别,count(*)
from student
group by 性别
having count(*)>1;
SQL运行顺序:
select 查询结果 5
from 从哪张表中查找数据 1
where查询条件 2
group by分组 3
having对分组结果指定条件; 4
业务问题解决套路
1.翻译成大白话;2.写出分析思路;3.写出对应的SQL子句。
如何计算各科的平均成绩?
如何计算每门课程的平均成绩?
select 课程号, avg(成绩)
from score
group by 课程号;
如何计算每门课程的平均成绩,并且平均成绩大于等于80分?
如何计算每门课程(分组)的平均成绩(汇总),并且平均成绩大于等于80分(条件)?
select 课程号, avg(成绩)
from score
group by 课程号
having avg(成绩) >= 80;
对查询结果排序:order by
升序/降序/指定多个排序列名/空值如何排序
降序(desc):从大到小;单词descend的缩写。
升序(asc):从小到大;单词ascend的缩写。
如何计算每门课程的平均成绩,并且平均成绩大于等于80分?
select 课程号, avg(成绩) as 平均成绩
from score
group by 课程号
having avg(成绩) >= 80
order by 平均成绩 desc;
运行顺序:order by是查询结果排序,因为查询结果是在select子句后得到的,所以order by是在select子句后运行的。
select 课程号, avg(成绩) as 平均成绩 4
from score 1
group by 课程号 2
having avg(成绩) >= 80 3
order by 平均成绩 desc; 5
指定多个排序列名:
select * 2
from score 1
order by 成绩 asc, 课程号 desc; 3
先按照成绩升序排列,相同成绩的按照课程号降序排列。
空值null的排序:
如果数值中有空值,则空值null排列在前面。
null, ' ', 数值1 ,数值2
从查询结果中取出指定行:limit
--从查询结果中取出前2行数据
selct *
from score
limit 2;
SQL运行顺序:
select 查询结果 5
from 从哪张表中查找数据 1
where查询条件 2
group by分组 3
having对分组结果指定条件 4
order by对查询结果排序 6
limit 从查询结果中取出指定行;7
几种常见的 SQL 错误:
运行报错:Err
1. 在having/group by中不能使用了select里别名。having子句是先运行的,select子句是后运行的。having运行时没有”平均成绩“这个别名。
2. 在where子句中不能使用聚合函数(汇总函数)。
3. 字符串类型的数字。常见问题:数据存放的类型如果是字符串,如果进行排序和计算,按照字符串类型计算。字符串和数值类型的计算规则是不一样的。需要改成所需要的数据类型。
练习:sqlzoo中习题练习。
表中存放的是实际数据;
视图中存放的是SQL查询语句;
使用视图时,会运行视图里的sql查询语句创建出一张临时表。
临时表:当客户端与数据库断开时,这个表会自动删除。视图不存放数据,存放的是SQL语句。
create view .......as select 查询语句;
注意事项:不能往视图里插入数据。不建议频繁在视图里创建视图。
SQL语句中有有子查询,先运行子查询。
......in子查询
......any/some 子查询
......all子查询
需要单一值的时候。(返回单一的值。)可以和比较运算符/in/any/some/all一起使用。
编程-弹钢琴
关联条件:where。在每个组里比较(关联子查询)关联条件。
where s1.课程号=s2.课程号
翻译成大白话;写出分析思路;写出对应的sql子句。
如何看懂sql报错信息?一 一排除错误。
汇总函数:count;sum;avg;max;min;
算术函数:round;abs;mod;
字符串函数:length;lower;upper;contact;replace;substring;
日期函数:current_date;current_time;year;mont;day'dayname;
加法:union
(将两个表中重复数据删除,加起来)
union all
(不删除重复值将表的内容加起来)
联结:表之间的关系。
常见联结类型/方式:交叉联结cross join;内联结inner join;左联结eft join;右联结right join;全联结full join。
on是表示通过什么产生关系。
交叉联结(笛卡尔积):将两个表的每一行合并。
inner join:同时存在的数据。
left join:
right join:
full join:
联结在from子句。联结的表。
判断条件。
case when...then...when....then....else....end;
else可以省略,表示空值。最好写。
case表达式+聚合函数可同时使用
SQL知识点总结2:
侧重点SQL查询语句的书写逻辑和运行顺序
mysql:增删改查
SQL增删改查:
对数据库有修改的操作:增删改
对数据库没有修改的操作:查(数据分析中最主要的sql语句)
写sql顺序:
select....from....where...group by....having....order by....limit...;
执行顺序:
select....输出 4
from....获取数据 1
where...过滤 2
group by....分组 3
having....过滤 5
order by....排序 6
limit...;限定个数 7
数据流:
列:事物属性
行:同一个事物的内容
以表呈现的数据/二维数据
数据组织:表的形式
from student 把student这个表的所有数据都获取过来
select.....from....当这个表准备好以后要展示的内容有哪些?
select * from student
select name from student
select可以对数据进行过滤。
where:在from中获取的数据进行过滤,符合条件的数据留下,不符合条件的数据丢弃。(and, or.....)SQL必知必会
select name from student where name = '小刚' ;
select * from student where name = '小刚';
group by:分组。分组后如果遇到select,那么输出的代表是这一组的第一行数据。group by还可以和聚合函数放在一起。
select * from ....where...group by....;
select * from student group by classid;??分组后的第一行数据。不是很理解
把classid相同的分组。
selct count( id, classid ) from student group by classid;
selct count( id, classid ) from student group by classid, name;classid, name两个相同才算是一组。多列 的分组。
select count( 1 ), classid from student;
select count( name ) , classid from student;
区别:如果Name 中有null值,那么count 1 不记录null值。其他情况二者一致。
select count (distinct name) , classid form student;
distinct:排除重复值;
聚合函数:count计数;sum求和;max最大值;min最小值;avg平均数;group-concat字符集;
区分:count(1);count(name);count(distinct name)
sql语句和mysql的关系:
sql语句:一种语言;mysql是一个软件,数据处理软件。用sql语言操作数据库mysql。
开源数据库中最流行的是mysql;oracle/sql server也是试用于sql。
区别:同一个表在不同的数据库查询的内容不一致。有细微的区别。
创建数据库:(schema)
CREATE DATABASE student;
习惯(写sql的时候如果是sql的关键字大写,其他小写)
CREATE TABLE student (id int primary key, name char(10), classid int) defaut charset=utf8mb4;
插入数据库的SQL
INSERT INTO student(id,name, classid) VALUES (1, '假博学',1);
INSERT INTO student(id,name, classid) VALUES (2, '假博',2),(3, '假学',1),(4, '假仁',2),(5, '博学',20;
清空数据表:
TRUNCATE student;
TRUNCATE TABLE student;
where是对行的操作,一行一行的对比/排错是否满足条件。
SELECT * FROM student GROUP BY classid;
为什么class id在输出的时候还进行了排序?
GROUP BY分组,为什么结构还实现了排序,因为进行分组的时候,默地先进行了排序(升序asc)
如果是降序(desc),语句:
SELECT * FROM student GROUP BY classid DESC;
区别:
SELECT COUNT(*), clasid FROM student GROUP BY classid DESC;
SELECT COUNT(1), clasid FROM student GROUP BY classid DESC;
SELECT COUNT(name), clasid FROM student GROUP BY classid DESC;
SELECT COUNT(distinct 1), clasid FROM student GROUP BY classid DESC;
SELECT SUM(id), clasid FROM student GROUP BY classid DESC;
SELECT AVG(id), clasid FROM student GROUP BY classid DESC;
SELECT * FROM student WHERE 1=1 GROUP BY classid;
SELECT * FROM student GROUP BY classid;
where 1=1
where 1=0
1=1永远都是正确;1=0永远都是错误。
HAVING 过滤的是GROUP BY之后的内容。根据SQL语句执行顺序,HAVING 过滤的是GROUP BY后的数据。
查询每个班中人数大于2个的班级号。
每个:分组,GROUP BY,以班级分组class,人数大于2人数count(1)>2
SELECT classid FROM student GROUP BY classid HAVING COUNT (1)>2;
GROUP BY之后能勇select里别名的内容,之前不可以。
ORDER BY 用那一列进行排序
ORDER BY n;
ODER BY classid,n;
LIMIT:取前几的行
LIMIT 1;取第一行
LIMIT 1,2;从1 开始取2个
CASE WHEN:
CASE WHEN 放在SELECT里,输出的时候,把输出信息置换。
CASE WHEN 表达式
THEN 输出
WHEN 表达式
THEN输出
ELSE输出
END
CASE WHEN id=1
THEN 2
WHEN id=2
THEN 1
ELSE
10
END
对比python:
if id==1;
2
elif id==2;
1
else
0
if id=表达式;
输出
elif id=表达式;
输出
else
输出
将1班的学生转到2班
将2班的学生转到1班
3班的学生不变
SELECT id,name FROM student
CASE WHEN 放在SELECT里,输出的时候,把输出信息置换。
(CASE WHEN classid=1 THEN
2
CASE WHEN classid=2 THEN
1
ELSE classid END) AS cld
SELECT id,mame, (CASE WHEN classid = 20 THEN 6 WHEN classid = 6 THEN 20 ELSE END) AS cld FROM student;
聚合函数+CASE WHEN:
SELECT name,
MAX(CASE WHEN stage = ' 基’ THEN score ELSE null END) AS '基',
MAX(CASE WHEN stage = ' 爬’ THEN score ELSE null END) AS '爬',
MAX(CASE WHEN stage = ' SQL’ THEN score ELSE null END) AS 'SQL',
FROM score GROUP BY name;
JOIN连接:两个表相关的查询
连接:对两个表的数据进行重组,将数据组合在一个表里。
SELECT studentid,classid,
FROM后的内容:FROM student JOIN class
WHERE classid=2 AND studentid%=1;(ID是奇数:studentid%=1;ID是偶数:studentid%=0)
GROUP BY studentname;
ON条件:过滤条件
SELECT studentid,classid,FROM student JOIN class ON classid=class.id WHERE classid=2 AND studentid%=1 GROUP BY studentname;
SELECT studentid,classid,
FROM student JOIN class ON classid=class.id (JOIN是FROM后的内容,新生成的表)
WHERE classid=2 AND studentid%=1(ID是奇数:studentid%=1;ID是偶数:studentid%=0)
GROUP BY studentname;
FROM 表 JOIN表ON条件
标黄的当作一个新表
ON可以改成WHERE吗?单纯写JOIN是,ON可以和WHERE替换。但是LEFTJOIN这则不行。
LEFTJOIN和JOIN区别:
LEFTJOIN 必须有ON条件;
回去检查左边的数据是否都包含在新生成的表中。如果是,与JOIN没有区别;如果不是,用NULL与不包含的行组成新行加入新表。
RIGHTJOIN:
A RIGHTJON B=B LEFTJOIN A
多表连接:ABC三个表连接, A和B表先结合成AB表,AB表在和C表结合。
A JOIN B ON....
JOIN C ON...
JOIN D ON...
A JOIN B JOIN C JOIN D
A LEFT JOIN B ON...LEFTJOIN C...
什么情况下用JOIN,什么时候用LEFTJOIN。
查询通信A的班主任名称:
SELECT 班主任名字 FROM student JOIN class ON classid=class.id WHERE studentname='A';
查询哪些同学目前是没有班级的?
SELECT studentname FROM student LEFTJOIN class ON classid=class.id WHERE class.id IS NULL;
两个值做比较只能是同一行的数据。
需要两行之间的对比,自己JOIN自己。
SELECT e1.name FROM employee AS e1 JOIN employee AS e_m2,id WHERE e1.salary>e_m2.salary;
PRIMARY是MYSQL的关键字,加反引号/双引号。反引号在TAB键上。
分组,聚合函数不分家,分组一般都是用来做聚合函数。
子查询:相当直观的SQL语句。
类似英语里的从句。
WHERE里面的子查询:
在查询如果在WHERE/SELECT相当于子查询先计算结果再判断。
SELECT id FROM class WHERE manager='小蓝';
SELECT name FROM student WHERE classid=1;
子查询语句:
SELECT name FROM student WHERE classid=(SELECT id FROM class WHERE manager='小蓝');
SELECT里面的子查询:
=的子查询只能返回一个数值;但是in的子查询能返回一列数据。
SELECT * , (SELECT id FROM class WHERE manager =’ LV‘ )FROM student
HAVING子查询不常见;
FROM里面的子查询:
在FROM里的子查询必须有别名(as)。FROM里的自查询相当于一个新的表。
SELECT studentname FROM student JOIN class ON studentclassid=classid WHERE manager=’小蓝';
自查询语句:
SELECT studentname FROM student JOIN (SELECT * FROM class WHERE manager=’小蓝') AS c ON studentclassid=classid;
任何子查询都可以用JOIN的表变换。
半连接:semi-join
子查询内引用复查询的表信息/在子查询中有访问复查询表信息的叫做半连接。
半连接作用:找到每个classid中最大的ID做对应的信息。
SELECT * FROM student s1 WHERE id=SELECT MAX(id) FROM student s2 WHERE classid=s1.class id;
半连接前置:
找到每个班级学生的最大学号对应的学生信息。
SELECT * FROM student WHERE id IN (SELECT MAX(id) FROM student GROUP BY classid);
SQL书写思路:按数据的流程一点点写。