mysql笔记(基础篇)

1. 数据库概述

1.1 什么是数据库

  数据库(Database:DB),顾名思义是数据的仓库,所以数据库具有存储的作用,但是也不能单单存储,还得方便日后的查询,所以数据要有一定的组织性(或者叫管理性),那么由"谁"来组织?,选对"谁"对数据的检索和存储都会有影响,比如:在检索方面,你东西随便乱扔,没有组织性,查找起来是不是很麻烦。而在存储方面,类似桶装水,不单单桶哦,洗脸盆也可以装水,但是这两个装的水容量一样吗?还有,手是不是可以接水,容量不用说,肯定比不上桶装水,并且用手接水是不是会漏水呀,为啥,手有缝隙呀,是不是不安全。所以,选对"谁"是不是很重要,而在这,这个"谁"就是表(table),它跟数组,集合一样都属于数据结构。
  那什么是数据结构?官方解释为:数据结构是计算机存储、组织数据的方式。进行等价替换为:表是计算机存储、组织数据的方式。
  从前两段可以看出,数据库的存储和管理,其实是表的存储和管理,也就是说,数据库包含了表,表是数据库的基本组成单元。所以重点来了,总结:数据库是按数据结构来组织,存储和管理数据的仓库

1.2 为什么学习数据库

  可能有人会说,文件不也可以保存信息吗?而且我们在java学习阶段中接触过io流,用io流来操作文件,进行读写,不也好,那为什么还要用数据库,没有错,但是如果用文件来保存数据会存在如下几个缺点:
  1) 文件的安全性问题。
  2) 文件不利于查询和对数据的管理,比如管理,它不像数据库,会制定各种完整性约束,很难保证数据的有效,完整,正确与一致性。
  3) 文件不利于存放海量数据,所以文件适合存储规模小,管理简单的数据。
  4) 文件在程序中控制不方便,并且如果用io流这种原生的java方式来操作文件,会显得异常麻烦。

注意点:
  1)数据库本身也是一种文件系统,所以数据库肯定具有持久性,区别于像数组,集合这种非持久性的存储。
  2)数据库的命令执行底层其实也是一种io流的执行过程,只不过数据库把它封装了,因为数据库专门提供了一种语言,你只需要学习它给你提供好的语言,轻轻松松就可以把数据取出来。
  3)虽然数据库也需要用文件来保存数据,而且是很多文件,但是对于使用数据库的人来说,操作的只是数据库给的接口(语言),至于这些数据如何被读写到这些文件上,并对这些数据进行管理的,我们并不关心,这个是数据库程序自己要操心的事。
  4)想想我第一段说的装水例子。

1.3 数据库的分类

  根据组织方式的不同,数据库可分为关系数据库(RDBMS)和非关系型数据库。关系型数据库的代表存储模型(关系模型)为表,也是本章的主角。往后,我们对数据的一切操作,其实就是对表的操作,所以,我们要对表有个正确的认识。
  总结:关系型数据库是依据关系模型来创建的数据库
  注意,数据库里面是包含多张表的,而多张表之间或多或少会存在某种关系,由浅到深分别是一对一,一对多,和多对多。那么针对这种表与表之间存在的关系,我们就称它为关系型数据库。

1.4 表的认识

  表,就是所谓的二维表,也就是有行有列,在本章节当中,列就是字段,属性(column|field),行就是记录,元祖(record|row)。如下:
在这里插入图片描述

图1:

  映射到java类中,列就是属性,属性是不是有数据类型,那么也就是说,表中的字段也具有数据类型,可以对它这一列的数据做好约束。而每一行就是对象,表名就是类名啦。
  我们现在既然认识了数据库中的表,那么就要把表创建出来,在创建之前我们肯定会去设计表,比如这个表的表名叫什么呀!有哪些字段呀!字段分别是什么类型呀!并且有哪些约束呀!其实,只要字段确定了,
表的结构也随之确定,表的结构都确定了,表不就出来了吗?
  我们现在即认识了数据库中的表,也知道怎么形成一张表,那么在设计表的时候我们也会慢慢的接触到一种东西叫三范式,反正现在要知道的就是在设计表的时候注意分类,比如学生信息,我们就把它统一放在一张表中,教师表我们也统一放在另一张表中,千万不要混杂在一起就是了。

1.5 mysql的准确叫法

  前面我们都在说数据库,虽然没有提及mysql,但是也恰恰说明了mysql就是一个数据库,没毛病,但是,mysql的准确叫法不叫数据库,而叫数据库管理系统,英文名为Database Management System,简称DBMS,它是一款软件,需要下载安装,安装成功后即可以打开软件,在里面创建一个个数据库,然后选择某一个数据库,进行创表,完成对我们大量数据的管理。这就是这一款软件的作用,而mysql只是其中之一,像其它软件oracle,SqlServer,db2,sybase等也是一款对数据管理的软件,只不过本章它们不是主角,mysql才是主角,所以应该先把目光看向mysql,对于数据库只是我们一种偷懒的叫法,学习mysql,说白了就是学习一款软件,学习如何在这款软件当中创建数据库,如何创建表,如何对表中的数据进行管理。而这一系列的学习都离不开这款软件给我们提供的语言,叫sql语言,DBMS就是通过执行我们的sql语言去操作数据库以及表的。注意:准确的说sql语言是一个标准,它适用于所有的关系型数据库,就是前面所说到的oracle啊,db2啊这些,所有我们学完mysql语言,然后再学习其它的比如oracle就会轻松一点,只是不同的软件具有它们不同的方言而已。
  往后,在正式开始学习语言之前,我们都会先连接上(启动)mysql服务,就相当于打开了数据库软件,这样我们就能在软件里创建库和表了,而我要说明的,就是这里的mysql服务就相当于DBMS。还是那句等价替换,mysql服务会帮我们执行sql语言。

1.6 总结

  1.数据库简称DB,是一些关联表的集合,但表有存数据,所以又可以说它是一个存储和管理数据的仓库,保存了一系列有组织(因为这数据我们要重复利用,所以要有一定的组织性)的数据。
  2.数据库管理系统,又叫数据库软件,简称DBMS,数据库DB就是通过DBMS创建和操作的容器,它能够为数据库提供数据的定义,建立,维护,查询和统计等操作功能,并完成对数据完整性,安全性进行控制。常见的数据库管理系统有mysql,oracle,db2,sqlserver。
  3.SQL,是一种用来跟数据库通信的一门语言,说准确点,就是SQL命令。
  4.DBMS包含多个DB,DB包含多张表。
  5.DBMS通过执行sql命令来操作DB中的表,从而达到对数据的增删改查。
  6.表是由许多的行(非表头)和列(表头)组成的,列为字段,比如姓名,行为数据,比如小明。所以数据是以表格的形式组织。

2. SQL基本概念

2.1 什么是SQL

  SQL上面简单的说过了。其实就是定义了操作所有关系型数据库的规则(只要是关系型数据库,都可以被sql操作)。

2.2 SQL分类

1. DDL(Data Definition Language)数据定义语言,用来定义数据库对象:数据库,表,列等。关键字:create,drop,alter,show等。
2. DML(Data Manipulation Language)数据操作语言,用来对数据库中表的数据进行增删改。关键字:insert,delete,update等。
3. DQL(Data Query Language)数据查询语言,用来查询数据库中表的记录(数据)。关键字:select,where等。
4. DCL(Data Control Language)数据控制语言,用来定义数据库的访问权限和安全级别,及创建用户。关键字:grant,revoke,commit,rollback等。

2.3 整体流程

在这里插入图片描述

3. DDL命令

  上面已经知道了,DDL是操作数据库和表的,也知道它们的几个关键字,那么现在就开始来学习DDL命令吧!
  首先,要安装数据库软件,这里用黑界面窗口来学习。安装就不说了,省略,默认已经安装成功!并且连接名也连接成功!当然,启动mysql服务和连接命令说一下,如下:

  • net start mysql;启动服务。关闭服务就把start改成stop,注意这里的mysql是服务名,它不是固定的,一般我们给它起的服务名就叫mysql。我们可以打开任务管理器,选择服务那项,就能找到你的mysql服务名,如果该mysql不是你安装的,是别人安装的,那别人有可能把服务名改了,不叫mysql,叫mysql80都有可能,反正就找mysql字眼的就行。
  • mysql [-h 连接名 -p 端口号] -u 用户名 -p密码;中括号表示可以省略(注意后面一旦见到中括号,就表示省略),如果省略,默认连接本地,并且端口号默认为3306。


  只要出现mysql>就表示连接成功,我们已经进入mysql命令行界面了,如果要退出mysql命令界面,输入exit;即可。现在可以开始写sql命令了。

库的操作

3.1 查询有哪些数据库

  命令:show databases;,表示展示所有数据库。注意database是s结尾。
在这里插入图片描述  发现默认就已经存在这四个数据库了。了解一下即可,最后不要去动它,但test可以随意动,它里面没有一张表。
  前面说过了,数据库本身也是一个文件系统,所以上面列举出来的四个数据库在磁盘上也有它对应的文件,那么在哪里呢?打开my.ini文件,然后定位到datadir属性即可,它的值就是文件所在的地址。
  有一点,要说一下,以上结果是不是以表格的形式出现的,像这种表格我们称为虚拟表,往后提到虚拟表要知道表示什么意思,就是它不是实实在在存在的表,也不是我们创建出来的,而是命令执行后给我们的结果而已,只不过它的表达方式是用表,其它所有命令都一样。那么反过来,是我们创建的表就是原始表
  在上面那条命令上,我们再扩展一下,比如上面是查询所有的数据库,如果我要查询指定的数据库,那命令就是show databases like '数据库名';,注意它可以部分匹配或者具体匹配,更详细的用法我放到后面再说。
在这里插入图片描述

3.2 创建数据库

  命令:create database 数据库名。创建单个数据库不需要加s。作用:在磁盘上划分一块区域用于数据的存储和管理
在这里插入图片描述  是不是多了一个db1数据库。然后你还可不可以再创建一个数据库,也叫db1呢?肯定是不可以的,不然它会报错,所以,上面的命令我们要改造一下,如下:
在这里插入图片描述  是不是并没有报错。exists为存在的意思,就是说如果不存在db1,就创建,提高了容错性。

3.3 查看对应数据库的字符集(或叫查看数据库的定义)

在这里插入图片描述  注意/**/为注释,这种注释叫多行注释,说到注释,除了多行注释,还有两个单行注释,分别是#--。然后注意注释里面的内容,其中latin1是字符集(如果是该字符集,因为它是不支持中文的,所以添加中文数据的时候会乱码),可以修改的,比如修改为utf8,但这是下一节的事了。注意当时在创建数据库的时候是可以顺便指定使用哪个字符集的,如下命令:
CREATE database db1 character set gbk;
  最后一点,就是有没有发现,创建语句是这样的:CREATE DATABASE `db1`,这里一共有两个点,就是前面的create database都为大写,那是因为mysql命令不区分大小写,也就是大小写都可以。第二点,就是db1的外面打上了反引号,这个反引号就是键盘Esc下面的那个键盘,那么为什么要打上反引号呢?这其实得看你起的数据库名怎么样了,如果你的数据库名含有特殊符号,mysql的关键字,包括空格,如果不打上反引号就会报错,反之,如果你起的数据库名很正常,没有任何特殊符号,可以不打上反引号,它不仅仅适用于数据库名,包括后面查询表的字段时也适用,所以它主要是用来区分关键字和字段的。
