一)计算机的基本结构
但是实际上,更多的是这种情况:
二)MYSQL中的数据类型:
一)数值类型:
数据类型 内存大小(字节) 说明 bit(M) M指定位数,默认为1 单个二进制位值,或者为0或者为1,主要用于开/关标志 tinyint 1字节 1个字节的整数值,支持0-255的整数 smallint 2字节 2个字节的整数值 int 4字节 表示4个字节的整数值 bigint 8个字节 float(M,D) 4个字节 单精度浮点值,M指定长度,D表示小数位数 double(M,D) 8个字节 双精度浮点值,M指定长度,D表示小数位数 decimal(M,D) M/D+2 定点或者精度可变的浮点值,M表示长度,D表示小数点位数 numeric(M,D) M/D+2 二)字符串类型:
sql中的字符串值可以用单引号也可以用双引号
数据类型 大小 说明 varchar 0-65535字节 可变长度字符串 text 0-65535字节 长文本数据 mediumtext
0-16 777 215字节 中等长度文本数据 BLOB 0-65,535字节 二进制形式的长文本数据 三)日期数据类型
数据类型 大小 说明 datetime 8个字节 范围从1000-9999年 timestamp 4个字节 范围终止到2038年 MYSQL中表示日期:
1)dateTime:1753/1/1~9999/12/31
2)TimeStamp(2023年不够用)
3)字符串(用户自己写了一个日期,系统无法进行校验)
MYSQL插入时间:
1)调用NOW()方法进行插入
2)curdate();
3)curtime();
4)sysdate();
有关日期数据的插入,尽量使用
datetime
, 日期的插入格式为 :yyyy-mm-dd hh:mm:ss
, 其中now()能够获取当前日期
三)数据库的基本操作:
create database Java200; create database if not exists Java300; create database Java400 charset utf8;
1)数据库的名字只能是数字字母下划线,不可以包含其他特殊符号,况且数据库名字不能是SQL中的关键字,如果非要使用,需要将关键字用反引号引起来关键字. 2)创建重复的数据库,前面加if not exists,虽然也是可以执行的,但是不会创建新的数据库,避免了sql语句批量执行时由于这一句报错而中断了后面的执行 3)使用数据库:use+数据库的名字 4)删除数据库:drop+database+数据库的名字 5)展示数据库show databases; 在show databases中间还可以有空格,可以有一个,也可以有多个,不可以没有,SQL中的单词基本上是都是使用空格来进行分割的,况且SQL语句是不会分成大小写的; 6)创建数据库:create+database+数据库的名字1)创建表create table 表名(自定义类型名字+"空格"+数据类型,自定义类型名字+"空格"+数据类型); 2)查看表desc+具体的表名(查看表中都有哪些类型以及字段,每一列是啥名字) (列名在前,类型在后),以及一些补充信息 3)查看都有哪些表,展示表 show tables,这个只是展示表名 4)删除表 drop table+表名
4)咱们在MYSQL中的代码表示钱有两种方式,第一种是使用Decimal/BigDecimal来进行表示还有就是使用int,单位是分
5)在MYSQL中创建列的时候,变量名是不能有-的,一般是使用_的
6)日常在cmd里面,使用ctrl+c来进行表示中断当前的输入,就是你的一个SQL输入一半,你不想要了,想重新进行输入,想要进行复制,就要先进行选中,然后enter建来进行复制
7)比如说像什么varchar(数字),长度具体是多少,产品经理都会有明确的规定的
一)向表中插入数据
1)insert into+表名+values(对应的字段内容),这里面的一个括号就对应着一条完整的记录,字段内容之间要用,来进行分开,values后面可以放多个括号,每一个括号对应一行,多个括号用,来进行分割,列的个数和类型要匹配
create table student(-> id int,-> name varchar(20),-> score decimal(3,1),-> email varchar(50)); Query OK, 0 rows affected (0.03 sec) 插入表中的属性 insert into student values(1,"A",78.0,"156@.com"); 还可以这样进行插入 insert into student values(3,"C",78,"ddd"),(4,"D",89,"hhh");没有被插入的列会自动设置成false 字段与字段之间用,来进行分割 查看表中的数据:select * from+表名,*是一个通配符,意思是进行展示所有的数据 values后面的每一个括号都对应到一行,多个字段之间用,来进行分割 1)要求values后面的字段的个数和表头约定的列数以及每一个列的类型要匹配 2)在SQL中,不区分字符串和字符,我们可以使用,也可以进行使用“ 在咱们的JAVA中,""表示字符串,‘表示的是字符,但是在MYSQL中,是没有单独的字符类型 3)如果某一列是datetime,那么此时该如何进行插入操作呢? 1.通过指定固定格式的字符串来插入一个指定时间 2.通过now()函数来进行插入一个当前系统时间
insert into student (id) values(1),而不可以写成 insert into student id values(1)
2)insert into +表名(属性一,属性二)+空格+values(与前面对应属性的字段)
其他字段将采用默认值
1)就算插入一列,这个大括号还是一定要有的,在进行插入的时侯,不管是几条,大括号都是一定要有的,大括号中的个数和类型要匹配
2)insert操作一次也是可以进行插入多个行,values后面的每一个()都对应着一行,可以一次性的带有多个(),多个()可以使用’,’来进行分割
3)一次进行插入N条记录的速度比一次插入一条,分N次进行插入,要快很多,因为实际上在客户端输入的每一条SQL都会通过网络传输给服务器,插入三条语句,就会构造出三条这样的请求,服务器每一次处理请求的时候,会进行解析请求中的SQL,执行里面的内容,并将响应返回给客户端;
1)insert into student(id,name)values(5,"E"),(6,"F"); 2) insert into student(id,name,score)values(8,"H",89); 3) insert into student(id,name,score,email)values(9,"I",67,"123.com")(10,"J","69","139.com");
二)查询操作:
1)全列进行查找,直接把一张表中的所有行和列都进行查找出来
selext * from 表名,这里面的*表示通配符,可以匹配表中所有的列
1)此时一定要注意:这种查询方式是把一个表中所有的行和列全部查询到,我所查询到的结果是一个临时表,之前咱们所说的数据库中的表,都是在硬盘上面的
2)真实的数据是持久化存在硬盘里面的,但是此时的这个查询结果临时表,不是在硬盘上面,而是在内存当中,随着进行输出的时候,数据也就被释放了;
1)当用户输入sql之后,客户端就会把这条SQL语句包装成网络请求,发送给服务器;
2)服务器在收到请求之后,就会操作硬盘,从硬盘中读取数据,再把数据包包装成响应,响应的数据就是查询的结果;
3)客户端在收到这个响应数据之后,就会临时的把这个结果在内存里面保存起来,并在显示器上面输出
4)随着打印完毕,客户端在内存中的保存的结果数据也就会被释放掉,况且select操作,是不会影响到服务器这边硬盘上面保存的数据
1)但是select * from 表名,这样的操作其实是非常危险的,传输select *语句是很快的,尤其是在线上环境下来进行这个操作,解析执行的过程,数据量很大,就意味着MYSQL服务器会疯狂读取硬盘数据,瞬间会把硬盘IO给吃满,硬盘的读取速度是存在上限的,尤其是机器硬盘;
2)同时MYSQL服务器,就会立即返回数据,由于返回的响应数据数据量很大很多(读出多少返回多少),也会把网卡的带宽给吃满;(百兆网卡,千兆网卡,万兆网卡);
3)服务器就会变得非常忙,如果有其他的客户端来连接服务器,服务器就会无法响应;就难以对其他客户端的请求进行响应,用户可能会出现页面延迟,咱们生产环境的服务器是在无时无刻的在向普通用户提供响应的
4)在实际进行开发的时候,一般公司都会对SQL的执行时间进行长时间的监控,一旦出现了这种慢查询的SQL,就会强制的进行把这个SQL进行杀死;
2)指定列进行查询:
1)在指定列查询的时候要显示的告诉数据库要查询的是那些列,这样数据库就会有针对性地返回数据了,查询的列不用加括号;
2)insert插入操作尤其是指定列插入一定要加上括号,哪怕只给一类插入数据,也要加上括号
3)这里查询结果所显示的表是服务器端数据库返回给客户端的一个临时表,使用查询操作不会对服务器中的数据造成影响, 该临时表在客户端打印后就销毁了;
create table student(-> id int,-> name varchar(20),-> chinese decimal(7,1),-> math decimal(6,1),-> english decimal(6,1));
3)还可以进行一些查询字段是表达式
3.1)就是说在我们进行Select查询的时候,同时进行一些运算操作,通常是列和列之间进行的运算
3.2)比如说:查询所有同学的语文成绩加10,加10操作只是对我们的临时表进行操作,也就是说这里面的查询结果变了,只是数据库服务器针对Select查询的结果进行了加工,把这些加工的数据作为临时表暂时显示出来了
4)根据字段指向别名:相当于是给查询结果的临时表,指定一个新的列名,避免我们的得到的临时表,名字比较混乱,查询每一个同学的总分
select 列名或表达式 as 别名, ... from 表名;
1)总而言之,咱们上面的Select查询结果只是一个临时表,在客户端中临时保存的一个数据表,随着打印的进行,内存就被释放了,临时表的结果对咱们的原始数据没有任何影响
2)况且临时表的结果的数据类型不一定和原始的表的数据类型是完全一致的,比如说进行了相加操作,临时表的类型是会自动适应的,从而保证我们的计算结果是正确的,有效数字可能会发生变化,decimal;
3)比如说上面的+10操作,都是针对一个或者多个列来进行运算操作的,也就是说针对指定列中的每一行数据都会做相同的运算,计算总成绩的时候针对每一行的结果都进行相加,只是行和行之间不会有影响,列和列之间进行相加,才会有影响
- 有null参与的运算最终的结果还是为null.
- 每一个列或者表达式的别名只在当前字段有效, 如果运用到下一个字段就会报错
4)根据查询结果进行去重操作:针对查询的结果,把重复的记录给去掉
select distinct chinese from student; 最好针对一个元素进行使用
针对一列来进行去重:select distinct +列名+from+表名
针对多列来进行去重:select distinct+列名1,列名2+from+表名
在这里面要注意如果是根据多个列来进行去重,那么必须满足一个条件,就必须是多个列的值相同的时候才视为重复,
- 去重查询只支持单列进行去重, 如果一次查询多列就会失去去重效果.
1)创建学生表:create table student(id int,name varchar(20),chinese decimal(3,1),math decimal(3,1),english(3,1)); 2)插入数据:insert into student(id,name,chinese,math,english) values(1,"A",90,90,80),(2,"B",90,90.3,90.1),(3,"C",80,89,70); 3)指定列名进行查找:select name,chinese from student;
+------+------+---------+------+---------+ | id | name | chinese | math | english | +------+------+---------+------+---------+ | 1 | A | 90.0 | 90.0 | 80.0 | | 2 | B | 90.0 | 90.3 | 90.1 | | 3 | C | 80.0 | 89.0 | 70.0 | +------+------+---------+------+---------+
mysql> select distinct chinese from student; +---------+ | chinese | +---------+ | 90.0 | | 80.0 | +---------+
5)排序,针对查询结果临时表进行排序
不会影响到原磁盘的数据的顺序,它所影响的只是针对查询出来的临时表进行排序
select 列名, ... from 表名 order by 列名(要排序的列) asc(或者desc);
select 列名 from 表名 order by 列名 desc/asc;
1)asc是升序操作,desc是降序操作;
2)desc是降序,asc是升序
3)null+任何值都是null,null和其他的值进行运算结果还是null
4)有的数据库记录中是带有NULL值的,像这样的NULL值就认为是最小的,升序就排在最前面,降序就自动排在最后面;
5)当排序指定多个列的时候,不是去将每一列单独进行排序,指定多个类属于复杂规则的比较,比如说指定的两个列,那么先以第一列的比较规则为准,如果第一列的比较比较不出来结果,那么再以第二列的规则去进行比较,以此类推;
1)先查找同学们的信息,再根据语文成绩进行升序排序,select * from student order by chinese desc;
2)根据表达式来进行排序,select id,name,chinese+math+english as total from student order by chinese+math+english asc
3)按照总成绩进行升序排序,降序排序,也可以指定别名select id,name,chinese+math+english as total from student order by total;
4)根据多个列来进行排序
先根据第一个列来进行排序,如果第一个列结果相同,相同结果之间在根据第二个列来进行排序,select * from student order by chinese,math,english;
这是先根据语文成绩进行升序排列,如果语文成绩相同,在按照数学成绩进行升序排列,如果数学成绩在相同,就按照英语成绩进行升序,多个列进行排序的时候是明确优先级的1)select * from student; +------+------+---------+------+---------+ | id | name | chinese | math | english | +------+------+---------+------+---------+ | 1 | A | 90.0 | 90.0 | 80.0 | | 2 | B | 90.0 | 90.3 | 90.1 | | 3 | C | 80.0 | 89.0 | 70.0 | | 4 | D | 10.0 | 78.0 | 80.0 | | 5 | E | 11.0 | 77.0 | 80.0 | | 6 | F | 11.0 | 79.0 | 60.0 | +------+------+---------+------+---------+ 6 rows in set (0.00 sec) 2)select id,name,chinese from studentorder by chinese desc; +------+------+---------+ | id | name | chinese | +------+------+---------+ | 1 | A | 90.0 | | 2 | B | 90.0 | | 3 | C | 80.0 | | 5 | E | 11.0 | | 6 | F | 11.0 | | 4 | D | 10.0 | +------+------+---------+ 3)select name,id,chinese+math+english as total from student order by chinese+math+english asc; +------+------+-------+ | name | id | total | +------+------+-------+ | F | 6 | 150.0 | | D | 4 | 168.0 | | E | 5 | 168.0 | | C | 3 | 239.0 | | A | 1 | 260.0 | | B | 2 | 270.4 | +------+------+-------+ 4)select name,id,chinese+math+english as totalfrom student order by total asc; +------+------+-------+ | name | id | total | +------+------+-------+ | F | 6 | 150.0 | | D | 4 | 168.0 | | E | 5 | 168.0 | | C | 3 | 239.0 | | A | 1 | 260.0 | | B | 2 | 270.4 | +------+------+-------+
select name,chinese+math+english as total from student order by (chinese+math+english as total) asc,这样的查询语句是错误的
select 列名 from 表名 order by 列名1 asc/desc,列名2 asc\desc,列名2 asc\desc
当我们的制定多个列来进行排序的时候,以第一列为主,第二列其次,当第一列的值相同的时候,才比较第二列,如果说最终第一列已经分出大小关系了,那么就不会比较后面的列了
最终的表显示什么,完全取决于select后面的列是怎么写的,写和不写不影响后面的条件,也就是说where后面的条件和咱们前面写的列没有任何关系
这里我们要注意客户端显示的内容,select到where之间写的内容不会影响where之后的条件
6)按照条件进行查询where
写上了条件之后,MYSQL在执行的时候就会进行条件筛选,遍历表中的每一条记录,带入到条件中,如果条件符合,就会把结果返回给客户端;不符合就会进行跳过;
- where条件可以使用表达式,但不能使用别名
- and的优先级高于or,在同时使用时,需要使用小括号()包裹优先执行的部分
最终的表显示什么,完全取决于select后面的列是怎么写的,写和不写不影响后面的条件,也就是说where后面的条件和咱们前面写的列没有任何关系
这里要注意客户端显示的内容,select到where之间写的内容不会影响where之后的条件
1.查询数学成绩小于60分的同学 select * from student where english<60; 2 查找数学成绩大于语文成绩的同学,两个条件之间进行比较 select * from student where math>chinese; (这是指的是针对同一行的语文和英语成绩进行比较,这是不会涉及行和行之间的比较) 3 查找总成绩低于200分的同学 select * from student where chinese+math+english<200; 4 查找数学成绩是空的同学 select * from student where math<=> null; select * from student where math is null; 不可以写成这样 select * from student where math=null 5 查找语文成绩大于80英语成绩大于60分的同学 select name ,chinese+math+english as total where chinese>80 and english<60; 6 查找语文成绩大于60小于70之间的同学 select * from student where chinese>=60 and chinese<=70; select * from student where chinese between 60 and 70; 7 查找数学成绩是45或者是70的同学 select * from student where math=45 or math=70; select * from student where math in(45,70);
7)指定别名:
1)表达式中如果同时有and和or,那么就先计算and再对or进行计算,如果想要打破优先级,就需要加上括号
2)现在来进行写一个SQL语句:
select * from student where chinese>80 or math>70 and english>70
执行结构就类似于这样:
select * from student where(chinese >80) or (math >70 and english>70)
想要显示这个结果的条件就是满足语文成绩大于80分或者数学和英语都大于70分的人
select * from student from student where (chinese>80) or( math>70 and english>70);这样两个括号的条件满足一个就可以在屏幕上面进行展示了;
但是下面的这个写法就和上面完全不一样了:
select * from student where (chinese >80 or math>70) and english>70,这条语句的满足条件就是你这个人的语文后者数学成绩大于70并且你的英语成绩必须大于70
8)like通配符的形式来进行查找
模糊匹配,就相当于描述了一个规则;凡是符合这个规则的字符串,都会被匹配出来;
查找所有名字中含有羊的同学:select * from user where username like "%羊%";
insert into student(id,name) values(7,"G"); Query OK, 1 row affected (0.01 sec)mysql> select * from student; +------+------+---------+------+---------+ | id | name | chinese | math | english | +------+------+---------+------+---------+ | 1 | A | 90.0 | 90.0 | 80.0 | | 2 | B | 90.0 | 90.3 | 90.1 | | 3 | C | 80.0 | 89.0 | 70.0 | | 4 | D | 10.0 | 78.0 | 80.0 | | 5 | E | 11.0 | 77.0 | 80.0 | | 6 | F | 11.0 | 79.0 | 60.0 | | 7 | G | NULL | NULL | NULL | +------+------+---------+------+---------+ 7 rows in set (0.00 sec)mysql> select * from student where chinese=null; Empty set (0.01 sec)mysql> select * from student where chinese <=> null; +------+------+---------+------+---------+ | id | name | chinese | math | english | +------+------+---------+------+---------+ | 7 | G | NULL | NULL | NULL | +------+------+---------+------+---------+ 1 row in set (0.01 sec)mysql> select * from student where chinese is null; +------+------+---------+------+---------+ | id | name | chinese | math | english | +------+------+---------+------+---------+ | 7 | G | NULL | NULL | NULL | +------+------+---------+------+---------+ 1 row in set (0.01 sec)mysql> select * from student where chinese is not null; +------+------+---------+------+---------+ | id | name | chinese | math | english | +------+------+---------+------+---------+ | 1 | A | 90.0 | 90.0 | 80.0 | | 2 | B | 90.0 | 90.3 | 90.1 | | 3 | C | 80.0 | 89.0 | 70.0 | | 4 | D | 10.0 | 78.0 | 80.0 | | 5 | E | 11.0 | 77.0 | 80.0 | | 6 | F | 11.0 | 79.0 | 60.0 | +------+------+---------+------+---------+ 6 rows in set (0.00 sec)
like操作通常要搭配通配符来进行使用 1)%匹配任意个任意字符,包含0个字符 2)_匹配一个任意字符1))查找所有姓孙的同学 select * from student where name like "孙%";//此处我们这里面的孙%就可以匹配 任意以孙为开头的字符串,孙,孙########,但是匹配不到aaaa孙,aaa孙aaa select * from student where name like "孙_";//孙坚,孙策,但是孙XXXX不能匹配2))查找所有语文成绩以9开头的同学 select * from student where chinese like"9%"; 3))查找所有姓孙的同学并且语文成绩大于90的同学 select * from student where chinese>90 and name like "孙%";如果写成%孙%,就意味着孙前面有着任意个字符,后面有着任意个字符 孙,孙aaa,aaa孙bbb,名字中包含孙即可,如果写成aaaa就匹配不到
10)进行分页查询,order by的所在位置是在limit前面,limit是在最后面的
select 列名, ... from 表名 limit 限制个数 offset 起始下标;
select 列名, ... from 表名 limit 限制个数, 起始下标;limit+n,取出已经读取数据结果中的前N条,他的效果就类似于limit+n+offset+0,意思就是说从我们查询结果的第0个位置开始,向后取N条记录
limit+n+offset+M,取出当前下标为N的记录开始,再向后找M条,默认从0开始,从我们查询结果的第M个位置开始,向后取N条记录
在SQL中我们可以通过limit来实现分页查询 此时就可以约定好,一页当中最多显示多少个结果,在进行查找的时候 就可以根据页来进行返回,假设一页有20条记录: 第一页,就返回1-20 第二页,就返回21-40 第三页,就返回41-60 在SQL中,我们可以通过limit来实现分页查询 1)取我们进行查询结果的前三条 select * from student limit 3;只取出结果中的前三条 2)取我们结果中的4,5,6条记录,意思就是从当前我们查询出来的结果开始,再向后找三条 select * from student limit 3 offset 3; 3)我们来进行查询一下总成绩排名的前三名(注意order by是不可以和where进行在一起使用的select id,name,chinese+math+english as total from student order by chinese+math+english desc limit 3; 4)现在我们来查询一下总成绩排名的4-6名 select id,name,chinese+math+english as total from student order by chinese+math+english desc limit 3 offset 3;
现在来进行使用一下分页查询,根据每一位同学的总成绩显示结果,每一页有三条记录:
第一页:mysql> select name,chinese+math+english as total from student order by total desc limit 3 offset 0; 从我们查询结果的0号索引开始,向后取三条记录0-3 +------+-------+ | name | total | +------+-------+ | B | 270.4 | | A | 260.0 | | C | 239.0 | +------+-------+ 3 rows in set (0.00 sec)第二页:mysql> select name,chinese+math+english as total from student order by total desc limit 3 offset 3; 从我们查询结果的第三条记录开始,向取三条记录 +------+-------+ | name | total | +------+-------+ | I | 218.0 | | H | 210.0 | | D | 168.0 | +------+-------+ 3 rows in set (0.01 sec)第三页:mysql> select name,chinese+math+english as totalfrom student order by total desc limit 3 offset 6; 从我们查询结果的第六条开始,向后取3条记录 +------+-------+ | name | total | +------+-------+ | E | 168.0 | | F | 150.0 | | G | NULL | +------+-------+ 3 rows in set (0.00 sec)
三)修改操作:
SQL修改:update+表名 set 列名=值,列名=值 where 条件
1)set关键字只需要写一次就可以了
2)多个修改恒等式中不要使用and,要使用,号来分割多个更新条件;
注意: 1)insert和表名之间有一个into字段 2)select与表名之间有一个from 3)但是update与表名之间,啥也没有,啥连接词也没有 另外,这里面的where和select中的where,用法是相同的,除了where之外,像order by和limit也是可以进行使用的 update是真的可以修改数据库服务器上面的原始数据的
1)核心操作:update+表名+set+列名=修改的值 where 条件,符合条件的行就会被修改,不符合条件的行就不会被修改,如果这里面的where省略了,就默认修改所有的行和列,这里面的列名可以有多个;
2)常见错误:
1)精度溢出:update student set chinese=chinese+10 where id<5; ERROR 1264 (22003): Out of range value for column 'chinese' at row 1 2)语法记忆错误: update student set chinese=chinese+1 and set english=english-1 where id<9; update student set chinese=chinese-1,set english=english-2 where id<9;
但是下面这两个操作都是正确的:
1)mysql> update student set chinese=chinese-10 and english=english-10 where id=1; 2)mysql> update student set chinese=chinese-10,english=english-5where id<7;
//注意:order by的使用要在from+表名之后 1)将A的语文成绩加10,下面两条是错误的update student chinese+10 where name="A";update student set chinese+10 where name="A"; 正确的写法是update student set chinese=chinese+10 where name="A"; 2)将B的语文成绩改成80,英语成绩改成40 update student set chinese=80,english=40 where name="B"; 3)将所有人的语文成绩减10分,后面where不用添加任何条件update student set english=english-10; 4)将总成绩倒数后三名的同学的英语成绩减十分 关键的点有两个:1)首先要根据总成绩进行排序 2)再用limit来进行筛选前三 3)在对英语减10分 update student set english=english-10 order by chinese+math+english asc limit 3; Matched:where语句匹配到了几行 Changed:真正的修改了几行
1)当执行SQL语句进行操作数据库的时候,比如说:update student set chinese=chinese/2,这个是对所有的数据进行修改
2)这个时候的/2操作就会有可能发生警告,因为/2之后数据的位数就有可能超decimal(N,M)中的N位数,这时候有可能会发生数据溢出,通过show warnings就可以查看警告,Data truncated for column........,这就说明数据发生了截断
四)删除操作:
delete from 表名 where 筛选条件 delete 后面没有* 如果这里面的筛选条件不一样,如果不写条件 或者是写错了 那么影响范围就会非常大 就会把整个表的数据都会给删除掉,但是表中的属性还在 他和drop table还是有一定的区别的,表没了,数据也没了; 这个就例如 ArrayList list=new ArrayList(); list.clear(),这个操作是将arraylist中的所有数据都给清空掉了; list=null;把盒子和数据都给清空掉了 1)根据指定名字来进行删除 delete from student where name="A"; 2)根据指定ID来进行删除 delete from student where id=3; 3)根据指定分数来进行删除 delete from student where chinese=118.1;
以后进入到公司之后,如果说你确实要进行操作线上的数据库,其他重要的程序都是同理,要是怕自己整出事来,你可以拉上一个人,让它来帮你看看;
四)数据库的约束
约束:数据库对数据本身有一些要求和限制,帮助程序员检查数据是否靠谱
自定义名字+数据类型+约束类型,例如id int not null;
1)约束就是,数据库在使用的时候,对于里面的所存数据提出的要求和限制;
2)程序员就可以根据约束,对数据进行很好的校验,保证数据给符合咱们的需求和规范
3)在这里面所说的约束,都是针对每一个列进行设置的,不同的列之间是没有影响的
约束是关系型数据库的一个重要功能, 添加到库中的数据需要保证其的正确性;
约束, 就是让数据库帮助程序员更好的检查数据是否正确.
下面说的限制,都是针对每一个列来单独进行设置的,只是针对这个列是这样的 不同的列之间是不会有影响的1)not null表示某列不可以存储空值,如果尝试向着里面插入空值,就会发生报错; 2)unique数据必须保证唯一,如果尝试插入相同的数据,就会报错(通过desc查看表就会发现key的一列变成了UNI) 3)default约定一个默认值,如果在进行插入的时候没有进行设置具体的值, 就会自动被设成默认值,自动的默认值被设成null; 4)primary key叫做主键约束,相当于是数据身份的唯一标识,相当于是身份证号码 手机号码,相当于是not null和unique的集合 5)foreign key外键,描述两张表之间的关联关系,表一指定列的数据必须在表二指定列中中存在 6)check这是指定一个条件,通过条件来对值进行判定,MYSQL不做支持
1)Map里面存的是一个一个的键值对,Map里面又没有实现Iterable接口,要想遍历一个Map,就要先把Map转换成Set,Set里面的元素就叫做entry,里面就包含着一个键和一个值
2)MYSQL是如何进行判断列是否是重复的呢?
这样的插入过程,其实就和咱们之前学过的二叉搜索树的插入过程是类似的,先要进行查找在进行插入
3)在MYSQL中,默认的默认值是空,在进行指定列插入的时候,其余的值会被自动的设成默认值,也就是空,此处也可以把默认值转化成我们想要的值就需要使用关键字default;
4)对于一张表来说,只能有一个列被指定为主键,primary key=not null unique
2)mysql> create table student(-> id int unique,-> name varchar(20) not null); 没有指定id列,其默认值为null,插入失败 not null是可以给任意一个列来进行设置非空,这是互不影响的 mysql> insert into student values(1,"A"); Query OK, 1 row affected (0.01 sec) mysql> insert into student values(1,"B"); ERROR 1062 (23000): Duplicate entry '1' for key 'id'3)create table student(-> id int unique not null,-> name varchar(20) not null);
1)create table student(id int not null,name varchar(20));当我们在尝试去插入元素的时候,就会插入失败; mysql> insert into student values(null,"A"); ERROR 1048 (23000): Column 'id' cannot be null inert into student(name) values("zhangsan”);
1)create table student(-> id int unique not null default "80",-> name varchar(20) default "like"); insert into student(id) values(1); 这个查询的结果就是id 变成1,name变成like 2)主键约束primary key相当于是unqiue和not null; 对于一张表来说,只能有一个列被指定成主键;create table student(-> id int primary key,-> name varchar(20) unique);create table teacher(id int not null unique,name varchar(30) default "生命"); //not null和unique是可以写在一起的
3)主键的一种特殊用法,自增主键,在primary key后面加上一个auto_increment 这就变成了一个自增主键; create table student(-> id int primary key auto_increment,-> name varchar(20));insert into student values(null,"A"),(null,"B"); 当我们设置好自增主键之后,此时我们所插入的记录,就可以不指定自增主键的值了(直接写成null) 交给MYSQL自行处理即可; +----+------+ | id | name | +----+------+ | 1 | A | | 2 | B | +----+------+
但是要注意,自增主键也是可以自己手动指定id的,例如我写一条,insert into student values(10,"E"),后续插入的数据只能是10以后的数据了,3-10中的数据就用不了了
create table teacher(id int not null unique,name varchar(30) default "生命");mysql> desc teacher; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(30) | YES | | 生命 | | +-------+-------------+------+-----+---------+-------+ 2 rows in set (0.01 sec)
外键约束:
1)外键约束描述的是两张表两个列之间的对应关系,子表要依赖于父表,要求子表中的数据必须在父表中存在;
2)foreign key(本表中所要父表中所依赖的列) references 父表的名字(引用的父表的列);
1)外键约束,针对两张表进行关联 学生(studentid,name,classID) 1 A java100 2 B java102班级表(id,name) 1,java100; 2,java101; 3,java102; 这里面的每一个学生都有着自己的班级,这个班级必须存在,如果学生表这里出现了一个记录 这里的班级ID是10000,这个数据就是非法的; 学生表中的classID要依赖于班级表中的ID;学生表依赖于班级表,我们此时就把学生表称为子表,班级表称为父表;create table class(-> classID int primary key auto_increment,-> name varchar(20));insert into class values(null,"java100"),(null,"java101"),(null,"java102"); 构建一个学生表,指定外键约束create table student(-> studentID int primary key auto_increment,-> name varchar(20),-> classid int,-> foreign key(classid) references class(classID));外键约束一般是写在末尾,foreign key(在这个表中要指定的加上主键的列) references 另一张表的名字(需要关联的另一张表的列)insert into student values(null,"A",1),(null,"B",2); 这时就可以插入成功,因为学生的classid里面的值 在class表中的ClassID中已经存在 我们不仅仅在新增的时候要考虑到外键约束, 在修改的时候也要考虑到外键约束 update student set classid=90 where studentID=1;------操作失败
在指定外键的时候,需要指定三方面
1)需要指定当前表中的那一列进行关联
2)需要指定和那张表进行关联
3)指定和目标表中的那一列进行关联
foreign key(属性1) references 目标表名(属性2)要删除/修改的父表中的记录,前提是子表中没有记录与父表中的记录相关联
当子表中插入的记录和父表建立联系后此时就不能直接删除父表了要删除父表要先删除子表
1)当进行对student表进行插入的时候,MYSQL会先拿着classid到class表中的ClassID查询一下,看看是否存在,才可以插入成功;
2)但是此时想要删除class表,也就是父表,可以删除成功吗?
delete from class where classID=2,这时候是无法进行删除的,外键约束是在同时约束着父表,当父表中的某条记录被子表依赖着的时候,此时如果再去尝试删除或者修改,都会失败,drop table class也是不可以的;
3)但是如果student表中的班级ID没有在class表中的班级ID出现过,此时就会可以进行删除把class中的这个classID,对于子表来说不能乱插入或者修改,对于父表来说不能随便乱删除,必须保证这一列没有其他字表依赖才可以进行删除
外键约束的底层实现原理:在子表中插入新的记录的时候,就会先根据对应的值,在父亲表中进行查询,查询到之后,才可以进行后续的插入
4)但是这里面的内置的查询操作,可能是一个成本比较高的操作,在父亲表中进行查询是一个比较低效的操作,父表中所要依赖的这一列,必须要有索引,有了索引之后才可以大大的提高效率;
5)class表中的classID这一列,必须是primary key或者是unique,有了这两个条件之后,就会自动地构建出索引了;
6)一个父列可以约束多个子列,一个子列可以被多个父列所约束
1)现在有一个商品表,保存了商品信息:
商品ID 商品名字
1 苹果手机
2 涂改液
3 鼠标键盘
2)现在还有一个订单表,里面保存了订单信息
订单ID 商品ID
1 1
2 2
在上面描述的就是外健约束的关系,我们就可以认为商品表是父表,订单表就是子表,订单表中的ID应该在商品表中的ID存在
1)下好单子了,买了一个苹果手机,很久之后发现这个商品被下架了,从商品表中被删除了
2)那么这个数据是怎么删除的呢?按照刚才说的这个外健约束,一旦建立好了约束关系,那么父亲表的记录是不能够随便删除的,如果这个记录被子表依赖了,那么就无法删除了
第一种方案:
直接将订单表对应的记录进行删除,然后订单表中的订单ID不能出现在商品表中的商品ID,我们再进行删除对应的商品,先把订单表中的订单ID=1进行删除,然后再进行删除商品表中的订单ID=1的记录
第二种方案:但是正常来说订单是不应该被删除的,用户有权限查看自己的历史订单记录
重新建立一个不被约束的订单表,删除原记录
第三种方案:
在这里面删除的方法是,并不是真的删除,而是采用一种逻辑上的删除,我们给商品表增加一个列,如果这个列是1,那么表明这是一个有效记录,如果这一列是0,那么就表示这是一个无效记录;
后续在进行条件查询商品信息的时候,直接将IsOK=0的商品信息全部过滤掉就可以了,只保留isOK=1,也不会打破子表和父表之间的外健约束
如果不是真的删除而是逻辑上进行删除的话,这个记录不就会始终存在吗?这不就会导致数据库里面的内容越来越多,逐渐膨胀,不是十分浪费磁盘空间吗?
磁盘空间不值钱,远远不如人力成本高
那么我们如何进行设计数据库,如何进行设计表呢?
一个典型的通用的办法就是先找出这个场景中所对应到的实体再来具体分析实体之间的关系,实体也被称之为JAVA中的对象,实体就是可以视为需求中一些关键性的名词;
现在有一个具体的场景:
学生管理系统:
1)表示学生的基本信息
2)表示班级的基本信息
3)表示学生所学的课程的基本信息
一对一:
设计数据库:造句法
一对一的关系:
拿教务系统为例
学生表(student):学生的ID,学生姓名名字,学生班级名字
用户表(user):用户的账号(13848061479),用户的密码
造的句子:一个学生对应到一个账户,一个账户只能对应到一个学生,一个账户不能被多个学生所共有,一个学生也不能有多个账户;
那么在数据库中如何表示这种一对一的关联关系呢?1)可以把这两个实体用一张表来进行表,我们直接用一张表把这两张表的信息全部表述出来
2)可能用两张表来进行表示,其中一张表包含了另一张表的ID,user表中用一个userID,student表中可以有一个studentID,根据这个对关系,就可以随时找到某个账户对应的学生是谁,某个学生对应到的账户是谁
一对多:
student学生表(姓名,学号)
class表(班级编号,班级名称)
一个学生应该处于一个班级中,一个班级里面可以包含很多学生;第一种方法:在班级表里面新增一列,表示这个班级里面的学生ID都有啥?
通过这种方式就可以进行表示一个班级都对应着那些同学像MYSQL没有数组类型,但是像Redis这样的数据库,就有数组类型
第二种方法:班级表不变,在学生表里面,新增一列,叫做classID;
通过班级编号和学生的班级ID就巧妙地把学生和班级进行了关联,通过这种方式也能知道一个班级对应着那些同学
多对多:
学生表(学号,姓名)
课程表(课程编号,课程名字)
造句:一个学生,可以选择多个课程,一个课程,可以包含多个学生;
M个学生可以有N门课
在多对多的过程中,数据库设计就一招,使用一个关联表,来进行表示两个实体之间的关系
五)高级查询:
1)和查询结合在一起的新增操作
中心思想:把从上一个表中的查询结果,作为下一个表中要插入的数据
1)现在先创建一个表A,里面两列是字符串和整数,此时也要进行创建表B,分配字段名,保证第一列是字符串,第二列是整数,这样才可以保证A的记录成功的被导入到B里面了
2)insert into B select * from A,这个操作就是说先执行查找,在A里面进行查找,针对进行查找到的每一条结果插入到B里面;
drop table student; mysql> create table student(id int,name varchar(40)); drop table teacher; mysql> create table teacher(userID int,username varchar(40)); 1)insert into student values(1,"A"),(2,"B"),(3,"C"); 2)insert into teacher select * from student; 3)select * from student; +--------+----------+ | userID | username | +--------+----------+ | 1 | A | | 2 | B | | 3 | C | +--------+----------+ 4)select * from teacher; +--------+----------+ | userID | username | +--------+----------+ | 1 | A | | 2 | B | | 3 | C | +--------+----------+
当表中的列的顺序和我们已经写好的标的顺序不一样的时候,我们也是可以进行插入的create table Test(username varchar(50),id int); 这么写是不行的:insert into Test select * from student; ERROR 1366 (HY000): Incorrect integer value: 'A' for column 'id' at row 1 可以这么写:所以我们就可以通过指定列查询的方式,保证我们查询结果的顺序可以和Test对上:insert into Test select name,id from student; mysql> select * from Test; +----------+------+ | username | id | +----------+------+ | A | 1 | | B | 2 | | C | 3 | +----------+------+
1)create table TestDemo(username varchar(40),userID int,password varchar(40)); 2)insert into TestDemo(username,userID) select name,id from student; 3)select * from TestDemo; +----------+--------+----------+ | username | userID | password | +----------+--------+----------+ | A | 1 | NULL | | B | 2 | NULL | | C | 3 | NULL | +----------+--------+----------+
1)总而言之,select指定一些其他的条件例如说排序,去重,limit操作都是可以的,我们实际上插入的就是select执行结果的临时表,是把查询的结果是在临时表里面的,是把临时表的数据插入到数据库服务器的硬盘里面了insert修改的是硬盘的数据
2)将表a中的数据插入到表b,实质上是先查询a表中的数据生成临时表,再将临时表中的数据插入表中,要注意的是查询出来的记录(临时表)的列需要和表b相对应的列数据类型相同才能插入成功.
2)聚合查询(把多个相同字段的列放在一起):
本质上来说就是把多行的数据给聚合到一起了,之前咱们查询带有表达式的操作,这个是属于列和列之间的运算
常见的聚合函数:
查看有多少行:
下列四种聚合函数不是针对数字没有意义
1)null的值是不会记录到count(Chinese)中了,但是计算count(*)的时候会进行计算
2)sum只能针对数字进行运算,不可以用于字符串之间的运算
MYSQL中内置了一些聚合函数让我们直接进行使用 1)查找数据库中有多少行 select count(*) from student; 上面的这个操作就是先进行select * from student,再来进行查询结果有多少行 2)查找名字有多少行,查找语文成绩有多少行? select count(id) from student; select count(name) from student; select count(chinese) from student; 3)求和sum(字段)select sum(chinese) from student;//查找语文成绩的总和select sum(math) from student;select sum(english) from student;
这些聚合函数是针对一个或多个列的行来进行运算的, 其中sum,avg,max,min这几个聚合函数只能针对数值类型进行计算,,不能是字符串和日期类型;
聚合函数还可以针对搭配where语句来进行使用
比如说我们进行查找所有英语成绩大于70分的同学的总和
select sum(english) from student where english>70;
形如这样的操作,会先执行条件筛选,再去执行聚合操作
select sum(chinese)+sum(english) from student;
select sum(chinese+math+english) from student;
1)查找语文成绩的平均分
select avg(chinese) from student;
2)查找所有语文成绩分数小于90的平均分
select avg(chinese) from student where chinese<90;
3)求最大值
select max(chinese) from student
1)create table student (userID int,username varchar(70),chinese int, math int,english int);
2)insert into student values(1,"A",90,89,80),(2,"B",70,30,80),(3,"C",80,77,60),(4,"D",70,89,60),(5,"E",70,88,100);
3)insert into student values(6,"H",null,null,99);
4)select * from student;
+--------+----------+---------+------+---------+
| userID | username | chinese | math | english |
+--------+----------+---------+------+---------+
| 1 | A | 90 | 89 | 80 |
| 2 | B | 70 | 30 | 80 |
| 3 | C | 80 | 77 | 60 |
| 4 | D | 70 | 89 | 60 |
| 5 | E | 70 | 88 | 100 |
| 6 | H | NULL | NULL | 99 |
+--------+----------+---------+------+---------+
5)select distinct count(chinese) from student;
+----------------+
| count(chinese) |
+----------------+
| 5 |
+----------------+
6) select count(chinese)+count(math) from student;
+----------------------------+
| count(chinese)+count(math) |
+----------------------------+
| 10 |
+----------------------------+
7)> select sum(chinese+english+math) from student;
+---------------------------+
| sum(chinese+english+math) |
+---------------------------+
| 1133 |
+---------------------------+
8)select sum(chinese) from student where chinese>100;
+--------------+
| sum(chinese) |
+--------------+
| NULL |
+--------------+
创建一个学生表插入数据,进行函数查询
create table result (id int,name varchar(20), chinese decimal(4, 1), math decimal(4, 1), english decimal(4, 1)); insert into result values(1, 'A', 67, 98, 56),(2, 'B', 87.5, 78, 77),(3, 'C', 88, 98.5, 90),(4, 'D', 82, 84, 67),(5, 'E', 55.5, 85, 45),(6, 'F', 70, 73, 78.5),(7, 'G', null, 75, 65),(8, null, null, 75, 65);
1)计算所有同学语文成绩的总和
select sum(chinese) from result
2)统计英语成绩不及格同学(<60)成绩的总和
select sum(english) from result where english<60;
3)统计所有同学总分的平均分
select avg(chinese+math+english) from result
4)统计英语最高分
select max(english) from result
5)统计70 分以上的数学最低分
select min(math) from result where math>=70
3)分组操作:group by根据行的值,对数据进行分组,把值相同的行都归成一组,不同的值就存放在不同的组里面
create table HH(id int primary key auto_increment,name varchar(20),role varchar(20),salary decimal(7,1));insert into HH values(null,"E","teacher",3000.3),(null,"B","doctor",4000.2),(null,"C","teacher",5000.7);
1)需要来进行查询,要查找每一个角色的最高工资,最低工资和平均工资,那么这种角色我们就需要按照岗位来进行分组
2)group by role,这种写法就是把角色进行分组,把teacher,放在一组,里面有A,E,C;把doctor放在一组,里面有B,S;把所有的leader放在一组,里面有O和J;
1)先根据group by按照role来进行分组
2)再次执行聚合函数,针对咱们分出来的每一个组来进行求最大值,最小值,还有平均值
3)咱们SQL的执行顺序并不是按照咱们写的顺序来进行执行的
select role,max(salary),min(salary),avg(salary) from HH group by role
from前面是最终展现的结果;后面实质上是先分成了三个组,并针对每一个组来进行求最大值,最小值,平均值;
先执行group by进行分组,再去针对每一个组,来进行聚合查询函数,也可以进行指定别名:
select role,max(salary) as max,min(salary) as min,avg(salary) from HH group by role
咱们实际上可以先画图,根据group by来进行分组,再来进行执行聚合函数
1) create table TestData(userID int primary key auto_increment,username varchar(60),role varchar(80),salary int); 2)insert into TestData values(null,"A","老师",9000),(null,"B","老师",9003),(null,"C","程序员",10000),(null,”D","程序员",10000); 3) insert into TestData values(null,"I","领导",10007),(null,"R","领导",6000),(null,"K","老师",7000),(null,"O","程序 员",10000); 4) select * from TestData; +--------+----------+-----------+--------+ | userID | username | role | salary | +--------+----------+-----------+--------+ | 1 | A | 老师 | 9000 | | 2 | B | 老师 | 9003 | | 3 | C | 程序员 | 10000 | | 4 | D | 程序员 | 10000 | | 5 | I | 领导 | 10007 | | 6 | R | 领导 | 6000 | | 7 | K | 老师 | 7000 | | 8 | O | 程序员 | 10000 | +--------+----------+-----------+--------+ 5)select role,avg(salary),max(salary),min(salary) from TestData group by role; +-----------+-------------+-------------+-------------+ | role | avg(salary) | max(salary) | min(salary) | +-----------+-------------+-------------+-------------+ | 程序员 | 10000.0000 | 10000 | 10000 | | 老师 | 8334.3333 | 9003 | 7000 | | 领导 | 8003.5000 | 10007 | 6000 | 6) select role,salary from TestData group by role; +-----------+--------+ | role | salary | +-----------+--------+ | 程序员 | 10000 | | 老师 | 9000 | | 领导 | 10007 | +-----------+--------+ 这么写的话只会针对第一次出现的工资进行显示
因为我们是在分组之前进行指定的条件,所以我们需要把这个where条件写在group by之前
如果我们是在分组之后进行指定的条件,所以我们需要把having加入到group by的后面
1)分组之前指定条件:去掉A这个角色,再去求每一组角色的薪资的平均值:
select role,avg(salary),max(salary),min(salary) from TestDatawhere username!="A" group by role; +-----------+-------------+-------------+-------------+ | role | avg(salary) | max(salary) | min(salary) | +-----------+-------------+-------------+-------------+ | 程序员 | 10000.0000 | 10000 | 10000 | | 老师 | 8001.5000 | 9003 | 7000 | | 领导 | 8003.5000 | 10007 | 6000 | +-----------+-------------+-------------+-------------+
2)分组之后指定条件,求出每一种职业的平均薪资大于9000以上的:
mysql> select role,avg(salary),min(salary) from TestData group by role having avg(salary)>9000; +-----------+-------------+-------------+ | role | avg(salary) | min(salary) | +-----------+-------------+-------------+ | 程序员 | 10000.0000 | 10000 | +-----------+-------------+-------------+
1)先根据group by来进行分组
2)再求出聚合函数,求出每一组的平均薪资,最小薪资
3)再根据having对分组之后算出的聚合函数再进行筛选,平均薪资大于9000的被保留
再来写一个SQL,把用户K和O去掉,求每一类职业的平均值大于8000的
select role,avg(salary) from TestData where username!="K" and username!="O" group by role having avg(salary)>8000; +-----------+-------------+ | role | avg(salary) | +-----------+-------------+ | 程序员 | 10000.0000 | | 老师 | 9001.5000 | | 领导 | 8003.5000 | +-----------+-------------+ 1)先根据where指定条件进行筛选,这是分组前的筛选,把K和O去掉 2)根据group by进行筛选,把角色相同的分成一组 3)进行聚合查询,求出平均工资 4)再根据分组后的工资进行筛选(having)
进行avg的时候,NULL这样的记录是不会记录在其中的,是不会影响到平均值这样的结果的
2)现在再去查询,查询出所有平均工资高于4000的工作岗位
2.1)进行分组之后要对结果进行条件筛选,就要用到having
2.2)想要针对分组之前,想要查询结果,我们可以通过where来进行指定条件
这里一定要注意where和group by两者的区别
having是针对group by之后的结果进行筛选
where是针对原始数据表中的每一条记录都会进行筛选
下面来举个例子:
1)分组之前进行条件筛选:也是求每种角色的平均薪资,但是要去掉A,
这是分组之前所指定的条件,就需要使用where
select role,avg(salary) from HH where name!="A" group by role ;
去掉A并且去掉Eselect role,avg(salary) from HH where name!="A"
and name!="E" group by role;
这里一定要注意:where要写在group by 前面,having要写在group by后面2)分组之后再去进行条件筛选:求每种角色的平均薪资,
只保留平均薪资在4000以下的角色,在这里面就要先进行分组计算
知道了各个岗位的平均薪资之后才可以进行筛选;
select role,avg(salary) from HH
group by role having avg(salary) >4000;
select avg(salary) from HH group by role having avg(salary)>4000;
4)联合查询(把多张表组合在一起,多表查询,多个表的记录是放在一起的)
3.1)笛卡尔积,多表查询的核心操作,非常类似于排列组合,它是针对任意两张表进行运算;
3.2)中心思想就是拿第一张表的第一条记录,和第二张表的每一条记录分别进行组合,这样就得到了一组新的记录,我们再拿第一张表的第二条记录,分别于第二张表的第一条,第二条进行组合
学生表(studentID,name,classID)1 张三 12 王五 13 李四 24 赵六 2 班级表(classID,name)1 java1002 java1013 java102 进行笛卡尔积之后,得到的结果就类似于这样: studentID name classID classID name1 张三 1 1 java1001 张三 1 2 java1011 张三 1 3 java1022 王五 1 1 java1002 王五 1 2 java1012 王五 1 3 java1023 李四 2 1 java1003 李四 2 2 java101 3 李四 2 3 java102 那么此时A,B两张表进行笛卡尔积运算的时候,此时笛卡尔积的行数,就是A的行数*B的行数;笛卡尔积的列数就是A的列数+B的列数;如果A,B两张表都很大,就有可能把数据库给搞挂了
1)那么在SQL中如何进行笛卡尔积的操作呢?(表明之间使用,进行分割)
select 显示的字段+from+表名1,表名2;select * from student,class
2)查询同学对应的班级
1)现在要查询学生与对应的班级名字,上面的笛卡尔积是一个无脑的排列组合这里面的排列组合不一定都是有意义的数据
2)咱们使用多表查询之前是否应该是用一下count(*)来看看数据的范围呢?
这个是不行的,因为数据的变化是在随时变动的,你也无法确定一段时间内有多少用户被插入到了你的数据库里面
3)上述过程中的多表查询,学生表中的ClassID和班级表中的ClassID相同的信息记录才是我们最终要保留的记录;
直接from一张表,后面写表名,from 表1 join 表2 on 条件 select student.name,score.score from student join score on student.id=score.student_id and name="A"; from 表1,表2 where 条件
1)where语句之前是要展示的数据,表名.列名(属性),况且如果笛卡尔积中的两个列名字相同,就可以通过表名.列名的方式来进行访问,如果列名没有重复,那么也是可以通过表名.列名的方式进行访问,此时也是可以进行省略表名的
2)我们在最终的查询结果中,一般只是需要部分列来进行显示,想要哪个列就可以通过表名.列名的方式来进行显示这一行就行了;
drop table if exists classes; drop table if exists student; drop table if exists course; drop table if exists score;create table classes (id int primary key auto_increment, name varchar(20), `desc` varchar(100));create table student (id int primary key auto_increment, sn varchar(20), name varchar(20), qq_mail varchar(20) ,classes_id int);create table course(id int primary key auto_increment, name varchar(20));create table score(score decimal(3, 1), student_id int, course_id int);insert into classes(name, `desc`) values ('计算机系2019级1班', '学习了计算机原理、C和Java语言、数据结构和算法'), ('中文系2019级3班','学习了中国传统文学'), ('自动化2019级5班','学习了机械自动化');insert into student(sn, name, qq_mail, classes_id) values ('09982','黑旋风李逵','xuanfeng@qq.com',1), ('00835','菩提老祖',null,1), ('00391','白素贞',null,1), ('00031','许仙','xuxian@qq.com',1), ('00054','不想毕业',null,1), ('51234','好好说话','say@qq.com',2), ('83223','tellme',null,2), ('09527','老外学中文','foreigner@qq.com',2);insert into course(name) values ('Java'),('中国传统文化'),('计算机原理'),('语文'),('高阶数学'),('英文');insert into score(score, student_id, course_id) values -- 黑旋风李逵 (70.5, 1, 1),(98.5, 1, 3),(33, 1, 5),(98, 1, 6), -- 菩提老祖 (60, 2, 1),(59.5, 2, 5), -- 白素贞 (33, 3, 1),(68, 3, 3),(99, 3, 5), -- 许仙 (67, 4, 1),(23, 4, 3),(56, 4, 5),(72, 4, 6), -- 不想毕业 (81, 5, 1),(37, 5, 5), -- 好好说话 (56, 6, 2),(43, 6, 4),(79, 6, 6), -- tellme (80, 7, 2),(92, 7, 6)
创建表操作:
1)创建学生表create table student(id int primary key auto_increment,student_id varchar(20),name varchar(20),qq_mail varchar(20),class_id int);2)创建班级表 create table class(classID int,name varchar(20),about varchar(30));3)创建课程表 create table course(courseID int primary key auto_increment,name varchar(20));
Query OK, 0 rows affected (0.03 sec)4)创建分数表
create table score(
score int,
studentID int,
course_id int);
插入:
1)向学生表里面插入数据 insert into student values(1,"202110201001","A","131.com",1),(2,"202110201002","B","132,com",1),(3,"202110201003","C","133.com",1),(4,"202110201004","D","135.com",1),(5,"202110201005","E","136.com",1);
insert into student values(6,"202110201006","F","137.com",2),(7,"202110201007","G","138.com",2),(8,"202110201009","H","139.com",2);
2)像班级表里面插入数据insert into class values(1,"Java100","very nice"),(2,"java101","very rubblish"),(3,"java102","nice");
3)向课程表里面插入数据
insert into course values(1,"chinese"),(2,"math"),(3,"english");
4)向分数表中插入数据
insert into score values(71,1,1),(99,1,3),(33.0,1,5),(98.0,1,6),(60.0,2,1),(59.5,2,5),(33.0,3,1),(68.0,3,3),(99.0,3,5),(67.0,4,1),(23.0,4,3);
在这个场景中,学生和班级是一对多的关系,学生与课程是多对多的关系
分数表存在的意义不仅仅是为了表示分数,而且还是学生与课程之间的关联表
例题1:查找A同学的成绩,查看学生对应的分数信息
我们知道:同学A学了很多课,我们就需要在学生表中获取到学生姓名,在分数表中获取到分数信息;
分析这样的问题,一定要知道想清楚要查询的信息在哪些表里面;把学生表和分数表进行笛卡尔积
select * from student,score where score.studentID=student.id;
select * from student,score where score.studentID=student.id and name="A";
having是搭配group by进行使用的
1)当前咱们的笛卡尔积记录太多, 仔细观察就可以发现,在我们的学生表和分数表里面,都存在着有关于学生ID这一列
2)按照咱们之前总结的规律,应该将这两个学生ID进行匹配,才会进行保存对应的记录,这两张表中的学生ID不相等的列咱们就会视为无效记录
先分析数据是来自于那些表,然后再进行笛卡尔积,观察笛卡尔积的结果,筛选出合法的数据,在逐步地根据需求,添加新的条件,让数量一点一点地接近预期,最后把整个列在进行筛选;
查看所有同学所选课程对应的成绩 1)select student.name,course.name,score.score from student,course,score where student.id=score.studentID and score.course_id=course.courseID; +------+---------+-------+ | name | name | score | +------+---------+-------+ | A | chinese | 71 | | A | english | 99 | | A | Java | 33 | | A | C++ | 98 | | B | chinese | 60 | | B | Java | 60 | | C | chinese | 33 | | C | english | 68 | | C | Java | 99 | | D | chinese | 67 | | D | english | 23 | +------+---------+-------+ 查看A同学所选课程对应的成绩2)select student.name,course.name,score.score from student,course,score where student.id=score.studentID and score.course_id=course.courseID and student.name="A"; +------+---------+-------+ | name | name | score | +------+---------+-------+ | A | chinese | 71 | | A | english | 99 | | A | Java | 33 | | A | C++ | 98 | +------+---------+-------+
1)进行多个条件的筛选过程中where后面的条件可以用多个and来进行分割
2)select 列名1,列名2 from 表名1 join 表名2 on 条件
from 一张表 join 另一张表 on 条件,它还实现了from无法实现的功能
2)查询所有同学的总成绩,以及同学的个人信息(同学名字)
我们不光要查询出同学的名字,还要查询课程的名字,还有课程的分数这个案例要在多表查询的基础上加上聚合查询;
我们先去查询所有同学的总成绩,根据学生的id来进行分组;
select name,score.score from student join score onscore.studentID=student.id;
select name,score.score from student join score onscore.studentID=student.id;
+------+-------+
| name | score |
+------+-------+
| A | 71 |
| A | 99 |
| A | 33 |
| A | 98 |
| B | 60 |
| B | 60 |
| C | 33 |
| C | 68 |
| C | 99 |
| D | 67 |
| D | 23 |
+------+-------+
select name,sum(score.score) from score,studentwhere score.studentID=student.id group by student.name;
+------+------------+
| name | sum(score) |
+------+------------+
| A | 301 |
| B | 120 |
| C | 200 |
| D | 90 |
+------+------------+3)查询所有同学的成绩和同学的相关信息;
同学名字 学生表
课程名字 课程表
分数 分数表
insert into course values(4,"history"),(5,"bilology"),(6,"water");
select * from student,course,scorewhere student.id=score.studentID andcourse.courseID=score.course_id;
在进行这个查询之后,我们就列出了每一个同学每一个课程拿到的分数,同时带有课程的名字
下一步要做的就是去掉不必要的列,只保留所关注的关键列,学生姓名,课程姓名分数
select student.name,course.name,score.score from student,course,score where student.id=score.studentID and course.courseID=score.course_id;
1)select student.name,course.name,score.score from student join score on student.id=score.studentID join course on course.courseID=score.course_id; 2)select 列名1,列名2 from 表1 join 表2 on 条件 join 表3 on 条件
join on还有一些其他的用法
上面说的from多个表叫做内连接,使用join on既可以表示内连接,又可以表示外连接;
select 列 from 表1 inner 表2 on 条件
select 列 from 表1 left join 表2 on 条件; 左外连接
select 列 from 表1 right join 表2 on 条件; 右外连接
create table student(id int,name varchar(20),classID int);create table class(id int,name varchar(20));
insert into student values(1,"A",1),(2,"B",1),(3,"C",2),(4,"D",3),(5,"E",4);
insert into class values(1,"java100"),(2,"java101"),(3,"java102");mysql> select * from student;
+------+------+---------+
| id | name | classID |
+------+------+---------+
| 1 | A | 1 |
| 2 | B | 1 |
| 3 | C | 2 |
| 4 | D | 3 |
| 5 | E | 4 |
+------+------+---------+
5 rows in set (0.00 sec)select * from class;
+------+---------+
| id | name |
+------+---------+
| 1 | java100 |
| 2 | java101 |
| 3 | java102 |
+------+---------+
1)但是我们在上面发现student表中有一条为班级ID为5的记录,但是在我们的class表中,并没有id为5的记录(没有一个班级ID=5);此时如果进行联合查询,就会出现问题;
mysql> select * from student,class where student.classID=class.id; +------+------+---------+------+---------+ | id | name | classID | id | name | +------+------+---------+------+---------+ | 1 | A | 1 | 1 | java100 | | 2 | B | 1 | 1 | java100 | | 3 | C | 2 | 2 | java101 | | 4 | D | 3 | 3 | java102 | +------+------+---------+------+---------+ 此时上面的学生表中的id=5记录就消失了
2)现在进行内连接或者是普通的笛卡尔级运算的操作,此时我们就发现,这里面明显少了一条id=5的同学的记录的
此时改成左外连接:select * from student left join class on student.classID=class.id;
select * from student left join class on student.classID=class.id; +------+------+---------+------+---------+ | id | name | classID | id | name | +------+------+---------+------+---------+ | 1 | A | 1 | 1 | java100 | | 2 | B | 1 | 1 | java100 | | 3 | C | 2 | 2 | java101 | | 4 | D | 3 | 3 | java102 | | 5 | E | 4 | NULL | NULL | +------+------+---------+------+---------+
1)通过刚才的例子,我们可以看出left join和inner join的区别,inner join这里面就要求的是两张表里面同时都有的数据(都有classID),才最终会显示在屏幕上;
left join以左侧的表为主,左侧表中的数据都能够在最终的表中显示出来,大不了对应的右侧的表数据填空
2)right的作用也是类似,是以右侧的表为主,尽可能把右侧的记录都列出来,在最终的表中有体现,大不了对应的左侧的表都填成null;
3)此时如果改成右外连接:select * from student right join class on student.classID=class.id;就会发现,此时又变成了四条;
create table student(id int,name varchar(30));create table score(studentID int,score int);insert into student values(1,"A"),(2,"B"),(3,"C");insert into score values(1,90),(2,80),(4,70); 1)查看表结构: mysql> select * from student; +------+------+ | id | name | +------+------+ | 1 | A | | 2 | B | | 3 | C | +------+------+ 3 rows in set (0.00 sec)mysql> select * from score; +-----------+-------+ | studentID | score | +-----------+-------+ | 1 | 90 | | 2 | 80 | | 4 | 70 | +-----------+-------+ 3 rows in set (0.00 sec) 1)进行左连接: mysql> select * from student left join score on student.id=score.studentID; +------+------+-----------+-------+ | id | name | studentID | score | +------+------+-----------+-------+ | 1 | A | 1 | 90 | | 2 | B | 2 | 80 | | 3 | C | NULL | NULL | +------+------+-----------+-------+ 3 rows in set (0.00 sec)2)进行右连接:select * from student right join score on score.studentID=student.id; +------+------+-----------+-------+ | id | name | studentID | score | +------+------+-----------+-------+ | 1 | A | 1 | 90 | | 2 | B | 2 | 80 | | NULL | NULL | 4 | 70 | +------+------+-----------+-------+ 3)进行内连接: mysql> select * from student,score where student.id=score.studentID; +------+------+-----------+-------+ | id | name | studentID | score | +------+------+-----------+-------+ | 1 | A | 1 | 90 | | 2 | B | 2 | 80 | +------+------+-----------+-------+ 2 rows in set (0.00 sec)mysql> select * from student join score on student.id=score.studentID; +------+------+-----------+-------+ | id | name | studentID | score | +------+------+-----------+-------+ | 1 | A | 1 | 90 | | 2 | B | 2 | 80 | +------+------+-----------+-------+ 2 rows in set (0.00 sec)
我们此时就发现(3,C)这条记录,只在student表中存在,不会出现在score表中;但是(4,70)这条记录只在score表中存在,没有出现在student表中;
1)select * from student join score on studentID=id;此时我们发现,内连接里面的记录就是包含两张表里面同时拥有的记录;
2)select * from student left join score on id=studentID; 左外连接就是以左侧表为主 左侧表的每一条记录都在左外连接中有所体现 右侧表中不具有的属性就直接填成空 3)右外连接select * from student right join score on id=studentID; 此时我们就会发现 右外连接就是以右侧的表为主 右侧表中的每一条记录都在结果中有所体现 左侧表中不存在的数据就算成空;
group by进行分组的时候,指定列的值,相同的记录被分到了同一个组,分组还可以按照条件进行筛选分组之前指定条件要用where,分组之后指定条件要用having
5)自连接:将自己和自己进行笛卡尔积,用来处理特殊场景;
1)自连接的本质,实际上是将行与行之间的转换关系,转化成列和列;在之前写的SQL语句,本质上是对列和列进行查找,例如 select score id name,chinese,math,english from student,如果我们查询chinese>english就会非常好找,因为这是按照列来进行比较
2)现在需要查询chinese的成绩大于english的同学
select * from student; +----+--------------+------+---------+----------+ | id | student_id | name | qq_mail | class_id | +----+--------------+------+---------+----------+ | 1 | 202110201001 | A | 131.com | 1 | | 2 | 202110201002 | B | 132,com | 1 | | 3 | 202110201003 | C | 133.com | 1 | | 4 | 202110201004 | D | 135.com | 1 | | 5 | 202110201005 | E | 136.com | 1 | | 6 | 202110201006 | F | 137.com | 2 | | 7 | 202110201007 | G | 138.com | 2 | | 8 | 202110201009 | H | 139.com | 2 | +----+--------------+------+---------+----------+select * from class; +---------+---------+---------------+ | classID | name | about | +---------+---------+---------------+ | 1 | Java100 | very nice | | 2 | java101 | very rubblish | | 3 | java102 | nice | +---------+---------+---------------+select * from course; +----------+---------+ | courseID | name | +----------+---------+ | 1 | chinese | | 2 | math | | 3 | english | +----------+---------+select * from score; +-------+-----------+-----------+ | score | studentID | course_id | +-------+-----------+-----------+ | 71 | 1 | 1 | | 99 | 1 | 3 | | 33 | 1 | 5 | | 98 | 1 | 6 | | 60 | 2 | 1 | | 60 | 2 | 5 | | 33 | 3 | 1 | | 68 | 3 | 3 | | 99 | 3 | 5 | | 67 | 4 | 1 | | 23 | 4 | 3 | +-------+-----------+-----------+ 上面就不得不按照行来进行比较了,看看studentID=1的列是否满足对应的语文成绩大于英语成绩
1)course_id=1对应的课程是chinese,course_id=3对应的课程是english,满足chinese<english,在肉眼的过程中,是按照行来进行比较的,就需要把行转化成列
就会产生大量的临时数据,两张表结合在一起之后,只有学生的id相同才是有效数据
select * from score as s1,score as s2 where s1.studentID=s2.studentID;
现在这里面还有一些其它课程的分数,是用不到的,所以我们需要在增加一些筛选条件:例如我们让s1里面的数据只保留id=3的记录,我们让s2的数据只保留id=1的记录;
保证左侧的成绩一定是语文成绩,右侧一定是英语成绩
select * from score as s1,score as s2 where s1.studentID=s2.studentID and s1.course_id=3 and s2.course_id=1;
然后我们再根据where语句增加条件进行筛选
select * from score as s1,score as s2 where s1.studentID=s2.studentID and s1.course_id=3 and s2.course_id=1 and s1.score>s2.score;
最终结果如下图:
所以自连接的关键是将行比较转换成列比较
6)子查询:本质上来说是把多个select合并成一个
将一张表的查询结果作为另一张表查询的搜索条件
1)单行子查询
一)查询一下very nice的同班同学,先去查找very nice 的班级ID,然后再根据班级ID在学生表中出查询出同classID的同学,这里面虽然分成两步,但是是针对一张表来进行查询的;
经过子查询之后,可以写成这样select name from student where class_id=(select class_id from student where name="A");(将两个SQL合并在一起)
二:查询一下chinese或者math相关的成绩信息
要先去查询chinese和math的课程ID,然后再去score里面根据course_id去查询成绩信息;
也可以写成一条语句来展现出结果:两个SQL合并在了一起
7)合并查询:把多个查询语句的结果给合并到一起了
union:不会进行去重
union all:会自动进行去重
核心就是通过union把两个sql的查询结果给合并到一起了,合并的前提是两个SQL查询到的列必须是对应的
1)查询course_id<3或者名字为chinese的课程,也可以用or来进行替换,但是使用or必须保证是针对同一张表的来指定的多个条件来进行查询
2)但是合并查询是可以针对不同的表
视图:
定义:视图就是一种虚拟存在的表,视图中的数据实际上并不在数据库中存在,行和列数据来自定义试图查询中所使用的表,并且是在使用视图的时候动态生成的,
通俗的讲在视图中只是保存了查询SQL的逻辑,但是不会保存查询结果,所以在创建视图的时候,主要的工作就落在了创建这条SQL的查询逻辑上了,视图所保存的数据是来自于后面的select语句中;
1)创建视图:create(or replace)(可省略)view 视图的名字 as +查询的SQL语句
SQL语句的查询结果是视图中的数据来源
比如说查询的SQL语句是这样的:(select id from User where userID<1)
select查询的表就是这一张视图所包含的基表
2)查询视图:show create view 视图名字(查看创建视图语句)
或者是select * from 视图名字
3)修改视图:create or replace view 视图的名字 as 查询语句
(和创建视图的时候的SQL语句是一样的),创建视图的时候or replace可以不加,但是修改视图的时候or replace必须加
查询语句可以变成:select id,name from user where userID<10
或者这么修改也是可以的:alter view 视图的名字+SQL语句
4)删除视图:delete view if exists 视图名字
1)create table student(id int,username varchar(30),password varchar(50),qq varchar(40)); //创建一张表,向数据库表中插入数据 insert into student values(1,"A","B",123456); insert into student values(2,"N","C",1277); 2)create or replace view stu1 as select * from student where id<20;//创建视图 2.1)create or replace view 表示要创建一个视图 2.2)stu1表示的是要创建的视图的名字或者要替换的视图的名字 2.3)as后面加上的是这个视图里面所进行封装的数据是什么,我们要进行封装的数据就是select查询最终要返回的数据 3)向视图中插入数据:insert into stu1 values(21,"C","1",12345);select * from student; +------+----------+----------+--------+ | id | username | password | qq | +------+----------+----------+--------+ | 1 | A | B | 123456 | | 2 | N | C | 1277 | | 21 | C | 1 | 12345 | +------+----------+----------+--------+select * from stu1; +------+----------+----------+--------+ | id | username | password | qq | +------+----------+----------+--------+ | 1 | A | B | 123456 | | 2 | N | C | 1277 | +------+----------+----------+--------+
虽然使用insert语句是向视图中插入了一条id=21的一条数据,表面上来看系统没有发生报错,但是实际上他是不符合视图插入的逻辑的,视图中插入的数据应该是小于20的,但是实际上插入的这条数据是插入到基表里面了,此时如果进行查询视图就会出现无法查询这一条id大于20的数据,此时就会发生我通过视图插入的数据,结果在视图中无法查询到,这显然是不合理的,所以可以加上检查选项来显示检查条件;
1)当我们执行上面的SQL的时候,id超过20的记录看似是被插入到视图里面了,实际上是插入到原表里面了,但是实际上查询视图的时候,是无法进行查询到视图中id=20的记录的
2)因为我们在进行创建视图的时候,我们已经指定了视图创建的条件,所以我们无法成功进行插入;
3)这样做显然是不算太规范的,id>20就不能插入到视图里面了,但时间查询项可以自动地帮我们检查新插入的数据是否满足视图刚刚创建时候的条件,如果插入的数据和创建视图时候的条件是违背的,此时就插入失败
检查选项:
当我们使用with check option子句来进行创建视图的时候,MYSQL会通过视图检查正在更改的每一行,例如说向视图中进行插入,更新,删除以使其符合视图的定义,就是符合视图创建的时候所指定的条件,MYSQL允许基于一个视图创建另一个视图,他还会进行检查依赖视图中的规则以及一致性,为了进行确定检查的范围,MYSQL提供了两个选项,cascaded和local,默认值是cascaded,如果说不加上这个语句,MYSQL就不会检查插入的语句是否符合视图中的选中条件
create or replace view stu1 as select * from student where id<20
当仅仅执行这条语句的时候,既可以插入大于20也可以插入小于20的数据
1)此时假设我们在创建视图的时候指定create or replace view stu1 as select * from student where id<20 with cascaded check option,此时我们再向视图中插入id>20的记录,就会对自己视图的条件进行查询是否id小于20,发现id>20,那么视图插入,也就是向基表中插入失败
2)现在比如说创建两个视图:
2.1)create or replace view stu1 as select * from student where id<20
2.2)create or replace view stu2 as select * from stu1 where id>10 with cascaded check option
当我们向表2中插入数据的时候,不仅会检查插入的数据是否满足表2中的where语句,他还会进行检查表1中的where语句是否符合条件,相当于是在表1中添加了一个with cascaded check option,也就是说我们进行插入id<10或者id>20的数据都会失败
2.3)现在我们基于第二个视图创建第三个视图: create or replace view stu3 as select * from stu2 where id<=15,这个时候没有加检查选项;
2.3.1)insert into stu3 values(19,"A","12345","1233"),此时就会插入成功,因为此时第三个视图没有加检查选项,那么不会进行检查第三个视图的条件,视图检查会向上传递,但是第二个视图加上了选项,所以会检查第二个视图的条件,还会检查第一个视图
2.3.2)insert into stu3 values(23,"A","12345","1233"),此时就会插入失败,它还是不会检查v3视图的条件,但是他是依赖v2视图的,v2视图会自动检查自身的条件以及它所依赖的视图的条件
所以说cascaded会检查自己的条件和调用链上面的所有条件,当进行插入语句的时候,要满足上面视图的所有的依赖条件
1)cascaded:比如,v2视图是基于v1视图的,如果在v2视图创建的时候指定了检查选项为 cascaded,但是v1视图创建时未指定检查选项。 则在执行检查时,不仅会检查v2,还会级联检查v2的关联视图v1
2)local:比如,v2视图是基于v1视图的,如果在v2视图创建的时候指定了检查选项为 local ,但是v1视图创建时未指定检查选项。 则在执行检查时,只会检查v2不会检查v2的关联视图v1
3)只要由
cascaded修饰
,那么上面的local无用(没啥用)with local check option会进行查询当前视图所依赖的视图,如果当前视图或者是所依赖的试图有检查选项with check option,就检查,否则就不对条件做检查
视图的检查选项指的是当进行对视图中的数据进行插入,进行更新,进行删除的时候,检查选项会进行检查我们所操作的数据是否符合视图定义时候的条件
视图的更新:什么样子的视图可以进行更新呢,什么样子的视图又不可以进行更新呢?
如果要使视图可以可以进行更新,那么必须满足的条件是视图中的行数据和基础表中的行要满足一一对应的关系,如果视图包含以下任何一项,那么该视图不可以进行更新
1)当我们使用聚合函数或者窗口函数(sum(*),min(),max(),count(*));
2)distinct,group by,having,union,union all;
3)举个例子:create view stu_count as select * from student;
insert into stu_student values(10);
当使用聚合函数进行创建一个视图的时候,视图中的数据和表中的数据不是一一对应的,所以当前视图不可以进行插入也不可以进行更新,视图中的行必须和基表中的行是一一对应的关系
视图作用:
1)视图不仅可以简化用户对数据的理解,也可以简化他们的操作,那些被经常使用的查询可以定义为视图,从而使用户不必为以后的操作指定全部的条件,连表查询的结果可以封装到视图里面,以后进行连表查询的时候直接查询对应的视图就可以了,通过视图简化连表查寻
2)安全:数据库可以进行用户授权,但是不能授权到数据库特定的行和列上面,但是通过授权来控制MYSQL中的字段,MYSQL是做不到的,最基本的单位是表,通过视图用户只能查询和修改他们所能见到的数据,假设如果我想要让某一个人只看到学生表中的ID和name字段,不想让他们看到密码信息,那么此时我们就可以创建一个视图,这个试图在进行查询的时候只是包含id和name,那么这个用户操作视图的时候,只能看到用户的ID和姓名,并不能看到学号,保证敏感数据的安全性
学生-课程-(学生,课程表)--多对多的关系