1.MySQL引入
"""
------
流行蝴蝶剑
—-----
-conf:这个项目的配置文件
-core:核心逻辑;
-lib:公共的方法;
log:日志;
-bin:放置启动文件
-db:程序运行过程中产生的一些数据
readme.md:程序说明书
“”“
要将单机游戏,转变成为联网游戏,就是需要一个服务端存数所有客户端共有的数据部分,被称为数据库或是数据服务端。而这些向数据库发送请求的就被成为数据库客户端。也就是一个基于网络通信的应用程序,客户端发请求,服务端存储数据。客户端可以有多个,服务端只是有一个。所有客户端可以和服务端进行交互。
MySQL,Oracle,SQL server,DB2,Access
特点:
1)数据之间有关系或者约束(一张表的一条数据,和另一张表的多条数据有关系)
2)数据通常以表格的形式进行存储
1.3.2非关系型数据库
MongoDB,Redis,Memcache
存储数据通常都是key,value的形式。
mysql是一款基于网络通信的应用程序,底层一定是用的socket.
mysql服务端它支持编程语言来作为它的客户端,就是用代码来操作mysql数据库。它支持masql自己客户端语言来操作数据,也支持其他编程语言来作为客户端操作数据。怎么解决语言不同的问题?
--让服务端精通所有编程语言(写客户端的时候,进行判断,判断和数据库连接的客户端使用的是什么语言,切换成为一样的语言进行交互。
--同一语言,这个语言的语法由写这个数据库软件的人来定制(SQL语句)
https://www.mysql.com/进行官网,选择社区版,进行下载
点击MySQL Community Server进行下载
下载版本求稳不求新,避免新版本的兼容性问题。公司使用的是5.6或是5.7 。点击第2个压缩包进行下载。
将双击解压之后的mysql,移动到/usr/local路径下面,sudo是使用超级用户进行执行该操作。
使用 open mysql打开此文件夹
--mysql:它就是mysql客户端,我们要是连接客户端就是需要它
--mysqld:它就是mysql的服务端,不过我们启动服务端不是直接执行它
我们启动服务端使用的是support-files里面的mysql.server文件,启动的时候它会帮我们做一些配置,所以这些要执行的文件我们需要将其配置到环境变量里面。
mac系统的环境变量和windows系统的不太一样,它是通过配置文件来进行添加的。环境配置文件为:/etc/profile,这是系统级别的配置文件。
使用export进行配置:
$表示取值,表示取原来的PATH,因为系统会自带一些环境变量
在mac和linux里面环境变量的分割,使用的是:
$PATH:/var/bin:/usr/local/mysql/bin:/usr/local/mysql/support-files
修改配置文件之后不会立即生效,需要重启之后才会生效, 可以使用指令 source /etc/profile使得配置文件立即生效。现在再次输入mysql就不会再提示没有找到。而是显示ERROR,不能连接到本地mysql,这就表示我们环境变量配置成功了。
因为配置的是系统级别的环境变量,但是每次启动终端的时候,它加载的是用户级别的环境变量。所以还是需要执行一下 source /etc/profile.每次重启终端之后都要再次加载配置文件,很是麻烦。所以可以编辑用户目录下的配置文件.zshrc 里面只写一句话source /etc/profile,现在是真正配置好了环境变量。
环境变量配置好了之后,需要初始化数据库,还是需要超级用户来进行初始化。
由于配置了环境变量,可以在全局任意地方执行mysqld
--initialize表示初始化
--user=mysql:表示指定MySQL服务运行时,使用的用户身份,为了保证系统的安全,我们一般都会使用非特权用户,这个用户可以是任意用户,但是我们一般都会使用mysql
sudo mysqld --initialize --user=mysql
这里会给出一个初始密码,用户是root,地址是localhost,冒号之后的就是密码。
数据库初始化好之后,安装目录里面就会多一个data文件夹,这就是存放数据的地方。如果将这个文件夹删除了,那么所有的数据都会没有了。
这里显示SUCCESS,就是表示服务端启动成功了。
sudo mysql.server start
mysql:客户端执行文件
-h localhost:我们要连接的服务端地址
-P 3306:服务端的端口号,
在本地使用的时候,这两个参数可以不写,因为这是默认值,只有远程连接的时候,才需要进行指定。
-u:用户名
mysql -uroot -p
输入密码时,使用刚刚生成的密码,k5e,bM=5sm9a 粘贴进去。命令行这里显示mysql>就是表示我们已经连接上了
连接上之后的第一件事就是重置密码,不能使用其默认密码,因为我们记不住。而且不重置密码的话,它是不会让我们执行其他操作的。 这里的密码就是123。现在密码就重置好了
使用show databases;查看所有库,注意所有的sql语句都是以;结尾的
ALTER USER 'root'@'localhost' IDENTIFIED BY '123';
现在可以看见控制台里面存在mysqld服务。
输入sudo mysql.server stop之后服务停止。
sudo mysql.server stop
sudo mysqld_safe --skip-grant-tables
这样启动之后登陆的时候就不用输入密码了。
再次开启一个终端输入:就可以跳过密码验证直接登陆
mysql -uroot -p
修改的就是mysql数据库里面的内容
update mysql.user set authentication_string=password('123') where user='root' and host='localhost';
update mysql.user:修改mysql数据库里面的user表,用户相关的数据都在这里面
authentication_string:设置这张表里面的密码字段
password('123'):将用户传递的明文密码转为密文的形式
where user='root':判断是修改哪个用户的密码
host='localhost':判断这是是一个本地账号
之前也重置过密码为什么两次的指令不一样?
因为第一次连接数据库,我们是使用不了其它sql语句的,所以第一次修改密码不可以使用这种方式
现在root用户的密码就被强行修改了,我们是跳过授权进去的
flush privileges;
使用sudo启动的服务端,也要用sudo进行杀死
sudo mysql.server start
mysql -uroot -p
下载解压之后翻入文件夹中,但是不能存在中文路径.之后就是配置环境变量,将bin文件的路径加入到系统的环境变量里面.
初始化数据库时,一定要用管理员身份运行cmd输入命令
mysqld --initialize-insecure --user=mysql会生成,data文件夹,所有的数据库的数据都会存储在这里面.
初始化好之后就可以运行服务端,mysqld.exe,.exe也可以不写,服务端启动成功之后,服务端会在后台进行运行.
在开启一个cmd连接服务端,输入mysql -uroot -p 即可连接数据库,并且还是没有密码.但是现在的问题是,我的数据库没有密码并且还是root账户没有密码,很是危险.
输入mysqladmin -uroot -p password 123,[配置root账户密码为123
配置完成之后可以输入mysql -uroot -p123或者mysql -uroot -p再输入密码登录数据库
windows安装好MySQL之后,还是有一个问题,就是每次重启之后都是需要输入mysqld来重启服务比较麻烦.,如果重启之后不想在手动重启一次mysqld该如何做?可以将mysqld设置成为系统服务,系统服务就是说开机自启动.实现mysql服务端开机自启动.
使用mysqld -install和mysqld -remove
输入net start mysql启动服务,这行代码是用来启动服务的,mysql是服务的名字.因为安装的时候内有给服务设置名字.服务默认的名字就是mysql,这里不要和客户端混淆了.会发现启动的服务会在任务管理器里面体现出来.
如果发现端口被占用可以使用netstat -ano | findstr 3306查询占用端口的进程为6916
可以输入taskkill /F /PID 6916 杀死进程;/F为强制,/PID是进程号
打开服务的时候发现服务的启动类型是自动,下次系统起来的时候,这个服务就会起来.以后开机的时候mysql就会自动启动.我们操作数据库的时候直接连接就行.
对于mac系统而言,查询字符编码:进入mysql之后,输入\s,现在显示的还是存在两个latin1文。
对于mac上的mysql上面来说,要修改字符编码的话,同样是要修改配置文件,存放位置和centos一样。/etc/my.cnf
/etc 目录是一个系统级别的目录,所以需要使用超级用户打开。
sudo vim /etc/my.cnf
输入内容:
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
输入 sudo mysql.server stop :停止服务
输入 sudo mysql.server start: 开始服务
再次输入mysql -uroot -p进入数据库
输入\s结果如下:全部变为utf-8编码
windows下面的配置文件放置在根目录,创建配置文件my.ini
配置文件内容如下:
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
如何使用本机的mysql客户端,连接两外一台主机的mysql服务端
两台主机之间。。。。。
每一条sql都是以;结尾的,如果是不添加;则表示此条sql语句还是没有结束。
库:对应的就是文件夹
表:对应的就是文件夹里面的文件
记录:就是对应文件里面的一行行数据
那么这里的四张表就是对应四个文件夹
显示安装目录下面的文件
sudo ls /usr/local/mysql/data/
可以看到只有三个文件,因为information_schema 是虚拟文件夹,它是存放在内存里面的。里面都是数据库启动之后的信息。比如数据类型和访问权限等
mysql:主要就是一个授权表,我们修改密码的时候就是修改里面的内容
performance_schema:提供一些数据监控和诊断功能,可以用来分析数据库的性能
sys:数据库引擎的扩展库
单行注释:--
多行注释:/**/
\s:查看数据库字符编码以及其它信息
\c:结束当前语句
exit:退出连接
quit:退出连接
help:查看命令的帮助信息
我们现在涉及到经常性的退出和连接数据库,每次都要输入mysql -uroot -p,会有点麻烦。可以简化
编辑/etc/my.cnf文件,添加配置项目如下
mysql自己的客户端连接的时候就会执行下面的配置,我们配置了之后就不需要再次输入 -uroot -p,密码也不再需要输入
当然修改配置文件之后,需要重启mysql服务
但是这个配置只是针对客户端有效,并且是本机的mysql客户端.
db1是数据库名字,
create database db1;
create database db2 charset=gbk;
结果:在文件夹里面确实可以看到新增了文件夹db1,查询所有数据库,确实发现了db1
过程:
我们在客户端输入了一条命令,这个命令通过网络传输到了服务端,服务端在本地操作文件。所以说数据库就是一个远程操作文件的程序。我们操作本机的数据库也是一样的,只不过客户端和服务端都是在本地而已。
最好在创建数据库的时候加上字符编码,因为我们自己配置了编码格式utf8,即使不写,也会使用utf8编码,但是如果代码拿到别的电脑上面进行运行就是很有可能会出现问题。
drop(减少)
drop database db2;
对于数据库而言其实没有什么改的,能修改的只能是字符编码。alter(改变)
alter database db2 charset=utf8;
show databases; -- 查询所有数据库
show create database db1; -- 查一个,查看创建这个数据库的sql语句
-- 增
create database if not exists db1 charset=utf8;
-- 删
drop database if exists db1;
数据库已经存在,那么重复创建database就是会报错,现在添加判断条件就不会报错
--增
create database [if not exists] <库名> [charset=utf8];
--删
drop database [ if exists] <库名>;
--改
alter database <库名> charset=utf8;
--查
show databases;
show create database <库名>;
如果输入的时候想不起某一个命令如何使用就可以使用help + 命令
表是文件,文件一定是放在一个文件夹里面的,也就是说数据表一定是放在一个数据库下面的。所以我们在操作表的时候一定要指定所在的库。
select database(); -- 查询当前所在库的名字
use db1; -- 切换数据库
现在数据库切换到db1之后,所有对于表的操作增删改查都是操作db1了
新增表的时候,要设置表的结构,除了要设置表里面的字段,每一个字段都要指定它的类型
create table movies(id int,name char); -- 创建表(默认字符编码,就是库的字符编码)
create table movies(id int,name char)charset=utf8;--创建表
创建之后,db1数据库里面就多出来了两张表。为什么新建一张表,会多出两个文件?
这就是mysql存储数据的方式,它就是将一张表存储成为两个文件。frm存储的就是表的结构,也就是我们定义的字段,ibd储存的就是数据
drop table movies; -- 删除movies 表
输出db1.movies不存在。我们操作表的时候,也可以这样做。如果我们当前所在数据库不是db1,但是又想操作db1,就可以使用这种写法。类似于python中的绝对路径
alter table movies modify name char(4); -- 修改字段类型,数字4就是字段的宽度
alter table movies change name Name char(4); -- 修改字段名和类型
修改字段类型:
show tables; -- 查看当前库下面所有的表
show create table movies; -- 查看创建表的sql语句
describe movies; -- 查看一张表的结构,简写:desc movies;
输出结果:
这里的CREATE TABLE `movies`之所以添加反引号是为了防止和sql语句里面的关键字冲突,而且这里的大小写是没有关系的,sql对于大小写不敏感。
(
`id` int(11) DEFAULT NULL,
`name` char(1) DEFAULT NULL
)这里是设置的字段
CHARSET=utf8:设置表的字符编码,如果创建表的时候不指定这个编码,它会默认使用创建库的时候使用的编码。如果你创建库的时候也没有设置编码,它就会使用你配置文件里面的编码。配置文件也没有的话,直接使用默认的,就是latin的那个。
表结构查询结果:
所有对于表的操作,如果我们没有切换到对应的数据库里面,都可以使用绝对路径的方式。这样即便不切换数据库也可以操作数据库对应的表
insert into movies values(1,"流浪地球“); -- 插入一条记录
insert into movies values(2,"流浪地球"),(3,"三体"); -- 插入多条记录
过程:切换库到db1->创建表movies->插入失败->修改表字段name的宽度->重新插入
插入时出现报错:
mysql> insert into movies values(1,"流浪地球");
ERROR 1406 (22001): Data too long for column 'name' at row 1
是因为我们创建表的时候,给name字段设置最多只能存储2个字符长度。但是”流浪地球"字段长度为4
插入单条记录:
插入多条记录:
插入的值为多个即可
删除的操作大概率只是会在学习的阶段或者调试阶段用到,在项目里面,即使是客户做出了删除的操作,但是在我们的记录里面不会是真的删除掉。而是会给这个数据打一个标记。表示这个数据已经被删除了,下次不要再展示了。
delete from movies; -- 清空这张表
delete from movies where name="三体“; -- 将三体这条记录删除掉
update movies set name="满江红" where id=1; -- 修改表movies里面的字段name改为满江红,只是针对id为1的记录
select * from movies; -- 查询这张表的所有字段,在数据量太大的时候不要使用
select id,name from movies; -- 查询这张表的素有id和name字段
select user,host from mysql.user; -- 查询user表里面的所有user和host字段,在db1里面操作user表,必须使用绝对路径
select name from movies; -- 查询所有数据的单个name字段
类型 | 描述 | 关键字 |
DDL | 数据库定义语言,用来定义和管理数据库或是数据表 | create,alter,drop |
DML | 数据库操作语言,用来操作数据 | insert,update,delete |
DQL | 数据库查询语言,用来查询数据 | select |
DCL | 数据库控制语言,权限控制 | grant,revoke,commit,rollback |
在mysql里面创建数据库就是创建文件夹,创建数据表就是=创建文件。如果我们自己写一个数据库软件,来处理文件,那么针对不同类型的数据,应该使用不同类型的文件来处理。在数据库里面表就是文件,那么表肯定存在不同的类型。
在mysql里面表的类型,就是存储引擎。对于不同的存储引擎,它提供了不同的处理机制。
show engines; -- 查看所有存储引擎
create tabel t1(id int,name char)engine=innodb;
create table t2(id int,name char)engine=memory;
create table t3(id int,name char)engine=blackhole;
create table t4(id int,name char)engine=myisam;
数据库里面的内容为:
1. t1是通过innodb创建的,它对应两个文件一个是表结构t1.frm,一个是表数据t1.ibd(innodb缩写)
2. t2是通过memory创建的,它的数据存在内存,不会存到硬盘,所以这里只有一个表结构t2.frm。并没有它的数据
3.它是通过blackhole创建的,它是不存储数据的,它也只有一个表结构t3.frm.
4.t4是通过myisam创建的,它对应三个文件,t4.frm还是表结构,t4.MYD(myisam data),t4.MYI(myisam index)是其索引文件,myisam支持全文索引。全文索引是一种用于快速搜索文本数据的索引技术,它可以对文本数据中的每个单词进行分词,并将这些单词与其所在的数据行建立索引关系。当用户使用关键词进行搜索的时候,系统可以快速的查询全文索引,找到包含关键词的文档或是数据行。通常用于搜素引擎。
insert into t1 values(1,"a");
insert into t2 values(1,"a"); -- 存在内存,内存特点断电丢失,或者mysql服务端停止运行了。进程结束了,内存回收,数据也就是没有了
insert into t3 values(1,"a");
insert into t4 values(1,"a");
重启mysql服务端,发现t2数据表里面的内容都已经丢失
sql语句是可以换行的,;才是一条sql语句结束的标志。在同一张表里面,字段名是不可以重复的
注意:这个宽度不是字节,而是字符个数,或者是字符串长度,是我们在python里面使用len()查询的字符长度。
create table <表名> (
<字段名1> <字段类型>[(宽度)] [约束条件],
<字段名2> <字段类型>[(宽度)] [约束条件],,
<字段名3> <字段类型>[(宽度)] [约束条件1 约束条件2]
);
-- 宽度指的是字符个数,或者说是字符长度
-- 约束条件,注意顺序
[unsigned] |[zerofill]| [not null ]
--unsigned :无符号
--zerofill:0填充
--not null : 非空
alter table <表名> engine=<存储引擎名字>;
alter table <表名> rename <新表名>;
alter table <表名> add <字段名> <字段类型> [(宽度)] [约束条件] [first | after <字段名>];
尾部增加字段:
指定位置增加字段:通过after和first进行实现
alter table <表名> drop <字段名>;
alter table <表名> modify <字段名> <新字段类型>[(宽度)] [约束条件];
alter table <表名> change <旧字段名> <新字段名> <新字段类型>[(宽度)] [约束条件];
所以约束条件到底是什么呢?
这里的Null就是空,类似于python里面的None。id 和 name两个字段都是YES,就是表明这两个字段都是可以为空的,都是可以插入空。name的字段限制了宽度为1,为什么这里可以插入null?这里的null它不是一个字符串,而是一个关键字。null这项为yes,这其实就是一个约束条件。
修改字段类型或是约束条件的时候,如果表里面已经有数据了,而且有不符合你想改的约束条件。这个时候如果你想改类型或是约束条件的话,就是改不了的。比如说t1表里面已经有了一个null,再想将约束条件改为非null数据就是修改不了的。
使用约束条件,创建table:
约束条件应用场景:
drop table <表名>;
create table <新表名> select * from <旧表名>[条件];
create table <新表名> like <旧表名>;
复制表结构和表记录:
只是复制表结构:
id=1就是一个表达式,它就是一个条件,它的结果就是一个布尔值,所以是一个布尔值,他就可以随便写。查不出数据,它就是空,就可以直接复制表结构.
除此之外,还是有更加简单的方式,创建的表像是,就可以直接复制表结构.
create table t1 select * from movies where id=100;通过筛选出来的值为空创建表结构.
create table t2 like movies;直接复制表结构
一个Byte有8个bit,对于tinyint而言,就是一位用来表示符号,剩余7位表示大小.无符号就是8位都是用来表示大小;
以tinyint举例,默认指定tinyint类型时,使用的范围是有符号的.并且如果插入的记录超出了范围,直接会提示报错.
类型 | 大小 | 范围(有符号) | 范围(无符号) | 描述 |
tinyint | 1 Byte | (-128,127) | (0,255) | 很小的整数 |
smallint | 2 Byte | (-32768,32767) | (0,65535) | 较小的整数 |
mediumint | 3 Byte | (-8388608,8288607) | (0,16777215) | 一般的整数 |
int | 4 Byte | (-2147483648,2147483647) | (0,4294967295) | 标准的整数 |
bigint | 8 Byte | (-9223372036854775808,9223372036854775807) | (0,1844744073709551615) | 较大的整数 |
默认添加tinyint数据类型为有符号
alter table t1 add m tinyint unsigned; --无符号添加字段
添加字段m之后之前加入的n数据内容,在字段m上都为Null.如果设置约束条件不可以为null,那么字段是数字就用0填充,字段是字符串就用空字符串填充.如果not null约束条件和unsigned一起用的话,unsigned一定要放在前面.
数字0填充和空字符串填充
not null 不能在unsigned前面
整型的存储宽度是固定死的,只有表里面的5种.在指定整型数据类型的时候给定宽度,那只是修改了显示宽度.
指定的显示宽度,如果显示宽度不足会用0填充,但是没有设置填充效果的话看不出来.如果插入的记录超过了显示宽度,那么该多长还是显示多长.显示宽度根本影响不了它.所以说整型的显示宽度一点用也没有.
int类型无符号的最大显示宽度是10,有符号的是11.其实就是int类型显示的最大数.它的长度就是10.默认显示的宽度就是很合理的,所以根本不需要我们去设置它的显示宽度.
除了整型类型之外,所有的宽度指的都是存储宽度.
zerofill填充后的效果:
int类型默认显示宽度
E+38就是小数点往后移动38位,E-38就是小数点往前移动38位。
十进制小数转二进制就是:乘2取整法
3.625
3 -> 11
0.625 -> 0.625x2 = 1.25 0.25x2 = 0.5 0.5x2=1->0.101
3.625 -> 11.101
在大多数编程语言里面浮点数分为两种,单精度浮点数和双精度浮点数
浮点数表现形式:符号,尾数,基数,指数
+/-m*2^e,由于基数2是固定的,所以只是需要2进制数表示出来符号尾数和指数就可以将浮点数进行存储了。
单精度浮点数(32位):符号(1位)+指数(8位)+尾数(23位)
双精度浮点数(64位):符号(1位)+指数(11位)+尾数(52位)
符号:用第一位数字表示正负,1->负,0->正或是0
尾数: 把小数点前的二进制数,固定为1的规则
类型 | 大小 | 范围(有符号) | 范围(无符号) | 描述 |
float | 4 Bytes | (-3.402823466 E+38,-1.175494351 E38),0 (1.175494351 E-38,3.402823466351E+38) |
0,(1.175494351 E-38,3.402823466E+38) | 单精度浮点值 |
double | 8 Bytes | (-1.797693134862315E+308,-2.225 0738585072014 E-308),0,(2.225073858507201E-308,1.797693 1348623157E+308) |
0,(2.225073858507 2014E-308,1.797693134862 315E+308) |
双精度浮点值 |
decimal | decima(m,d) | 依赖于m和d的值 | 依赖于m和d的值 | 小数值 |
字符类型分类很多,只要了解两种
- char (定长字符串)
char(10)最多存储10个字符,如果超过10个字符,会直接报错,如果不足10个字符,空格补齐。
缺点:浪费空间,不论是几个字符,都会存储成为10个字符
优点:存取速度快
- varchar(变长字符串)
varchar(10)最多存储10个字符,如果超过10个字符,会直接报错,如果不足10个字符,直接存储。
优点:节省空间
缺点:存取速度慢,但是慢是相对于计算机而言的,因为节省空间,所以一般都是使用varchar
char_length():是mysql的内置函数,可将返回传递过去字符的长度
插入两组数据之后,发现长度都是一样的,这是因为char的name数据在硬盘上面存储的确实是带有空格的,但是经过查询之后,它会自动去除多余的空格,所以在使用的时候是感受不到有空格存在的.插入数据的时候补空格是补充在数据后面的,如果数据本身后面是带有空格的,那么在去掉空格的时候,会将数据本身的空格也认为是它补充的空格一起去掉.所以数据尾部如果是有空格的话,最好是使用varchar.
这里t5第二个数据长度为3的原因是将后面的两个空格全部去除掉了.
宽度就是字符个数,char类型最多可以存储255个字符
对于varchar而言最多存储65535个字节,,但是我们在实际使用的时候要减少3个字节,因为它需要一个字节来标识是否可以为null,而且varchar的每一个数据都要有个头,用来记录长度标识.当数据长度小于255的时候,头部占据一个字节.255<数据<65535时,头部占据2个字节.所以我们可以使用的只有65532个字节.但我们在指定宽度的时候,指定的是字符个数.这个字符的个数受到字符编码的影响.gbk一个字符占据2个字节,它的范围就是65532/2=32766字符,如果是utf8,一个字符占据3个字节
65532/3=21844字符.
varchar(变长字符串)
最大字符数=(65535-行其他字段总字节数量-null标志字节-长度标志字节)/字符集单个字符最大字节数.
虽然我们的varchar最大是21844,但是前提是这一行只有它一个字段.如果有其他字段,他们是共享这个65535字段的 .现在新增了一个id字段是int类型,int类型是4字节,char是utf8编码的所以是3字节.所以最少是要减少掉两个字符才能将其存储进去.
同时varchar的长度并不是越长越好,我们在定义长度的时候,最好只分配我们需要的空间.如果数据长度不超过255的话,我们定义的长度就不要超过255,超过255头部就会占据2个字节.
时间日期就没有宽度的概念,它的格式都是固定的.
year,年
date,年-月-日
time,时:分:秒
datetime 年-月-日 时:分:秒
8字节,1000~9999
timestamp
4字节,1970~2038
使用mysql的内置函数now(),会自动获取系统时间,有因为存储的数据类型不同,会根据数据类型进行存储. 插入指定的时间,只需要将日期处理成为字符串即可
enum:enumerate的缩写,单选,从给定的范围内选择一个值,插入其他值报错
set:表示多选,可以从给定范围内选择一个或是多个,不再范围内的则是会报错.
[unsigned] |[zerofill] 放置在前面
[unsigned] |[zerofill]| [not null ] [ default
--unsigned :无符号
--zerofill:0填充
--not null : 非空
--default:默认值
--unique key:唯一
其实严格来说unsigned和zerofill都是属于字段类型的,int是强制约束这个字段必须是整型,unsigned进行进一步约束,它是无符号的,接着进行0填充.而not null约束,它是对于字段的约束.所以我们在写约束条件的时候,约束条件要写在前面,字段约束要写在后面.
Null:默认为yes,是可以为空的.只不过这里指定了not Null,指定非空的时候,要给出一个默认值就是Default(默认为NULL),可以通过default 设置默认值
create table t2(id int,name varchar(10),gender enum("male","female") not null default "male");
插入数据的时候可以通过指定字段,不插入默认值来实现数据的插入
insert into t2(id,name) values(1,"zz");
我们往表里面存储的数据默认是可以重复的.如果想让字段唯一,不重复.就需要另外一个约束条件.unique key:唯一 场景(用户名唯一)
--单列唯一unique
--指定id和name唯一
create table users(id int unique,name varchar(10) unique);
create table users(id int,name varchar(10),unique(id),unique(name));
单个是可以重复的,联合在一起却是唯一的.比如ip+端口,ip是可以重复的,端口是可以重复的但是ip+端口必须是唯一的.
--unique(host,port),就可以实现联合唯一
--联合唯一,两组一样的值是插入不进去的
create table route(id int,host varchar(15),port int,unique(host,port),unique(id));
primary key:主键(不为空,且唯一),对于innodb这个存储引擎来说,每一张表都必须要有一个主键..主键还可以帮助我们组织数据,提高查询速度.注意:一张表必须有一个主键.为什么前面建表的时候,没有指定主键也没有出现问题?
如果我们没有指定主键,mysql会查询我们的字段,看看有没有不为空且唯一的字段.如果有的话,就将其作为主键.如果没有找到,mysql就会自动帮我们创建一个隐藏的主键,但是隐藏的主键,对于我们而言并没有实际的作用,他只是为了方便组织表的数据.而且我们是要用主键来加速查询的.
所以在建表的时候我们一定要自己指定一个主键
--主键是唯一且非空的
--主键实现方式是建表加入primary key
create table t1(id int primary key,name varchar(10));
之前说过int 类型设置为非空,默认会使用0进行填充.但是这是在新增字段的条件下,而不是说插入数据的时候,不设置数据会被default 0进行填充.
如果我们在创建表的时候没有设置主键,就会将非空且唯一的数据作为主键.所以主键的
约束特性: 就是非空且唯一.所以现在创建每一张表,我们都应该给它一个id字段,并且将id字段作为主键.现在我们使用的主键也可以叫做单列主键
复合主键:就是前面的两个字段联合起来,作为一个主键
--复合主键
--复合主键的实现方式类似于联合唯一,使用primary key(host,port)
create table t3(id int,host varchar(15),port int,primary key(host,port),unique(id));
之前说过每一张表都应该有一个id字段,我们一般都是将id设置成为主键,用来标示每一条记录.但是我们每次插入数据的时候都需要加上id,但是id并不属于数据.所以可不可以不插入id,让其自动生成呢?
创建表的时候,给主键id指定,自动递增
--自动递增
--设置primary key时设置 auto_increment
create table t1(id int primary key auto_increment,name varchar(16));
--插入数据时候要注意指定字段名,否则会报错
insert into t1(name) values("hh"),("rose"),("jack");
--但是设置自动递增之后也是可以指定id进行插入的,并且递增是在上一条记录上面增加的
insert into t1 values(10,"ss");
递增是在上一条记录上面增加的
trancate:截断,舍弃.
-- 清空数据,不清空自增值
delete from t1;
--同时清空数据和自增值
trancate t1;
清空表可以通过以上两种方式实现,如何查看自己的自增值是否发生改变呢?
--查看表的创建,里面存在自增值,即插入下一个数据的id号
show create table t1;
自增值为14,插入下一个数据id为14.
tranctae t1;之后数据表的记录和自增值都被清空,从1开始.再次插入数据,数据的id号码为1.
之前就是说过mysql是关系型数据库,现在开始表与表之间建立关系.也算是另外一种约束条件.
foreign key:外键约束
id | name | gender | mobile | dep_name | dep_desc |
---|---|---|---|---|---|
1 | 张三 | male | 123 | 研发部 | 造火箭 |
2 | 李四 | female | 456 | 销售部 | 卖火箭 |
3 | 王五 | male | 789 | 销售部 | 卖火箭 |
4 | 赵六 | male | 012 | 人事部 | 裁员的 |
5 | 秦七 | female | 345 | 人事部 | 裁员的 |
这样存储的信息存在大量的重复信息,并且以后数据修改也会很麻烦.使用解耦合的思想,将部门表拆分出来即可.并且使用dep的主键,就和emp表之间建立了关联
我们使用dep_id使得两张表之间建立了关联,那么dep_id是不是一定要来源于dep表呢?原则上是这样的,但是我们也可以不遵循.但是这样肯定是不合理的.所以对于dep_id应该要有一定的约束,它必须来自于部门表里面存在的id,如果存储了部门表里面不存在的id,它就会直接报错.要实现这个效果,就需要用到外键.
id | name | gender | mobile | dep_id |
---|---|---|---|---|
1 | 张三 | male | 123 | 1 |
2 | 李四 | female | 456 | 2 |
3 | 王五 | male | 789 | 2 |
4 | 赵六 | male | 012 | 3 |
5 | 秦七 | female | 345 | 4 |
--创建部门表
--针对字段desc是关键字,需要使用反引号
create table dep(
id int primary key,
name varchar(16),
`desc` varchar(64)
);
--创建人员表
--创建外键约束关联dep表的id字段
--现在的创建的表要关联部门表,但是部门表根本不存在,所以需要先创建部门表
create table emp(
id int primary key,
name varchar(16),
gender enum("male","female"),
dep_id int,
foreign key(dep_id) references dep(id)
);
插入人员表数据时,一定要保证部门表里面是存在数据的,否则关联的部门表里面的数据若是不是存在的话,会直接报错.
--先插入部门表的信息
insert into dep values(1,"研发部","造火箭的"),(2,"销售部","卖火箭的"),(3,"人事部","裁员的");
--再插入人员的信息
insert into emp values(1,"张三","male",123,1),(2,"李四","female","456",2),(3,"王五","male",789,2),(4,"赵六","male",012,3),(5,"秦七","male",345,3);
直接删除部门表里面的信息会提示"外键约束错误"报错,因为部门的id在emp表里面有引用.就类似我们电脑里面的文件处于打开的文件,我们删除的话,电脑就会提示文件被占用.所以删除dep表里面的内容,得先将emp表里面引用的人全部删除才能过引用.
现在删除部门表的信息还要先删除所有人员的信息.有没有方法删除部门表的信息之后,自动让所有人员表里面的信息自动删除.
我们现在是可以更新部门表里面的字段,但是同样原因id被引用,所以无法被修改.
--更新dep表里面的部门
update dep set name="市场部" where id=2;
--更新id,失败
update dep set id=100 where id=3;
解决的方式就是在创建表的时候,设置外键删除同步,更新同步
--删除emp人员表
drop table emp;
--重新创建表,并且设置外键删除同步,更新同步
create table emp(
id int,
name varchar(16),
gender enum("male","female"),
mobile int,
dep_id int,
foreign key(dep_id) references dep(id)
on delete cascade
on update cascade
);
--重新插入被删除的研发部信息
--指定id之后,会自动排序
insert into dep values(1,"研发部","造火箭的");
--重新插入人员信息
insert into emp values(1,"张三","male",123,1),(2,"李四","female","456",2),(3,"王五","male",789,2),(4,"赵六","male",012,3),(5,"秦七","male",345,3);
--现在删除emp表的内容,人员表也会自动删除,更新也是一样的
delete from dep where id=2;
update dep set id=999 where id=1;
创建表emp,并设置删除和更新同步
删除部门id=2
修改部门id为999
我们好不容易将两张表解耦合之后,现在又使用foreign key本质将两张表耦合在一起了.虽然删除同步,更新同步,但是两张表始终都是强关联的.所以以后写大型项目的时候,一定不要使用外键.一切外键概念必须在应用层解决.这里使用foreign key建立的外键,可以理解为物理层的强关联,用起来很不灵活. 但是对于小型项目而言,外键结合django的orm就很香了,外键随便使用.
之前说过foreign key是用来给表与表建立关系的 .这个关系其实还是有几种分类
左边表的多条数据对应右边表的一条数据,设计表的时候要站在两边进行观察.一边是多对一的关系,另外一边一定不是多对一的关系.外键就要建立在多的那一边类似于前面的dep表和emp表之间的关系.
id | name |
1 | 以父之名 |
2 | 夜的第七章 |
3 | 止战之殇 |
4 | 夜曲 |
5 | 北京欢迎你 |
id | name |
1 | 周杰伦 |
2 | 刘欢 |
3 | 韩红 |
4 | 成龙 |
id | signer_id | song_id |
1 | 1 | 1 |
2 | 1 | 2 |
3 | 1 | 3 |
4 | 1 | 4 |
5 | 2 | 5 |
6 | 3 | 5 |
7 | 4 | 5 |
--创建song表
create table song(
id int primary key,
name varchar(16)
);
--创建singer表
create table singer(
id int primary key,
name varchar(16)
);
--创建song2singer表
create table song2singer(
id int primary key,
signer_id int,
song_id int,
foreign key(signer_id) references singer(id)
on delete cascade
on update cascade,
foreign key(song_id) references song(id)
on delete cascade
on update cascade
);
--song表插入数据
insert into song values(1,"以父之名"),(2,"夜的第七章"),(3,"止战之殇"),(4,"夜曲"),(5,"北京欢迎你");
--singer表插入数据
insert into singer values(1,"周杰伦"),(2,"刘欢"),(3,"韩红"),(4,"成龙");
--song2singer表插入数据
insert into song2singer values(1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,2,5),(6,3,5),(7,4,5);
为什么多对多的删除了singer只是song2singer的表同步删除,但是song表里面没有变化?
存在客户表和业主表,潜在的客户表最终发展成为业主.多个客户不能发展成为同一个业主,多个业主也不可能是由同一个客户发展而来的.一个客户只能发展成为一个业主.一个业主只能由一个客户发展而来,这就是一对一的关系.现在要建立关系,不管是什么表,只要是建立关系,就是foreign key.理论上外键建立在哪里都是可以的,但是如果外键建立在customer表里面,要插入数据必须owner表里面先存在数据.所以结合实际,外键应该建立在owner表里面.
foreign key只能保证owner表的信息来自于customer,但是不能保证数据是唯一的,加上unique约束.
id | name | gender | mobile |
---|---|---|---|
1 | 张三 | male | 123 |
2 | 李四 | female | 456 |
3 | 王五 | male | 789 |
4 | 赵六 | male | 012 |
5 | 秦七 | female | 345 |
--创建customer表
create table customer(
id int primary key auto_increment,
name varchar(16) not null,
gender enum("male","female"),
mobile int not null
);
--创建owner表
create table owner(
id int primary key auto_increment,
room_num int not null,
area int not null,
is_loan enum("TRUE","FALSE"),
customer_id int unique,
foreign key(customer_id) references customer(id)
on delete cascade
on update cascade
);
--插入custoer数据
insert into customer(name,gender,mobile) values("张三","male",123),("李四","female",456),("王五","male",789),("赵六","male",012),("秦七","female",345);
--插入owner数据
insert into owner(room_num,area,is_loan,customer_id) values(688,300,"FALSE",2),(233,180,"TRUE",4);
select distinc <字段....> from <库名>.<表名>
where <过滤条件>
group by <分组条件>
having <过滤条件>
order by <排序字段> {ASC | DESC} --默认升序ASC
limit n;
--创建员工表
create table emp(
id int primary key auto_increment,
name varchar(16) not null,
gender enum("male","female") not null,
age int not null,
salary float(10,2),
dep varchar(32),
notes varchar(64)
);
-- 插入内容
insert into emp(name,gender,age,salary,dep) values
("关羽","male",20,8000,"技术部"),
("张飞","male",18,7000,"技术部"),
("刘备","male",28,9000,"技术部"),
("赵云","male",15,10000,"技术部"),
("曹操","male",28,12000,"研发部"),
("荀彧","male",21,9000,"研发部"),
("许攸","male",20,8000,"研发部"),
("夏侯惇","male",20,7000,"研发部"),
("孙权","male",10,6000,"人事部"),
("周瑜","male",20,10000,"人事部"),
("小乔","female",20,8000,"人事部"),
("大乔","female",22,8500,"人事部");
通过关键字distinct实现去重,在筛选的字段前面加上即可.但是需要注意的是,这个去重是去除筛选记录的重复,如果筛选出来的内容一整条里面,只有部分是重复的,就无法去重.
--去重
select distinct emp from emp;
我们查询出来的记录可以做加减乘除运算
--四则运算
--计算出年薪并且修改名字
select name,salary*12 as yearly_salary from emp;
将查询的数据设置成为需要的格式,使用concat()函数和concat_ws()函数
-- 设置显示格式
-- 姓名: 关羽 年龄:20 薪资:8000
-- 直接使用concat进行拼接
select concat("姓名:",name," 年龄:",age," 薪资:",salary) from emp;
-- 重命名
select concat("姓名:",name," 年龄:",age," 薪资:",salary) as "姓名 年龄 薪资" from emp;
-- 直接使用concat_ws()进行拼接,第一个参数是拼接符号
select concat_ws("-",name,age,salary) as "姓名-年龄-薪资" from emp;
concat查询:
concat查询并重命名:
concat_ws()查询:
我们的查询语句是有优先级的 from table -> where 条件 ->select 执行查询
where的条件查询支持not and or
where条件查询也支持in 范围查询,is null 空数据查询, like 模糊查询
-- where单条件查询
select name, age from emp where age> 25;
-- where多条件查询
select name, age, dep from emp where age>=20 and dep="技术部";
-- where 区间查询支持between(20<=X<=25)
select name, age from emp where age between 20 and 25;
-- where or条件查询
select name, age from emp where age<20 or age>25;
-- where not条件查询
select name, age from emp where age not between 20 and 25;
-- 找到工资为7000 8000 9000的员工
select name, salary from emp where salary=7000 or salary=8000 or salary=9000;
-- 使用in范围值查询
select name, salary from emp where salary in (7000,8000,9000);
-- is null判断数据是否为空,注意空字符串不是null
select name, notes from emp where notes is null;
-- is null 取反
select name, notes from emp where notes is not null;
-- like(模糊匹配)
-- like里面的_类似于正则表达式里面的.
-- like里面的%类似与正则表达式里面的.*
select name from emp where name like "荀_";
in 范围查询:
is null 空数据查询:
模糊查询:
group by是在where之后执行,书写sql语句的时候,也要遵循这个顺序
from table -> where 条件查询 -> group by进行分组 -> select 执行查询语句
分组的目的是分组之后统计每一组的数据,我们分组之后要操作的就是组了.对某一组数据进行统计的话,就需要使用聚合函数了
作用就是从某一组中聚合出我们需要的结果,聚合函数如下
count,我们可以使用任意字段进行统计但是前提是统计的字段里面不能有null.
max.min,sum,avg
如果我们对查出来的数据不做分组操作,它默认就是一组,查出来的所有数据就是作为一组.聚合函数针对的是组.所以我们不使用group by也是可以使用聚合函数的.
-- 统计每个部门员工的数量
select dep, count(dep) as dep_count from emp group by dep;
-- 统计每个部门年龄最大的员工
select dep, max(age) as max_age from emp group by dep;
-- 统计每个部门工资最低的员工
select dep, min(salary) as min_salary from emp group by dep;
-- 统计每个部门总工资
select dep, sum(salary) as sum_salary from emp group by dep;
-- 统计每个部门平均工资
select dep, avg(salary) as avg_salary from emp group by dep;
-- 统计所有员工的平均工资
select avg(salary) from emp;
对分组内容进行拼接
-- 查询每个部门所有员工的名字
select dep, group_concat(name) from emp group by dep;
-- 统计每个部门年龄大于20的员工数量
select dep, count(name) as dep_count from emp where age>20 group by dep;
-- 统计男员工和女员工的数量
select gender, count(id) as gender_count from emp group by gender;
having过滤是和where干的一样的事情,只不过它的执行顺序是在分组之后的,where是在分组之前进行过滤的
注意:分组之后的结果在被执行时一定要使用聚合函数,不然会报错
-- 查询所有部门内,员工数量小于4的部门名,以及该部门内的员工名和员工数量
select dep, group_concat(name) as name, count(id) as emp_count from emp group by dep having count(id)<=4;
-- 查询各部门年龄大于20的员工超过2个人的部门名,以及大于20的人数
-- 此种方式会报错,因为分组之后没有age字段了
select dep, count(id) from emp group by dep having age>20 and count(id)>2;
select dep, count(id) from emp where age>=20 group by dep having count(id)>2;
排序只有两种,降序和升序.默认就是会按照id升序 ,升序就是asc,降序就是desc.排序时可以指定第二个字段.对于工资相同的内容可以使用第二个字段进行排序.
-- 对于所有员工,按照员工工资进行排序
select * from emp order by salary;
-- 对于所有员工,按照员工工资进行排序-降序
select * from emp order by salary desc;
-- 对于所有员工,按照员工工资进行排序-工资相同按照年龄的升序
select * from emp order by salary,age;
它的作用就是用来限制展示记录的条数
也可以实现分页功能,每一页只是显示几条到十几条记录,当用户点击下一页的时候,再显示下一页的数据.虽然直接使用limit可以实现分页,但是实际项目中并不是那么简单.实际项目中会用到一些缓存机制.
-- 查询所有员工信息只显示前5条
select * from emp limit 5;
-- 找出公司里面工资最高的5个人
select * from emp order by salary desc limit 5;
-- 实现分页功能
-- 从0开始往后取10条数据
select * from emp limit 0,10;
select * from emp limit 10,10;
模糊查询只有两个符号,实现更加复杂的查询就实现不了,现在支持mysql正则表达式
-- 正则表达式
select name from emp where name like "荀_";
select name from emp where name regexp "^荀.*";
-- 查询曹或是荀开头的名字
select name from emp where name regexp "^(荀|曹).*";