纠错:
  上面说/**/为注释,其实是对的,错就错在它为这个格式:/*!40100 xxx */,这个40100表示4.1.00版本,也就是如果当前版本大于4.01版本就执行,比如如下:
在这里插入图片描述  总结:/*!40100 ...*/这部分注释会被MySQL执行,表示服务端版本号大于4.1.00时会被执行。
  也就是说,正确的写法是:CREATE DATABASE `db1` DEFAULT CHARACTER SET utf8;(该语句的DEFAULT翻译过来是默认的意思,这个单词可以省略不写)。对了,在查看的时候结尾也可以加上\G,比如:show create database db1\G,\G表示纵向排列。

3.4 补充

  这里的补充,是要对创建数据库命令的一个补充,在上述,完整的命令格式是:create database 数据库名 [character set 字符集]。中括号是可以省略的,你省略,就表示用默认的字符集,除了它,还有一个,叫collate(核对),它表示校验规则,什么意思呢?比如我在这个数据库里创建了一张表,并且在表里增加了两条数据,一个是小写的字母a,另一个是大写的字母A,按道理说,一个大写一个小写,它们应该不是同一个值,也就是a≠A,但当我们去查询的时候,比如我要查询小a,如下(先忽略创表,查表语句):
  注意,当前aaa数据库的字符集被我改为utf8。很明显,它把大A也给查出来了,这说明它的默认校验规则是utf8_general_ci,如果我不想把大A也查出来怎么办呢?如下:
总结:

    1) 如果字符集为utf8,默认的校验规则是utf8_general_ci,可通过一条命令查看:show collation;如下:
  注意上图Yes表示该行为默认值。补充:查看有哪些字符集:show character set;
    2) utf8_general_ci不区分大小写,utf8_bin区分大小写。
注意:
    校验规则的改变也会导致你对数据进行排序的改变,所以我们最好用默认的校验规则,不要改它。

3.5 修改数据库

在这里插入图片描述  数据库名一般不会去修改,最多只会去修改它的字符集。除了字符集,还有collate,上节讲过了。
  上图的命令还可以这样写:

alter database db1 charset=utf8;

概念:
  不管是charset,还是collate,它们两个都属于表选项,表选项就是你对表的整体设定,比如charset就是来定义数据的存储方式,校验规则就是用来比较数据的。那么表选项还包括哪些呢?在我看来,它还有三个,分别是存储引擎(又叫表类型)engine,自动增长auto_increment,说明comment。也就是说,一共有五个,然后另外三个在后面慢慢学习就懂了。

3.6 删除数据库(别乱删)

  命令格式:drop database 数据库名称;
  注意,为了删除命令执行不报错,要加上if exists判断是否存在。

3.7 使用数据库

  使用数据库表示当前在数据库之外,还没有进入到数据库里面去,所以就无法对数据库里面的数据进行操作,所以,要先选择你要进入到哪个数据库,因为数据库有多个,所以如下命令:
在这里插入图片描述  当然了,在执行以上命令之前,可不可以先查询一下我当前所在哪个数据库,当然是可以的,如下命令:

select database();

如果没有进入任何的数据库,就是NULL。
在这里插入图片描述如上就表示已经进入db1数据库了,你可以理解use即可以说是进入,也可以说是切换。如果你不想进入,也可以用这条命令格式:select * from 数据库名.表名

表的操作

3.8 查询某个数据库中的所有表

  以上面查询数据库为模板,是show databases,那么照葫芦画瓢,查询所有表会不会是show tables,但前提是你要进入某个数据库,以db1为例,如下:
在这里插入图片描述  诶,show tables好像真的是对的,因为它并没有报错,反而给出了一个简单的单词Empty,空的,那没错了,如果要想验证,请进入数据库名叫做mysql的,use mysql,再去show一下即可证明。回过头来,看看结果,是不是空的,因为我们才刚刚创建了db1这个数据库,还没创建表呢,肯定是空的,那么问题来了,如何创表?

3.9 创建表

  一样,照葫芦画瓢,创建表就是create table 表名,但是,还没完,为什么还没完?你想,表是不是会有字段呀,所以,你在创建表的同时,是不是要指明字段信息呀!这样才是一张完整的表。
  同时,注意一下,表的字段跟我们学java时学的属性其实是一个样的,是不是也从侧面说表名就是java中的类名?好像有道理哦!而且我在第一章的时候也好像提到过。
  那么学java的时候我们有没有创建过学生类?学生类有哪些属性,是不是有学号,姓名,生日,性别,班级,总分数这些?那好,映射到表里面,是不是有画面了?好,知道要创建的表是学生表,那么表名取student,可不可以?然后属性分别是number,name,birthday,sex,class,total_score。

create table student(
	    # 写列名以及列的类型
);

  如上,表的字段,或者叫列名,是不是写在括号内的,然后呢,因为它跟学java时定义类的情况很相似,所以,是不是不单单要写上列名,还得有类的类型,所以,如下:

create table student(
	    列名1 数据类型1,   #完整的格式是:列名1 数据类型1 [约束] [表选项]
	    列名2 数据类型2,
	    ...
	    列名n 数据类型n
);

  这时候我们发现,它跟java定义类的属性好像有点不太一样,哪不太一样?是不是顺序颠倒了?没错,这点要注意,顺序是颠倒的。还有,每个列写完用逗号隔开,但是写到最后一个列的时候,就不要加逗号了。
  好,既然语法格式都已经出来了,那么写出来不就是so easy的事,但是呢,mysql的数据类型跟java的数据类型稍微有点不同,但是没关系,数据类型我们放到后面讲,所以,我们现在的关注点就不要放到数据类型上了,但是我会先提一提。
在这里插入图片描述
完整代码如下:

use db2;
create table student(
		id int not null primary key auto_increment, 															
		number int,
		name VARCHAR(32) comment '学生姓名',#comment表示为字段备注是什么意思
		birthday DATE, 
		sex VARCHAR(8),
		class VARCHAR(32),
		total_score DOUBLE(4,1),
		insert_time TIMESTAMP 											
);

show tables一下:
在这里插入图片描述是不是就不为Empty了。当然,表的创建如果也想判断表是否存在,如下:

create table if not exists student(
	    # 写列名以及列的类型
);

在创建表的时候,可以指定字符集,校对规则和引擎,否则都拿默认的,如下:

create table if not exists 表名(
	 # 写列名以及列的类型
)character set 字符集 collate 校对规则 engine 存储引擎方式;#创建表的时候才会指定引擎,创建数据库不会。

在前面的建表语句上我们说过自动递增auto_increment,它默认是从1开始递增的,如果我要它从2开始递增的话那可以这样写,如下:

create table if not exists 表名(
	 # 写列名以及列的类型
)auto_increment=2;

上面语句有个叫character的可以被charset代替之外,剩下的像collate,engine,auto_increment是不是都是表选项呀,观察它们的格式,是不是:

create table if not exists 表名(
	 # 写列名以及列的类型
)表选项[=];

注意,等号是可以省略的,在上面我们说了四个,是不是还少了一个呀,是不是comment,那么按照以上格式,是不是就明白怎么写了呀,如下:

create table if not exists 表名(
	 # 写列名以及列的类型
)comment='说明';

3.10 查看表结构

命令:desc 表名;或者desc/describe/show columns from 表名;,意味着要返回每列的具体信息。其中desc的全称是describe。
在这里插入图片描述
解释:
1. NULL:表示该列是否可以存null值。
2. Key:表示是否建立索引,最常见的有主键索引,唯一索引之类的,图上PRI表示主键索引。
3. Default:表示该列是否有默认值,有的话是多少。
4. Extra:翻译过来为附加的,常见的有auto_increment。


除了该命令可以查看表结构,还有一条命令可以查看,命令就是show full columns from 表名;,这条命令看的更加全。

3.11 查看表的建表语句

在这里插入图片描述  可以看到表的字符集为utf8,因为数据库也为utf8,我们刚刚是不是改过了。

3.12 表的删除和数据清除

  因为库跟表的操作属于DDL,而DDL的删除关键字是drop,所以,如下:

drop table 表名;

同样可以加if exists来判断表是否存在。注意drop是把整张表都干没了,但是如果我想保留表的结构,只把数据给清除掉,也是可以的,如下命令:

truncate table 表名;

如果想一次性删除多张表,那就用逗号隔开,如:drop table 表名1,表名2,表名3...

3.13 复制表

命令:create table stu like student;。表示创建的stu表跟student表是一样的。
在这里插入图片描述对student表进行复制,取名为stu。注意,复制表只会复制表的结构,不会把数据也复制过来。如果即想复制结构也想复制数据怎么办,如下:

create table stu select * from student;#如果已经是复制好student表了,那就是insert into stu select * from student;

这样就会把数据也复制过来了,是stu表复制了student表,后面的select * from student是查询语句,也就是查询student表里的所有数据,数据查询我们放到后面再讲。如果是要复制student表的部分数据,也跟查询命令有关,也就是如下:

create table 新表 旧表的查询语句;  #旧表的查询语句有全部查和部分查,决定了复制后的新表的数据多少。

既然复制表能够决定你是要复制全部数据还是部分数据,那么结构是不是也可以复制部分结构,像第一条命令那个,like student,它是把student里的所有字段都复制过来,但是我只想复制部分字段怎么办?比如我只想复制number字段和name字段,如下:

create table 新表 select number,name from 旧表 where 0;

一样,旧表的查询语句select number,name from 旧表 where 0;,对比上面,发现select的后面一个是*和number,name的区别,*代表查询所有字段,这里先提一下,如果想查询部分字段,就得跟我上面那样写,多个字段逗号隔开,到时候我们讲数据查询的时候你就明白了。然后后面的where接的是条件,0代表false,false就是不查询,说白了就是我不要数据,只要结构。
  总之一句话,我create table stu后面的查询语句执行得到的虚拟表格就是stu的表格。请细细品位。

3.14 修改表

修改表名

alter table 表名 rename [to] 新的表名;

或者:

rename table 老表名 to 新表名;  #这个to不能省略

修改表的字符集

alter table 表名 character set 新的字符集;

或者:

alter table 表名 charset[=]新的字符集;

注意charset是表选项的其中之一,那么其它的是不是也类似这样改呢?如下:
没报错,说明表选项的值确实是这样改的,不信可以show create table 表名一下,所以我们得出如下结构:

alter table 表名 表选项[=]新值;

添加一列

alter table 表名 add 列名 数据类型;

在这里插入图片描述上面的命令如果写全了是这样的:

alter table student add column address varchar(32);

也就是说,多了个字母column,column翻译过来为列,可以省略不写。

更改列名称和类型 或 修改类型

在这里插入图片描述以上修改命令可以把名称和类型一并修改(当然,也包括约束,接在类型的后面,格式:alter table 表名 change 旧列名 新列名 新字符格式 新约束),如果只想修改名称,类型不变,那么就写跟原来一样的类型就好了,也就是说,如果你想修改列名,没办法,得顺带把类型也带上。但是如果只想修改类型,不修改名称,有下一个命令,如下:在这里插入图片描述最后,不管是change还是modify后面可以接column,但是一般都是省略不写,包括下面我要讲的删除列。

删除列

在这里插入图片描述删除某一列不需要带类型,只有添加和修改的时候才会带类型。

修改/添加默认值

alter table 表名 alter 字段 set default;

调换列的顺序

  字段顺序命令desc student即可查看,如上图有,id为第一位,然后下一个为number,name等。
  我们把number调到birthday之前,如下:
在这里插入图片描述命令after表示在什么什么之后,如上是在number之后,也就是说birthday要修改到number之后。其实就是修改表的类型语句后面追加after关键字,除了after,还有first,first表示在第一位,注意first后面不能接其它列名。好,如果我要把性别sex移到第一位,如下:
在这里插入图片描述注意还回原位,也就是sex排在name之后。

4. 数据库的备份与恢复

4.1 数据库的备份

  我们备份数据库,其实就是要备份数据库表中的数据,命令如下:

mysqldump -u 用户名 -p 数据库 > 数据库存放路径(文件一般取名为:数据库名.sql/或者是.bak结尾的也行)

  注意-p参数后面是可以接密码的,不写的话当你回车执行这条命令时,它会让你输密码。同时还要注意mysqldump是mysql给我们提供的一个应用程序,也就是mysqldump.exe,这个程序就是专门来为我们备份的。如下:
在这里插入图片描述
  这时候在你的d盘下就会有db1.sql文件啦,别人拿到你的sql文件,就可以创建出跟你一模一样的的库和表来了,因为该文件里会有你的建表语句和插入语句这些,总之你可以看到很多信息。如下就是我截取的一部分:
在这里插入图片描述
  是不是看到了/*!40101 ... */格式,上面说过了,它不是注释,而是大于4.1版本的时候才会执行里面的命令。

4.2 数据库的恢复

  假设数据库被我误删了,但是有了前面的备份,我们就可以把它恢复过来,如下:

4.3 注意

  1. 如果我只想备份数据库里的某几张表,怎么办?比如数据库里有4张表,但是我只想备份其中两种表即可,那么命令如下:

mysqldump -u 用户名 -p 数据库名 表名1 表名2 > 数据库存放路径

  2. 如果我想同时备份多个数据库怎么办?命令如下:

mysqldump -u 用户名 -p -B 数据库1 数据库名2 > 数据库存放路径

  注意,我推荐如果只是备份数据库的时候最好加上-B参数,因为它为我们备份出来的文件里包含了建库语句。所以下次恢复的时候我们还需要在恢复之前先创建一个空库吗?是不是就不用了呀!

5. DML命令

  DML是对表中的数据做增删改的。所以它相当的重要,要重点掌握。

5.1 添加数据

  语法:insert into 表名(列名1,列名2,...列名n) values(值1,值2,...值n),按照该格式我们可以添加数据看看,如下:
在这里插入图片描述  在书写列名时,id和insert_time是不用写的,因为id是自增的,insert_time是系统自动给的,所以我们不用管,也不用给它赋值,values后的每个值跟前面的列名是一一对应的,这点要知道。最后列名的顺序不需要跟原表的顺序一致,同时个数也可以不一致,比如你只要插入name,其他不要,就是:

insert into student(name) values("小成"); 

  如果是插入全部的数据,就是不管是性别,学号,姓名,生日什么的都填上,那么一个一个写太麻烦了,就可以完全省略,但要注意values括号里值的顺序就要跟原始表上的字段顺序一致,一一对应。

insert into student values(NULL,3301,"2020-02-20","小陈","男","高三六班",500,NULL);

  注意因为列表省略的是(id,number,birthday,name,sex,class,total_score,insert_time),所以在values中与id对应的以及跟insert_time对应的值为null即可,到时候,它会自动识别这两个null,这两个null只起到占位的作用,并不是真正值就是null。
总结:
 1. 列名和值要一一对应。
 2. 如果表名后不定义列名,则默认给所以列都添加值。
 3. 这点要注意,日期要加上双引号
补充:
  这种添加方式叫批量添加,就是可以一次性添加多条记录,如下格式:

insert into 表名 values(记录1),(记录2),(记录3)

注意点:
  在添加数值的时候也可以打上引号,因为mysql会隐式的去转换。所以,当你不知道哪些要添加引号时,可以全部都打上引号。

5.2 删除数据

  语法:delete from 表名 [where 条件],比如你要删除id=2的数据,如下:

delete from student where id=2;

  注意,如果不加条件,则删除表中所有记录。如下:

delete from student;

  但是这个不推荐,因为它有多少条记录就会执行多少次删除操作。推荐使用truncate,命令:truncate table student;前面已经提到过了。它的流程是先删除表,然后再创建一个一模一样的空表,最多两条sql语句,效率更高。
delete与truncate的区别:
  1. 效果一样,但truncate速度更快。
  2. delete可以带where条件,删除更加的灵活。
  3. delete可以返回被删除的记录数,而truncate返回的是0。
  综合上述,如果是全表删除,推荐truncate,如果是删除某一条,那就用delete。
注意点:
  有些人可能会受到select * from 表名的影响,把删除数据的sql语句写成delete * from 表名,要知道,delete删除语句是把一条记录给删了,不存在把某一字段的数据给删了,所以,delete后不要加*号。

5.3 修改数据

  语法:update 表名 set 列名1=新值1,列名2=新值2,...[where 条件]。注意命令中的新值1,或新值2不一定是具体的值,还可能是表达式,比如函数,加减乘除这些。
在这里插入图片描述  比如小陈发现学号和生日写错了,学号不应该为3301,而是3601,33开头的是高三三班的,然后自己出生于2000,那么如下:
在这里插入图片描述  修改的时候一定要加条件,否则会将表中的数据全部修改。反正对数据的删除和修改一定要谨慎

6. mysql乱码问题

添加数据乱码:

先创建一个数据库:
在这里插入图片描述我们发现创库的字符集为latin1,这个字符集是不支持中文的,如果用这个库来建表,那么它的表默认也是latin1,如下:
在这里插入图片描述因为表的字符集是latin1,所以它会用latin1去解码,但是为了保证最终查出来的数据不被乱码,就得保持解码和编码的字符集是一样的,所以我们要查询一下它的编码方式是多少,如下:
在这里插入图片描述也就是说,在客户端(client)这边是用gbk字符集来编码的,而表这边则要求我们要用latin1去编码,所以我们要更改我们的编码字符集,如下:

set names latin1;

这样就可以插入中文了。注意这条命令在每次重新连接时,都要再执行一遍。同时如果表变成utf8,那就是set names utf8。

sql文件调整:

  像前面学过的数据库备份,是不是可以获得一个sql文件,打开这个sql文件,就会发现有这一行,如下:
在这里插入图片描述  这可不是注释哦,前面说过了,也就是说这条语句是会执行的,它的意思是设置插入的字符集为utf8,那么为保证不会乱码,表的字符集也应该为utf8,其实道理都是一样的。

7. mysql数据类型(又叫列类型)

  数据类型是用来约束数据的,比如生日只能写日期,不能写其它比如数字1,那么就要用数据类型加以约束才行。
  它可以分为数值类型,字符类型,日期类型,和复合类型。如下:
在这里插入图片描述

数值型

7.1 整数型

整数类型字节范围
Tinyint1有符号:-128~127
无符号:0~255
Smallint2有符号:-32768~32767
无符号:0~65535
Mediumint3
Int,integer4有符号:-2147483648~2147483647
无符号:0~4294967295
Bigint8

  以上所列出的类型从上到下依次增大,相当于java的short,int,long。字节越大,范围也就越大,范围是通过字节换算过来的,范围还分为有符号和无符号之分,这跟二进制的知识有关,此处不多说,不过要知道像Tinyint的有符号范围是-128到127,表示在该类型的约束下,数字只能在-128和127之间,不能超出,比如128就是不可以的,因为它超出了127,其它的同理。就以tinyint为例,如果是有符号(有符号是默认的)的话,那么你添加进去的数据就必须在-128到127之间,比如如下:
在这里插入图片描述  对比添加进去的数值和最终的输出结果,发现添进去的虽然没报错(也不能说没报错,毕竟它给出了1个警告),但是最终的结果输出的是离它们最近的临界值。
  如果我要让number字段为无符号的怎么办呢?如下:
在这里插入图片描述  signed表示为有符号的,unsigned就是无符号的
  以上不管是tinyint还是什么int,都是要根据字段的需求来的,比如年龄,用tinyint就已足够了,没必要用bigint,用了虽然没错,但是占用的空间大,导致有很多的空间都浪费了。

补充:
  1. zerofill,先看如下:
    id是有符号的,num为无符号的,观察他们的区别,注意看Type字段,一个是int(11),一个是int(10)。就说说这两个有什么不一样,是不是括号里的数字不一样,那括号里的数字表示什么意思呢?如下:
在这里插入图片描述  上图说明了int(1)里的数字1不是代表num字段只能存储1位,像12总共两位是不是照样存下去了。其实它表示的是字符的宽度,是字符的宽度为1位。要想看出效果就得让它跟zerofill搭配,如下:
在这里插入图片描述  也就是说,如果够5个长度,就原样输出,不够,就用0填充。还要注意,zerofill是一定跟unsigned搭配的,就算你不写unsigned,它也照样加上去。最后,注意,unsigned的默认长度为10,所以zerofill被unsigned一带,也是10,而signed的默认长度为11。
总结:
 1. 如果不设置无符号还是有符号,默认是有符号,如果想设置无符号,需要添加unsigned关键字。
 2. 如果插入的数据超出了整形的范围,会找它的临界值。
 3. int如果没有设置长度,会有默认的长度。不单单是int,其他比如Tinyint等。
 4. 像int(7),要怎么理解,int绝定了你只能插入什么样的类型以及范围,而括号里的7表示你插入的数据最大宽度为7,如果不够会有0在左边填充,但必须配合zerofill使用。

7.2 数值型bit(位)的使用

  格式:BIT[(M)]。注意bit比tinyint还小。
  它属于位字段类型,M表示每个值的位数(比如1 bit就是1位),范围从1到64(也就是不能出现bit(65)),如果M被省略,默认为1。
  值是空的?为什么会这样?
解读:

  • bit在显示的时候,是以对应的ASCII码显示的。比如10进制的10对应的ASCII码就是换行,如上图,是不是有换行那味了。如下就是ASCII码表:
    在这里插入图片描述比如我以70为例,对照表,应该是大写的字母F,如下:
  • 如果有这样的值,只存放0和1,那用tinyint类型来存放的话,虽然它是最小的,但还是浪费了,因为0或1只有1位,而tinyint占了八位,浪费了七位,所以显得不合适,那么bit怎么样?如下:
    因为是一位,所以是1*20=1,所以最大表示范围为0~1,也就是0和1。所以,bit只是让我们更加的节约空间
浮点型和定点型

  浮点型有float(单精度)和double(双精度)。float占4个字节,double占8个字节,定点型的有DEC(M,D),所占的字节是M+2个,它的范围跟double相同。DEC(M,D)又可以写成DECIMAL(M,D)。注意M和D是可以省略的。

7.3 float详解

  格式:float[(M,D)]
  M指定显示长度,D指定小数位数(如果为0,则没有小数部分,变成了整数,也就是整数部分最多有M-D位)。比如float(4,2)表示的范围就是-99.99~99.99。如果是无符号的,那就是0~99.99
细节:
  如果小数点后不足两位,用0填充:

7.4 decimal详解

  格式:decimal[(M,D)](M,D)是可以省略的,省略默认是(10,0)。最大为(65,30)
  作用跟float一样,比如decimal(4,2)表示的范围也是-99.99~99.99,无符号的情况也一样。
与float的区别:
  在范围和值一样的情况下,float和decimal最终输出的值却不一样,可以说明,decimal的精度更高。但是float只要小数位不是很多,也是可以精确的,大约精确到7位小数位(如果是double的话,则大概可以精确到15位,因为它双嘛)。

字符类型

7.5 char和varchar

  命令格式:char(size),固定长度字符串,size最大255字符。
  命令格式:varchar(size)可变长度字符串,最大65532字节。[utf-8编码最大21844字符,1-3个字节用于记录大小]。
在这里插入图片描述  我们发现ab和你好都插进去了,都有一个共同点,就是没有超出两位,如果超出,会怎么样呢?如下:
在这里插入图片描述  是不是超出就截取掉后面的字符呀,也就是说,在utf8的编码下,不管是英文还是汉字都不能超出两个字符
在这里插入图片描述  由此可见,varchar也是表示字符,如上,是不是最多只能存5个呀!那么它跟char又有什么区别呢?
varchar与char的区别:
  varchar(size)里的size具体可以多大,是取决于你给它的编码的。如果是utf8,那么最大就是21844个字符,为什么呢?

  • varchar长度可以指定0~65535之间的值,也就是最大可以为65535个字节,但是有1~3个字节用于记录数据大小,所以说有效字节数为65532。
  • 有上可知,varchar最大可以表示的字节有65532个,但是因为你选用的编码是utf8,所以会涉及到汉字,而一个汉字就占了3个字节,我们知道,一个汉字是不是就是一个字符,所以以汉字为基准,所能表示的最大字符就是65532/3=21844个。而如果我们选用的编码是gbk,那么一个汉字就只占了2个字节,所能表示的最大字符为:65532/2=32766。如下(注意如下表采用的是utf8编码):
    在这里插入图片描述细节:这个细节就是mysql要求你所有的字段不能超过65535个字节,而如上num字段就已经把65535个字节都用完了,所以,如果你再增加一个字段,就会报错,如下:
    在这里插入图片描述因为int占4个字节,所以id字段就占4个字节,而num字段占21844*3=65532个字节,还有一个被抛弃的三个字节,就是65535个了,那么65535+4=65539个字节了,已经超出65535了。其它的以此类推,注意gbk的话,就是乘2,不是乘3。latin1就不变,还是65532。


从上可以看出,就是varchar(n)括号里的n具体最大到多少字符,是根据你采用的是什么编码,而char就不一样了,最大永远是255个字符。第二个区别如下:

  • char(4)是定长,就是说,即使你插入'a',也会占用分配的4个字符。
  • varchar(4)是变长,就是说,如果你插入了'a',实际占用空间大小为L+1,这个L是你实际的数据的大小,1代表的是记录这个字符串的长度(如果声明的长度超过255,则使用两个字节)。


char和varchar在使用的时候,占用的空间机制不一样,比如对于varchar,如果你给的数据很小,那么varchar所占的空间也小,可变的嘛,而char是你声明多少,就分配多少。所以char更浪费空间,但是有好处,就是char的查询速度快,效率比varchar高。第三个区别如下:

在这里插入图片描述注意,如上插入的数据不管是num1还是num2,插入的数据都是"aa "。aa后面可是有两个空格的哦,然后一个空格表示一个字节,那么总共就是4个字节,而length函数是计算字节数的,所以可以看出,varchar没有丢失空格,而char丢失了空格

日期和时间类型

7.6 date,datetime,timestamp等

日期时间类型字节日期格式最小值最大值
datetime8YYYY-MM-DD
HH:MM:SS
1000-01-01 00:00:009999-12-31 23:59:59
timestamp4YYYY-MM-DD
HH:MM:SS
197001010800012038 年的某个时刻
date4YYYY-MM-DD1000-01-019999-12-31
time3HH:MM:SS-838:59:59838:59:59
year1YYYY19012155

  date翻译过来为日期,放年月日,那么datetime就放年月日时分秒。timestamp表示时间戳,也是一样放年月日时分秒的。记得我们在前面是不是有建立一张student表,student表里有insert_time的类型就是timestamp,并且我们在添加数据的时候是不是只要给它一个NULL值就好了呀!因为它是系统给的,用来自动记录这条记录的操作时间,不是我们手动给的,如果要手动,就用datetime就足够了。所以,可以先对timestamp进行总结:timestamp可用于自动记录insert,update操作的时间。好,现在开始详细讲日期时间类型,如下:
在这里插入图片描述  看看它们给我们的结果,可知,timestamp是不允许为空的,然后呢,默认值是current_timestamp,也就是默认值是当前的时间戳,时间戳上面也看了它的日期格式,无非就是年月日时分秒,所以,默认值就是当前的时间,怎么算的,这里先提一嘴,命令:select current_timestamp,那么举一反三,比如我们写的默认值为1,是不是就是select 1,null是不是就是select null,如下:
  然后我们添加一条数据如下:在这里插入图片描述  在添加数据时注意一下,比如,year类型的这个字段,它可以使用2位数插入,也可以用4位的插入,如下:
在这里插入图片描述  是不是我只是插入了是00和98,但结果却是2000和1998,是不是正证明了year既可以插入2位,也可以插入4位呀!但是插入2位的要注意一下,比如如下:
在这里插入图片描述  我明明想要的结果是1969,但是它却给我们返回2069,这是为什么呢?我们先再试几遍,如下:
在这里插入图片描述  由此我们可以先得出一个结论是:当这两位数低于70将往2000年后走,是不是这样子的。但是我们最好写4位数的,这样不仅清晰,还不会导致错误。
  还没完,现在说说为date类型的字段,它的插入格式不仅仅可以是YYYY-MM-DD,还可以是YYYY/MM/DD,我们用图说话,如下:

在这里插入图片描述  没错,它会自动转换。甚至连/都可以去掉,也就是20210220,千万千万别写成2021220,有没有发现少了什么,是不是少了个0呀,就是说,如果你把/-给省略了,那么在写几月的时候,如果月份是单数,也就是1月份到9月份这几个区间,1月份就给我写01,2月份就给我写02,反正前面的0不能给我省了,一样,用图说话,自己去对比,如下:
在这里插入图片描述

复合类型

7.7 Enum(枚举),SET(集合)类型基本使用

  enum类似html中的单选框,set类似多选框。比如,爱好有篮球,乒乓球,橄榄球,羽毛器,游泳这五个,可以多选。性别是男和女,单选,如下:
在这里插入图片描述  添加数据如下:
在这里插入图片描述  也就是说,对于set类型,添加数据应是'值1,值2...',比如如上的爱好,你喜欢哪个,就写哪个,中间用逗号隔开即可。但是值不能出现一样的,一样的按一个处理,如下:
在这里插入图片描述  当然别忘了它被归为字符串类型,所以要干嘛,是不是引号要引起来。
  对于enum类型,就直接'值'就行了,因为它是单选,多写反而错,最后,同样别忘了引号。
  那么如何查询呢?比如我要查询爱好喜欢游泳的人,如下:
在这里插入图片描述  怎么是空的,明明有呀!其实逻辑是对的,因为它找的就是游泳这个字符串,很明显乒乓球,游泳≠游泳。所以针对这种情况,我们要引入一个函数,该函数是find_in_set。如下:
在这里插入图片描述  也就是格式为find_in_set(查找项,列表项)。观察如上表发现结果为数字,这个数字就代表了查找项在列表项的位置,注意列表项从1开始。如果找不到就返回0。所以如下:
在这里插入图片描述  也就是在where后面接上那个函数,如果返回0,则这一行不显示,非0则显示。但对于单选就没必要用find_in_set函数了,用普通的条件查询就ok了。
扩展:
  set类型的添加有第二种方式,如下:
在这里插入图片描述  啥意思?怎么添加的是一个数字3,结果输出的却是篮球和乒乓球?那是因为,数字有特殊的含义,什么含义?如下:
在这里插入图片描述  对于enum也是如此,如下:
在这里插入图片描述注意点:
  在创建表时enum和set都不允许有默认值。

8. DQL命令

  DQL全称Data Query Language,译为数据查询语言。既然是查询,那么会涉及到关键字select。

8.1 基础查询

select 查询列表 from 哪张表

  select:查询。from:来自。意思:根据需求查询表中的某个字段信息
  查询列表(个数和顺序可以跟原始表不一致)意味着你要查询的东西可以有多个,查询列表不仅仅是表中的某个字段,还可以是常量值,表达式,函数这些都可以,只不过后三个不需要说明来自哪张表。比如我要查询学生的姓名和生日,那么结果如下:
在这里插入图片描述  因为student表就只有一条记录,所以查出来的就一条。你既然查询的是学生的名字和生日,那就要指定来自哪张表,因为它不是mysql所能认识的常量值啊,表达式啊,函数啊这些。然后多个字段中间用逗号隔开。最终查询出来的结果就是一个虚拟的表格,不是真实存在的。
  当你要查询某个列的时候查询列表就要写上你要查的列名,如果是所有列,就用*(但是以后尽量少用*号)代替。前面都有出现过。
select非字段:
常量值可以是数字,如下:
在这里插入图片描述还有字符型:

select 'cht';

表达式:

select 39*45;   #包括加减乘除,取模,但是不能自增自减

  像这种,比如以后涉及到查询总分的时候,比如学生表有3个字段分别是数学,英语,语文。那么总分就是数学+英语+语文。也就是select name,数学+英语+语文 from student;就可以查询到每个学生的总分成绩了。


函数:

select VERSION(); #查询版本

起别名:
以上图select 100为例,如下:
在这里插入图片描述我们发现,字段名不再是100了。是不是提高了可读性,有利于我们理解字段的意思。注意,as是可以省略的。并且别名也可以加反引号或引号。
去重:
 在演示去重之前,先增加几条记录,如下:
在这里插入图片描述 比如查询所有学生的总分,如下:
在这里插入图片描述  一看结果表中的数据有2个重复的,2个520分的,如果不想让结果重复,就是不让字段total_score重复,只要total_score唯一了,那么结果也不会重复了,重复的只需要显示一遍就可以了。所以:
在这里插入图片描述  注意,去重没有那么简单,比如有下表及数据:
  图上最后一条sql我并没有把结果截取出来,只是给大家猜它的结果集会是多少,这也是我为什么说distinct没有那么简单,一开始,我认为distinct只对num1去重,并没有对num2去重,因为在num2这个字段的前面并没有distinct修饰,但是结果却出乎我的意料,如下:
  不对呀,我不是对num1去重了吗,那怎么会出现了3个2,通过尝试,我知道了,其实它的去重是把num1和num2结合起来的去重,也就是说,我把num1和num2看作一个整体,如果有两条或者多条记录它的num1和num2都一样的话,我们就对它去重,比如我原本的数据是不是有两条记录是一样的呀,num1为2,num2为xiaochen的那个,看到了吗?是不是最终的结果只取一条呀!所以,明白了吗?除非你在查询的时候,查询列表上不要加入num2,也就是select distinct num1 from testdistinct;,那么它就真的只对num1去重了,这点要注意,还有一点,如下:
在这里插入图片描述  也就是说,distinct不能放在某个字段的后面。还有,也不能出现多个distinct,如下:
在这里插入图片描述  其它的也没什么了!!
+号的作用:
  在mysql中,加号是运算符的意思,并没有连接的作用,所有如果一条sql语句:select '100'+9,即使100加上了单引号,最终的结果还是109,因为它会对字符串100尝试看看能不能转换为数字,如果不能,那就是0,比如它会对字符串的第一个字符开始,如果第一个字符就已经不是数字了,那就为0。
  还要注意,在运算符中,如果有一方为null,哪结果肯定为null
concat实现连接:
  字符串连接,如下:
在这里插入图片描述  可用于字段与字段之间的连接,比如姓名和学号,如下:
在这里插入图片描述  因为是拼接,所以:
在这里插入图片描述  它跟加号类似,所以如果两个字段中有一个为null,结果就是null,我们可以增加一位同学,叫小和,它的学号是空的,如下:
在这里插入图片描述  执行结果,如下:
在这里插入图片描述  这时候,要引入一个函数就是ifnull函数,它有两个参数,第一个参数是那个可能会发生空的字段,第二个参数是如果为空,要返回什么值,所以如下:
在这里插入图片描述  这样就不会对名字造成影响了。
扩展:
  跟ifnull很像的有nullif,这个nullif是什么呢?如下:
  也就是说,它是让第一个参数跟第二个参数比较,一样就返回null,不一样就返回第一个参数的值。
  第二个扩展,直接放图:
在这里插入图片描述

8.2 条件查询(增加where子句)

select 查询列表 from 哪张表 where 筛选条件

  如果筛选条件成立,就会被筛选出来,否则不会被筛选出来,结果要么是true要么是false。与基础查询相比多了个条件约束。注意它的执行顺序,是先看表名,再看筛选条件,最后才看查询列表
筛选条件分类:
  1. 按条件表达式(条件运算符)筛选:>,<,=,!=,<>(也是不等于的意思),>=,<=
  2. 按逻辑表达式(逻辑运算符)筛选:&&,||,!,and,or,not
   &&和and:两个条件为true,结果就为true,反之为false。
   ||和or:只有有一个条件为true,结果就为true,反之为false。
   !和not:如果连接的条件本身为false,取反后结果就为true,反之为false。
  3. 按模糊查询筛选:like,between and,in,is null
条件运算符的使用:
  1. 查询学号大于3604的学生信息:
在这里插入图片描述  2. 查询学号不在3603到3605之间,或者总分大于500以上的学生信息:
在这里插入图片描述  我们发现id为3的不见了,为什么呢?因为or是不是或者的意思,上面的格式是A or B or C,也就是说A B C只要有一个条件成立,为true就会被筛选出来,像上面,能满足number<3603或者number>3605的有id有1,2,6,7。所以1,2,6,7会被筛选出来,剩下没被筛选的有3,4,5这三个,不是还有一个条件吗,是总分total_score要大于500的,那么我们看看id为3,4,5的总分哪个是大于500的,是不是4和5,虽然前两个条件不满足,但是第3个条件满足了,因为三者是或的关系,所以id为4和5的是不是也能筛选出来,所以最终筛选出来的是不是1,2,4,5,6,7。跟上图的结果是不是一样的。
  还可以这样写,如下:
在这里插入图片描述
注意点:
  where后面所接的字段不能是字段的别名,必须把字段写全了。

8.3 模糊查询

  第一个,like,译为像什么样子。在说它之前先添加一条数据,如下:
在这里插入图片描述
  1. 查询学生名包含舞的学生信息:
在这里插入图片描述  那个%(跟*一样是通配符)是什么呢?它表示任意多个字符,包含0个字符,是一种占位符,也就是说我要查询包含舞的姓名不管它在第一位,还是中间,或者最后,都要查出来,比如小舞的舞就在最后,舞魅的舞就在第一位,那么我想要舞魅,不要小舞怎么办,如下:
在这里插入图片描述  所以现在明白了吧!%代表任意多个字符,那么_就表示任意单个字符。我们再添一条数据,如下:
在这里插入图片描述  如果我要把小舞和小小舞筛选出来,就是%舞,如果只要小舞,不要小小舞,就用_舞,因为它表示筛选舞前面只有一个字符的文字,那么如下:
在这里插入图片描述  如果要找第三个字符为舞的,怎么办,用%不行,用_是可以的,虽然它表示一个字符,但是它可以连续写三个_表示三个字符呀,也就是:___舞
  如果是查询姓名中第三个字符为_的学生信息又该怎么办,直接三个_肯定不对呀,这样它查出来的就是所有只有三个字符的数据。第三个字符的_应该是个普通的符号,而不是特殊符号,所以我们能不能进行转义一下,当然是可以的,就是:__\_。这说明使用模糊查询是可以使用转义字符的。我们试一下,先添加一条数据,如下:
在这里插入图片描述  命令如下:
在这里插入图片描述  再说一点,你可以用其它的符号做转义字符,比如a,但是后面要escape说明一下它是转义字符,如下:
在这里插入图片描述


  第二个,between and关键字,译为在什么什么之间。
  1. 查询学生号在3602到3605的学生信息:
在这里插入图片描述  格式就是between 起始值 and 终点值。注意筛选出来的结果包括起始值和终点值,它是一个闭合区间。


  第三个,int关键字。它是判断某字段的值是否属于in列表中的某一项。
  1. 查询学生的学号是3603,3605和3608的学生信息:
在这里插入图片描述  注意,它不支持通配符,所以它不能像like那样模糊匹配


  第四个,is null关键字。直译过来为"是空"。
  1. 查询没有学号的学生:
在这里插入图片描述  结果是什么都没查到,小和的学号不是为NULL吗?也就是=不能判断null值,包括<>。这时候就要用is null了,如下:
在这里插入图片描述  结果是不是出来了。与之相反有is null就有is not null


  第五个,<=>关键字。表示安全等于。它也可以判断null值,也就是它可以替换is not。所以它跟=号的区别就是,=不能判断null,而<=>可以判断null,其它的没什么区别。
注意点:
  注意一下通配符是不能匹配null值的,什么意思呢?小和的学号是不是为null,如果用select * from student是可以查出来的,但是用select * from student where number like "%%";可以筛选出来吗?number like "%%"是不是学号要模糊查询所有,但是注意,不包括null,如下:
在这里插入图片描述  是不是并没有id为7的小和。

8.4 排序查询(order by子句)

  命令格式:select 查询列表 from 表名 [where 筛选条件] order by 排序列表 [asc|desc]asc为升序,desc为降序。默认是asc。
  1. 按总分由大到小对学生信息进行排序:
在这里插入图片描述  order by不仅可以对数值排序还可以对时间排序。并且它支持字段的别名,以及函数。
  注意,如果像这样的,select * from student order by total_score desc,birthday asc;。它表示先按总分进行降序排序,如果发生总分一样的,那么就按生日进行升序排序。
  order by子句一般放在查询的最后面,limit子句除外。执行顺序变成了先是表,再是where条件,然后是查询列表,最后才是order by。所以order by是可以接别名的,因为它先经过查询列表,再经过order by。
  2. 查询学号不在3603到3608之间的学生姓名和总分,按总分降序排序:
在这里插入图片描述  注意,order by后的总分可别加上引号。

8.5 常见函数

  类似java中的方法,将一组逻辑语句封装在方法体中,对外暴露方法名。好处是隐藏了实现细节,提高了代码的重用性。其实mysql的函数跟我们学java中的函数是道理一样的。只是调用的对象不同而已,mysql的调用对象就是select,也就是select 方法名()。并且括号里像java一样可以传入参数,如果传入的参数要用到某表中的字段,那么命令就是select 方法名(实参列表) from 表,最后,mysql的方法执行完是有返回值的。
  函数又分为单行函数和分组函数,单行函数有我们讲过的concat,ifnull,还有查看字节数的length。单行函数可以简单的理解为传入一个数最终返回一个值,而分组函数是传入一组数返回一个值,有点类似于统计,所以分组函数又叫统计函数,也叫聚会函数,组函数

8.5.1 单行函数
8.5.1.1 字符函数

查看字符集:

在这里插入图片描述

查看字节数:

  也就是一个汉字占两个字节,但是这是分字符集的,比如如果是utf8就是一个汉字占3个字节,我们用如下命令来查看当前所用的字符集:
在这里插入图片描述

upper(或者ucase),lower(或者lcase)

substr(substring)

  substr或者substring用于字符串的截取。

intstr

  它的意思是返回子串(第二个参数的小花)第一次出现的索引,如果找不到,返回0。

trim

  它的意思是去文字的前后空格,但是不能去中间的空格。如下:
  如果又这么一个字符串bbbb12345bb7654bbbb,我要去掉前后的b,那我们可以把b看成空格,去掉后就是12345bb7654,怎么实现?如下:

lpad

  必须满足5个字符,但是不足5个字符,左边用*填充。如果已经超出范围,如下:
  超过了就截断。

replace

8.5.1.2 数学函数

bin

在这里插入图片描述  bin表示把十进制转为二进制。
  类似的,像hex表示转为16进制。

round(或者format)

  round表示四舍五入,如下:
在这里插入图片描述  注意,round支持负数。同时round还有重载,如下:
在这里插入图片描述

ceil(ceiling)

  ceil为向上取整。如下:
在这里插入图片描述  注意它是返回>=该参数的最小整数。所以,如下:
在这里插入图片描述  最后注意ceil为向上取整,那么floor为向下取整,返回<=该参数的最大整数。

truncate(防止四舍五入)

在这里插入图片描述  如果按照四舍五入,那么应为2.4。

mod(取余)

在这里插入图片描述  15除以2是不是余1。

rand

  随机数,取0到1之间的小数,它是闭合区间。

获取最小值

在这里插入图片描述

8.5.1.3 日期函数

返回当前系统日期+时间:

在这里插入图片描述  除了now(),还有select current_timestamp();

返回当前系统日期:

在这里插入图片描述  也可以写成:select current_date()

返回当前时间,不包含日期:

在这里插入图片描述

date函数(返回日期部分的函数):

在这里插入图片描述

date_add(date,天数)函数:

在这里插入图片描述  interval译为间隔,也就是间隔10,说白了,就是在第一个参数的基础上加上10天,也就是10后是多少。如果是10天前,哪就在10前加上减号。注意,day可以换成其它的,比如minute,表示分钟。

date_sub(date,天数)函数:

在这里插入图片描述  效果跟date_add相反。
  可以获取指定的部分,年,月,日(day),小时(hour),分钟(minute),秒(second):
  上面为年的,下图为月:
在这里插入图片描述  下一个,是将字符格式的日期比如"4-2-2000",转换成指定格式的日期,如下:
在这里插入图片描述  第二个参数要跟第一个参数一一对应,意在告诉mysql,"4-2-2000"的4表示什么意思,是年,还是月,还是日?所以第二个参数就是告诉mysql第一个参数分别代表什么?比如上图的第一个命令,与4对应的是不是%m,%m就代表月,所以4代表4月。
  下一个,与str_to_date与之相反,叫date_format,它是将日期转换成字符,如下:
在这里插入图片描述  格式符还有如下几个,对date_format是适用的,但对str_to_date不适用,这点要注意:

序号格式符功能
1%Y四位的年份
2%y2位的年份
3%m月份(01,02,…11.12)
4%c月份(1,2,…11,12)
5%d日(01,02,…)
6%H小时(24小时制)
7%h小时(12小时制)
8%i分钟(00,01…59)
9%s秒(00,01,…59)

  为什么说对str_to_date不适用,看如下跟date_format对比就知道了:
在这里插入图片描述  %y表示2位的年份,那么2019就是19,没错,但是str_to_date就不一样了,2000按道理是00,但是结果却是2020,明显错了。
  最后,不管是str_to_date还是date_format,日期写错就是null,比如2月40号,有2月40号吗?没有吧,没有结果为NULL。
  下个,datediff(日期1,日期2)表示日期1和日期2两者的相差天数。注意是日期1-日期2=相差天数,所以日期1要比日期2大,如下:
在这里插入图片描述  计算时间差timediff:
在这里插入图片描述  unix_timestamp(date):
  计算date距离1970-01-01 00:00:00:00的秒数,如果unix_timestamp()不带参数,则是计算当前日期距离1970-01-01 00:00:00:00的秒数。也就是Unix时间戳,拿到这个秒数我们也可以重新把它还原为日期,如下:
在这里插入图片描述  可见,from_unixtime是对时间戳进行格式化,在格式化的同时也可以按你指定的格式显示,如下:
在这里插入图片描述

返回今天星期几:

在这里插入图片描述  还有一种是返回工作日索引,注意它的索引从1开始一直到索引7,也就是分别对应着星期日到星期六,如下:
在这里插入图片描述

查询今天是第几季度:

在这里插入图片描述

8.5.1.4 流程控制函数

  格式:select if(条件表达式,成立,不成立);
在这里插入图片描述  下个,ifnull函数,格式:select ifnull(字段,参数2);。表示如果字段的值为空,返回参数2,不为空,则返回它本身的值。
  再下个,case函数,类似java中的switch case,格式如下:

case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1; #值可以不加分号
when 常量2 then 要显示的值2或语句2; 
...
else 要显示的值n或语句n;  #else可以省略
end

在这里插入图片描述
  case的第二个使用方式,类似java的:

if(条件1){
		语句;
}else if(条件2){
		语句2;
}
...
else{
		语句n;
}

  case的第二个格式如下:

case
	when 条件1 then 要显示的值1或语句;
	when 条件2 then 要显示的值2或语句;
	...
	else 要显示的值n或语句n;
	end

在这里插入图片描述

8.5.1.5 其它函数

select version();
select user(); #表示当前的用户
select password(‘字符’); #返回该字符的密码形式,就是自动加密
select md5(‘字符’); #md5加密

8.5.2 分组函数

  分组函数分为sum(求和),avg(平均值),max(最大值),min(最小值),count(计算个数)。
在这里插入图片描述  注意count函数(表示返回某一列,行的总数)只计算不为null的个数,所以上图最后一条命令结果才为9。还有sum是求和的意思,null也不会参与运算,包括avg,max,min。结论:以上的分组函数都忽略null值
小细节:
  sum可以跟distinct搭配,格式:sum(distinct 字段)。count函数也一样,并且也用的比较多。除了这两个支持distinct,其它的也支持。
  count的第二个细节是count(*),*表示所有字段,假设表只有name和number两个字段,并且表的总记录总共有10条,包括null值。此时这张表的字段name是第5行为null,而number是第7行为null,因为null值是不同行,只要一行当中有一个不为null,那么这行就不为null,因为name的第5行为null,而number的第5行不为null,所以第5行也不为null,同理,第7行也不为null,所以count的结果就为10。
  还有一种可以统计null值的,比如count(数字或数值)。我们通常写1,也就是count(1)。它就好像在表增加了一列,这一列的每一行都为数字1,它就是统计1的个数。然后像count(*)和count(1)有效率问题,这要说到存储引擎,比如在myisam存储引擎下,count(*)的效率高,但是如果是在innodb存储下,count(*)和count(1)的效率差不多,比count(字段)要高一些。
  如果字段是分组函数,那么和分组函数一同查询的字段要求是group by后的字段。请看下一个知识点。

group by子句

  在说它之前先添加几行数据,注意添加的这几行数据是高三七班的学生,如下:
在这里插入图片描述  如果我要算高三六班的平均分和高三七班的平均分,如果按照上一节的知识,我们是不是无法完成,命令:select avg(total_score) from student;。这样子肯定不行,它会把高三六班和高三七班的成绩都算在一起,是不是得让它为班级分组呀,六班一组,七班一组。那就要用到group by了。
在这里插入图片描述  完整格式:select 分组函数,列(要求出现在group by的后面) from 表 [where 筛选条件] group by 分组的列表 [order by 子句]
  如果有个需求,是要在上图的基础上筛选出平均分>486的班级,怎么办?用where行吗?如下:
在这里插入图片描述  是不是报不能使用分组函数,因为分组函数在原始表student中不存在,确实,在student表中没有一个叫avg(total_score)的字段,只有总分,也就是说,where后面所接的字段来源于from的原始表,那么该怎么做?如下:,
在这里插入图片描述  重点在having上,它跟where一样,也是用于筛选,它的命令执行,其实相当于先执行了两条命令,第一条:select class 班级,avg(total_score) 平均分 from student group by class;,是不是就获取到了如下表呀:
在这里插入图片描述  这个表是虚拟表,然后呢它在这个表的基础上(把虚拟表当做原始表),进行where筛选,命令:select * from 虚拟表 where 平均分>486,这个就是第二条命令。注意,select * from 虚拟表是不是就相当于select class 班级,avg(total_score) 平均分 from student group by class;呀,那么等价替换,第二条命令是不是也可以写成,select class 班级,avg(total_score) 平均分 from student group by class where 平均分>486;但是为了跟where区分开,就用了having,having的字段来源于虚拟表,接在了group by的后面,注意having只和group by搭配使用。
总结:

数据源位置关键字区别
分组前筛选原始表(真实存在的表)group by子句的前面wherewhere后不能接别名
分组后筛选虚拟表(命令执行后的结果集)group by子句的后面havinghaving后可接别名

  注意如果查询后的结果集没有班级,如下:
在这里插入图片描述  因为你是根据原始表的班级分组,所以虚拟表也应该有班级这个字段,证明如下:
在这里插入图片描述  因为我前面说了,having后的字段来源于虚拟表,而最终结果是不是出来了,说明虚拟表是有班级class这个字段的。所以,虚拟表有class字段,原始表也有class字段,那么是不是也可以这样:
在这里插入图片描述  结果是不是跟having的一样,那么既然这样,以后是优先用哪个呢?记住:能用分组前筛选的,优先考虑使用分组前筛选
小细节:
  group by分组除了对字段进行分组,还可以对函数和表达式分组。也可以对多个字段分组,如果是多个字段,那么每个字段用逗号隔开即可,并且之间没有顺序。注意多个字段分组怎么理解,其实道理跟我上面讲的having是一样的,比如group by A,B就是先按A进行分组,就会得到分组后的结果,然后这个B会在A分组后的结果的基础上再进行分组,就是最终的结果啦!
理解:

  • 一说到group by,要知道它是分组的意思,如果没有分组,那么我们所算的平均值就是所有班级的平均值,但是一旦按了班级分组,那么一班就是一班,二班就是二班,也就是说,它的分组规则是"一样"的为一组,类似于去重了,这样才是我们想要的结果,因为我们看的是各班的平均分。也可以这样理解分组,因为有了分组函数的存在,那么最终的结果就只有一条记录,导致跟它一起出现的字段受到牵连,比如班级class,因为班级class并没有进行分组啥的,也没有被什么函数处理,就是一个普普通通的字段,那么你查询出来的这个class值就是从第一条记录的class字段值开始,但是受到分组函数的影响,导致只能显示第一条记录的class值,而不能显示后面比如第二条记录的class值,也就是说,分组函数跟它一起出现的字段并不能自动的关联在一起,它没这个能力,它们只是在履行它们(字段)各自的义务而已,像字段total_score它的义务就是算出平均分,因为它是用分组函数算的,所以导致最终结果只有一条记录,那么与它一起出现的字段就会受到牵连,除非这个字段分组了,那么最终的结果可能就不止一条啦!请结合如下图:
    在这里插入图片描述

注意:
  如果只是单纯的分组,没有涉及到分组函数之类的,默认会取分组后的第一条记录。
  如果查询列表有分组函数,那么与它一起出现的字段不是一定要分组,看情况而论。比如如下:
在这里插入图片描述  也就是说我可以指定某一组,并不一定就要分组。
扩展:
  就是在group by子句后面使用with rollup关键字,它表示要对分组后的各个结果做一个统计,统计结果另起一行,如下:
在这里插入图片描述  sum(total_score)是对分组后的统计,而with rollup是对虚拟表的聚合函数sum(total_score)字段进行统计,没有统计的class字段为null,统计结果另起一行,如上图,1658+4888是不是等于6546。那如果是max或者min的呢?如下:
在这里插入图片描述  也就是说,max的话本身是取自个班级的最大分数,而with rollup是取出所有班级的最大分数。min也是一样的道理。
  如果我不想让class字段的那个数据为null怎么办,有个函数coalesce(a,b),这个函数表示如果a等于null,则选b,如果a不等于null,则选它本身,也就是a,如果a,b都为null,那这个函数就没意义,怎么用?如下:
在这里插入图片描述  诶,这个coalesce跟ifnull有什么区别呢?其实如果在两个参数上它们是没区别的,但是如果在三个参数上就有区别了,如下:
在这里插入图片描述  也就是说,对于coalesce而言,第一个参数为null,就看第二个参数的,如果第二个参数还为null,就看第三个参数的,以此类推,可以放多个,不单单只有三个参数。所以,它们两个的区别之一就是接受的参数个数不一样。

8.6 多表查询

  在开发过程中,我们查询所得来的数据必定会来自不同的表,这时,多表查询就要出场了。以下案例我将用雇员表(我所认为的经典表)来演示,在我的网盘将会有sql文件:https://pan.baidu.com/s/17kjWwlJ2nbpC5Epr_JuTvw,提取码为jvsg,当然了,这sql文件不是我写的,而是我在b站学习过程中搞下来的,仅仅是为了学习而已。
  所谓的多表查询是指基于两个和两个以上的表或者是视图(先不管视图)的查询,比如,我要查询的是雇员名以及这个雇员所在的部门名称,怎么做?
分析问题:

  • 首先我们要知道查询涉及到哪些字段?一看,哦,雇员名和部门名称,总共两个字段,那么这两个字段来自那张表呢?先看雇员名来自哪张表,是不是来自employees表的last_name字段,那好,部门名称在不在employees表呢?我们发现并没有部门名称,有的只是部门编号,也就是department_id,诶,怎么跟我们想的不一样?不是存的是像技术部这样子的文字或者英文吗,怎么存的是数值?还有这些数值代表什么意思呢?其实,我们仔细看myemployees库就会发现它有一张表叫departments表,那么我的employees表里的department_id会不会跟departments表有关联呢?查询一下,还真有,我们的employees表里的department_id字段的值在departments表里的id字段都能找到出处,并且根据这条线过去就可以看到部门名称了,也就是department_name字段,这不就是我需要的部门名称吗,这样,我要查询的字段来源于哪几张表就已经齐了。如下:
select last_name,department_name from employees,departments;

  注意,你要查询的字段来自哪些表,一定要说明清楚,把它写全,上面也说了,last_name来自employees表,department_name来自department表,所以你在from的后面就要把它写全,用逗号隔开即可,也就是说查询列表来自from后面的原始表。然后回车执行,看看总共查出来多少条记录,如下:
  总共2889条记录,然后再看看数据,发现有点不对劲,以上图为例,名字叫Gietz竟然对应所有部门,包括其他人也是,不用说,这条命令有问题。那它是怎样把这两张表结合成一张的呢?为了好讲,我们演示select * from employees,departments;命令,如下:
在这里插入图片描述  没错,就是将两表的所有字段拼接起来,因为我们用的是*号,那么同理,select last_name,department_name from employees,departments;两表结合之后就只有两个字段。再说说数据是怎么回事,也就是说最终的数据就是第一张表对应的所有字段的每一条数据跟第二张表对应的所有字段的所有数据进行一一匹配,它的最终行数就是第一张表的总记录数乘以第二张表的总记录数的结果。像这种现象我们称为笛卡尔乘积现象。如果还有第三张表,那么就继续乘第三张表的总记录数,以此类推。那要怎么避免这种现象?继续往下看。

  • 要想避免以上现象,就要给它加上筛选条件,所谓的筛选条件,就是让employees表的department_id和departments表的department_id的值相等,相等就筛选出来,这样,一条线不就出来了吗,是不是也避免了笛卡尔现象,一个人也不会同时拥有多个部门了,那么命令如下:
select last_name,department_name from employees,departments where department_id=department_id;

  但还是有错,鬼知道你的department_id是哪个表的department_id,所以要指定是在哪个表吧,因为两张表都有department_id,包括查询列表也一样,那要怎么写?就是“表名.字段名”,这样就好了,如下:

select last_name,department_name from employees,departments where employees.department_id=departments.department_id;

  效果如下:
在这里插入图片描述  多表查询的第一个案例就完成了!
练习:
1. 查询部门号为20的部门名,员工名和工资:
在这里插入图片描述2. 这个例题将演示非等值连接,以student表来演示,演示它不仅需要student表(已经有了),还需要一个分数等级表,如下:

在这里插入图片描述那么问题来了,显示出学生的姓名,成绩,以及成绩所处的等级:
在这里插入图片描述3. 查询每个部门工资最高的人的部门号,姓名,和工资:

select department_name,last_name,max(salary) from departments d,employees e  where d.department_id=e.department_id group by department_name;

注意:
  只要涉及到多表查询,一定要给它加上连接条件,否则就会出现笛卡尔乘积现象,至于连接条件是等值连接还是非等值连接再考虑。

8.6.1 自连接

  自连接也是多表查询,只不过它是同一张表的多表查询,也就是自己连接自己。
1. 显示员工名叫kochhar的上级领导姓名:
分析问题:
先看employees表的结构,如下:
在这里插入图片描述所以,针对它,我们可以先查询出kochhar它的上级编号是多少?如下:
在这里插入图片描述  得出了kochhar的上级编号,我们就可以通过上级编号找到对应的信息,如下:
在这里插入图片描述  那怎么用多表查询来做,我们可以想象成是两张一模一样的表,这样不就好了:
在这里插入图片描述

8.7 子查询

  子查询是指嵌入到其它sql语句(增删改查,主要是查)中的select语句,也叫嵌套查询或者内查询。像外部的查询就叫主查询或者外查询。
分类:

按结果集的行列数不同

  1)标量子查询,又叫单行子查询(结果集只有一行一列)
  2)列子查询(结果集只有一列多行)
  3)行子查询(结果集有一行多列,也可以多行多列,但多行多列一般不叫行子查询)
  4)表子查询(结果集无所谓,一般为多行多列)

子查询可以出现的位置

  1)select后面(标量子查询)
  2)from后面(表子查询)
  3)where或having后面(标量子查询,列子查询,行子查询)
  4)exists后面的查询也叫相关子查询(表子查询)
不管怎么样,我们可以简单的把子查询分为单行子查询和多行子查询。单行子查询就是一行嘛,多行子查询就是多行嘛。
特点:
  1)子查询放在小括号内。
  2)子查询一般放在条件的右侧。
  3)标量子查询,一般搭配着单行操作符使用。单行操作符有> < >= <= = <>
  4)列子查询,一般搭配着多行操作符使用。多行操作符有in any/some all
案例1(单行子查询,会用到单行操作符):
1. 查询与Kochhar同一部门的员工:
  解决这个问题要先知道Kochhar的部门编号是多少,然后在通过部门编号是不是可以查出这个部门编号下的所有员工呀,所以如下:在这里插入图片描述注意上面的子查询属于单行子查询,因为括号里的select department_id from employees where last_name="Kochhar"语句就只返回一行结果。如下 :
在这里插入图片描述是不是一行一列呀,并且它是不是放在where的后面,用括号括起来的子查询呀,子查询里的操作符是不是就是=号。
2. 返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资:
在这里插入图片描述3. 返回公司工资最少的员工的last_name,job_id和salary:
在这里插入图片描述4. 查询最低工资大于50号部门的最低工资的部门id和其最低工资:
在这里插入图片描述
案例2(多行子查询,会用到多行操作符):

操作符含义
in/not in等于列表中的任意一个
any/some和子查询返回的某一个值比较
all和子查询返回的所有值比较

a in(1,2,3)就表示a可能是1,2,3里的某一个值。注意in可以替换=,用in的好处,就是当你不知道是单行还是多行的时候用in比=保险。
a>any(10,20,30)表示a的值可以大于any列表的某一个,其实,像这种情况,它想成立,大于10就可以了,那么any(10,20,30)就会被min函数所替代,这样,跟min函数对比起来,min可读性更高点。(但是如果是a<any(10,20,30),那么any(10,20,30)就会被max所替代。)

1. 返回location_id是1400或1700的部门中的所有员工姓名:
在这里插入图片描述2. 显示工资比部门30的所有员工的工资高的员工的姓名,工资和部门号:
在这里插入图片描述3. 显示工资比部门30的任意一个员工的工资高的员工的姓名,工资和部门号:

select * from employees where salary > any(select salary from employees where department_id=30);

或者:

select * from employees where salary > (select min(salary) from employees where department_id=30);

案例3(行子查询):
1. 查询员工编号最小并且工资最高的员工信息:
最能想到的解法:
在这里插入图片描述用一行多列来解:
在这里插入图片描述注意它们有一一对应的关系,比如employee_id就和min(employee_id)对应,salary就和max(salary)对应。
案例4(select后面):只能放一行一列的标量子查询
1. 查询每个部门的员工个数:

select d.*,(
  select count(*) from employees e where e.department_id=d.department_id
)from departments d;

2. 查询员工号=102的部门名:

select (
    select department_name from departments d,employees e where d.department_id=e.department_id and e.employee_id=102
);

案例5(from后面):注意起别名,并把子查询当做临时表使用
1. 查询每个学生的平均成绩和成绩等级:
在这里插入图片描述2. 查询各部门中工资比本部门平均工资高的员工的员工号,姓名和工资:

select employee_id,last_name,salary
 from employees e,
    (select avg(salary) ag,department_id
    from employees
    group by department_id
    ) ag_dep where e.department_id=ag_dep.department_id
 and salary>ag_dep.ag;

第二种写法:

select employee_id,last_name,salary from employees e1 where e1.salary > (select avg(salary) from employees e2 where e2.department_id=e1.department_id);

3. 查询每个部门工资最高的人的部门编号,姓名,和工资:
在这里插入图片描述

select e.department_id,last_name,salary 
from employees e,(
   select max(salary) mymax,department_id 
   from employees group by department_id
) d 
where e.department_id=d.department_id 
and e.salary=d.mymax;

案例6(exists后面):exists表示是否存在

select exists(select employee_id from employees);

像这条sql语句,就是判断select employee_id from employees语句有没有结果,没结果就返回0,有结果就返回有结果的值。
1. 查询有员工的部门名:
在这里插入图片描述注意,有exists也有not exists,同时exists可以用in进行替代。

8.8 分页查询

  分页查询,代表limit,格式:select 查询列表 from 表 limit 起始索引(从0开始),长度;limit一般放在查询语句的最后。
1. 查询前5条的员工信息:
在这里插入图片描述如果从0开始的话,可以直接写5,也就是limit 5。
2. 查询第11条-25条:

select * from employees limit 10,15;

假设知道每页显示5条,也就是size=5,那么第二页或者第三页从哪个索引开始是不是要算出来,怎么算,看公式:select 查询列表 from 表 limit (page-1)*size,size;page就代表第几页。

8.9 合并查询

  有时在实际应用中,为了合并多个select语句的结果,可以使用集合操作符号union,union all

union

  该操作符用于取得两个结果集的并集(就是将两个结果合并在一起)。当使用该操作符时,会自动去掉结果集中的重复行
在这里插入图片描述  将上面两条sql语句合并,会不会是17条呢?如下:
在这里插入图片描述  最终总记录数是16,为什么少了一条呢?是因为两张表有重复的记录,名字叫Hartstein的员工,那么有重复的记录就只需要保存一条就行了。
  注意union中的每个查询必须是相同的列,表达式或者聚合函数。并且在列类型上类型必须要兼容,可以隐式转换的那种。

union all

  跟union的区别就是它不会去掉重复的记录。那么这样两张表的总记录数就是17了。

8.10 表连接

  表连接分为内连接和外连接。

内连接

  内连接实际上就是利用where子句对两张表形成的笛卡尔积进行筛选,我们前面学习的查询都是内连接,也是在开发过程中用的最多的连接查询。但是标准的内联格式是:select 查询列表 from 表1 inner join 表2 on 连接条件 and 其它条件;,也就是说,它跟之前的比(select 查询列表 from 表1,表2 where 连接条件 and 其它条件;),逗号被替换为inner join,where被替换为on。像这种替换后的我们称为sql99标准,替换前的为sql92标准。所谓92是指1992年推出的标准,那么99的也一样。
1. 显示雇员的名字和对应部门的名称:
  第一种写法:

select last_name,department_name from employees e,departments d where e.department_id=d.department_id;

  第二种写法:

select last_name,department_name from employees e inner join departments d on e.department_id=d.department_id;

外连接

  外连接分为左外连接和右外连接。那什么叫左外连接?它的意思是如果左侧的表完全显示我们就说是左外连接。那么右外连接就是右侧的表完全显示啰。
左外连接:
  格式:select 查询列表 from 表1 left [outer] join 表2 on 连接条件 and 其它条件;。表1对于表2就是左表,跟书写顺序有关。
  在演示之前先创建两张表:

2. 显示所有学生的名字和成绩:
  如果用内连接做:
  用内连接做的话,因为两名同学没有成绩,所以导致查询的时候把另外两名同学给忽略了,但是,就算他没成绩,我也想把他的名字列出来,怎么做,如下:
  搞定!
右外连接:
  跟左外连接道理是一样的,还是以上例子为例:
  注意,不单单left要改为right,还有stu和exam的书写位置也要换一下。
理解:
  1.内连接取的是交集,比如stu表的id字段分别是1,2,3,4。而exam表的id字段分别是1,2,11。那么它们之间的交集是不是1和2。所以最终是不是有两条记录。
  2.外连接最终结果看似有交集的部分,但其实它是看左右的,比如如果是left join,那就是看左边,要让左边的表完全显示,而左边的表是stu,stu的id字段分别是1,2,3,4。那么这1,2,3,4就必须完全显示出来。

9. 表的约束

  表的约束,就算对表的数据约束,比如email这个字段,全世界应该没有两个人拥有两个一样的邮箱吧,所以邮箱这个字段我们是不是要设为唯一,是不是要给它一个约束呀!
  其实这个约束在前面建表的时候有提到过,像not null是不是见过,它就是一种约束。
  表的约束可分为5种:主键(primary key),非空(not null),unique(唯一约束),外键约束,check约束

主键约束

  格式:字段名 字段类型 primary key
  它是用来唯一的标识表行的数据,当定义主键约束后,该列不能重复。一般来说,我们建表都习惯给某个字段比如id,给它一个主键约束,就是用来标识每一行记录,相当于每一行都有身份证,因为主键是不能重复的。
特点:
1)primary key不但不能重复而且不能为null。
2)一张表最多只能有一个主键,但可以是复合主键(两列合起来当一个主键)。
在这里插入图片描述3)一般来说一张表总有primary key,而且是整数类型的。
4)主键的指定方式有两种,如下:

  • 直接在字段名后指定。
  • 在表定义最后写primary key(字段)。

5)primary key就是not null和unique(唯一)的结合。
6)使用desc 表名,可以看到primary key的情况。或者show create table 表名都行。
总结:
  主键又叫主码,英文名叫primary key,是唯一(unique)的且不能为空(not null)的。它是用来唯一的标识表中的每一条记录,类似人的身份证号。并且一个数据表中只能有一个主键,我们可以使用主键来查询数据。

not null

  如果在列上定义了not null,那么当插入数据时,必须为列提供数据,保证了数据的完整性。

字段名 数据类型 not null

unique

  当定义了唯一约束后,该列值是不能重复的。

[constraint 约束名] unique(字段名)

特点:
1)如果没有指定not null,则unique字段可以为null。
2)能够可以有多个null。
3)一张表可以有多个unique字段。

外键约束(foreign key)

  用于定义主表和从表之间的关系:外键约束要定义在从表上,主表则必须具有主键约束或是unique约束,当定义外键约束后,要求外键列数据必须在主表的主键列存在或是为null。
  格式:foreign key (本表字段名) references 主键表名(主键名或unique字段名),references译为涉及的意思。
  如下是学生表:
在这里插入图片描述  有一个字段为class,它的所有数据是不是看起来很冗余,我能不能用数字来替代它们,这样也可以节省内存空间不是吗,比如数字6就表示高三六班,数字7就代表高三七班,可不可以?当然可以,如下:
在这里插入图片描述  这样看起来数据就比原先的简洁多了,看起来也比较舒服,但是查询的时候可读性就低了,别人不知道这个6代表什么意思?是不是就没有意义,那怎么办?在前面学习多表查询的时候,就连接那部分,相信就知道怎么做了,我们可不可以再创建一个班级表呀,如下:
在这里插入图片描述  这样,我们像查多表的时候不就可以把它查出来吗?如下:
在这里插入图片描述  虽然这样也可以,但是会造成数据不安全,比如student表的class字段,我要是添加一个数叫10呢。但是这个10代表哪个班级在班级表里有吗?是不是没有,也就是说,我们要保证数据的可靠性,不能对他人产生误导,所以,我们要对两表建立对应关系,用一条线把它们连起来,而这条线的产生就要由学生表来建立,说白了,就是在还没连起来之前,这两张表是没有一点关系的,而现在我们要让学生表的class字段以班级表为参考,就得让它们两产生关系,怎么产生,就是以class字段为基准点,创建一个外键,连接到clazz表的id字段。如下:
在这里插入图片描述  注意:语句没问题,但是报错了,那为什么不让连呢?其实,只是我没有保证它们具备相同的数据类型,我们的class字段是varchar类型,而clazz表的id字段为int字段,所以连不成功,那么我们是不是要统一数据类型呀,如下:
在这里插入图片描述  然后再说说它的sql语句,constraint翻译过来是约束的意思,就是要对学生表添加一个约束,并且要给它起一个约束名,虽然可以省略,但是最好还是给它起上,这个名就是上面的fk_class,再往后,就是按我前面说到的格式去写了。完整格式为:alter table 表名 add [constraint 外键约束名] foreign key(列名) references 引用外键表(列名),这条语句是在表已经建立好的情况下用的,如果是在建表的同时要指定,如下:
在这里插入图片描述  那么外键也建好了,解决了什么问题?是不是解决了我前面提到过的数据不安全问题呀,我们添加个10看看,如下:
在这里插入图片描述  外键我们也会建了,但要知道几个术语,有外键的那个表也就是student表,它是从表,那么clazz表就是主表,因为student表的class字段收到clazz表的约束,所以谁是主的,心里应该清楚。然后再删表的时候也要注意,应该先删从表,为什么呢?如果你把主表给删了,那么从表的字段要指向谁?数据不安全性是不是体现出来了。还有一点要注意,null是可以添加进去的,除非你不允许它为空,如下:
在这里插入图片描述细节:
1)外键指向的表的字段,要求是primary key或者是unique。
2)表的类型(存储引擎)是innodb,这样的表才支持外键。
3)外键字段的类型要和主键字段的类型一致(长度可以不同)。
4)外键字段的值,必须在主键字段中出现过,或者为null。
5)一旦建立主外键的关系,数据不能随意删除了。
总结:
  外键是用于管理两张表的,比如A表的主键在B表中以列的形式出现,那么这列数据就是外键。也就是说,表的外键是另一张表的主键。注意,外键可以重复,也可以为空,并且一个表可以有多个外键。
练习:
有如下表:
在这里插入图片描述1. customer_id设为主键:

alter table customer add primary key(customer_id);

2. 客户的姓名不允许为空:

alter table customer modify name varchar(32) not null default '';

3. 电子邮件不能重复:

alter table customer add unique (email);

4. 增加身份证也不能重复:

alter table customer add card_id varchar(30) not null unique;

5. 客户的住址默认是广州市白云区:

alter table customer modify address varchar(30) not null default '广州市白云区';

删除约束:
1. 删除主键约束:

alter table 表名 drop primary key;

2. 删除外键约束:

alter table 表名 drop foreign key 外键约束名;

3. 删除唯一约束:

alter table 表名 drop index 字段名;

10. SQL语句的执行顺序

  先创两张表,并添加数据,如下:
在这里插入图片描述
在这里插入图片描述  查询数据,如下:
在这里插入图片描述  结合上图两张表,有如下sql语句:
在这里插入图片描述

select distinct w.id,w.name,address,count(w.address),h.name
from wife w left join husband h on w.id=h.id
where w.name!="小宝" group by address having w.name!="小梦" order by w.id limit 4;

语句执行流程:
  1.看from,看看来自哪张表,一看,哦,是wife表和husband表,并且知道它们的别名,注意,它只看from有哪张表,暂时忽略是left join还是right join的情况,那么如下:

select * from wife w,husband h;

  不用说,结果是笛卡尔积,总记录是195条,我们把这个虚拟表叫做虚拟表1。
  2.看on,on后接的是不是连接条件呀,也是一个筛选条件,那么它会在虚拟表1的基础上筛选出符合连接条件的记录,形成一个新的虚拟表,我们叫它虚拟表2,如下:
在这里插入图片描述  3.回到from子句,看看是left还是right,很明显,是left join,在虚拟表2的基础上再次处理变成虚拟表3,如下:
在这里插入图片描述  4.对虚拟表3执行where子句,如下:
在这里插入图片描述  5.where后分组,如下:
在这里插入图片描述  6.分组后执行having,如下:
在这里插入图片描述  7.到having后,下一个执行的可不是order by,而是select,开始执行去重,count操作,如下:
在这里插入图片描述  8.接下来,才是order by,如下:
在这里插入图片描述  9.最后才是limit,最终语句执行结果就出来了,如下:
在这里插入图片描述总结:
  第一步:先执行from子句,但是要忽略什么left join,right join这种情况,否则执行不成功!
  第二步:执行on子句,防止笛卡尔积。
  第三步:到这步,才是看看是left还是right的情况,
  第四步:多表结合后才是执行where子句,把某些不符合条件的记录去掉。
  第五步:按照顺序,下一个就是分组,分组后才是having,筛选。
  第六步:分完组,过完滤,就要看看我们要查看的有哪些字段,比如我们不单单知道最终结果有哪些字段,还知道各个字段有哪些别名,如果有的话。同时因为之前已经分过组了,所以有聚合函数的字段是不是就可以正确的算出来呀!所以知道为什么select要放在group by后执行了吧!
  第七步:下一个,就是order by,排序,排完序,就是limit了,注意,limit永远是后执行的,仅次于order by。
细节:
  1. group by后有时会接with rollup,那么它就是排在group by后执行的。如果存在union关键字,那么它就是排在select后,order by之前执行。
  2. 如果是简单查询有些步骤就可以跳过。
  3. 明白这些执行的顺序,就明白where后到底可不可以接字段的别名了,也明白group by后可不可以接字段的别名了。答案就是where后不可以接字段的别名,而group by可以。

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值