数据库(Database)是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。它的存储空间很大可以存放很多符合一定规则的数据。
数据库在微观层面上来说是运行在计算机上专门处理数据的进程(程序)
数据库在宏观层面上来说是提供给操作者一个简单快捷的操作进程的软件
我们平时在说数据库的时候大部分指的是操作数据库的应用软件
1. 数据库软件的本质其实也是一款CS架构的软件,所以现在市面上其实存在很多数据库软件,大致可以分为
两类:关系型数据库、非关系型数据库。
2. 几乎所有的数据库厂商新出的数据库产品都支持关系型数据库,即使一些非关系数据库产品也几乎都有支
持关系数据库的接口。这主要是传统的关系型数据库可以比较好的解决管理和存储关系型数据的问题。
关系型数据库,存储的格式可以直观地反映实体间的关系。关系型数据库和常见的表格比较相似,关系型数据库中表与表之间是有很多复杂的关联关系的。
常见数据库名称:
MySQL、Oracle、PostgreSQL、MariaDB、sqlite、sql server
MySQL: 关系型数据库的代表,开源免费,使用频率极高
Oracle: 安全性极高,但是使用和维护收费,使用成本高
PostgreSQL: 支持二次开发(自己嫁接、扩展功能)
MariaDB: 与MySQL是同一个作者,开发的初衷是作为MySQL的替代品
sqlite: 小型数据库,携带方便但功能较少,主要用于本地测试使用
sql server: 老牌数据库软件,目前主流不用
NoSql数据库出于简化数据库结构、避免冗余、影响性能的表连接、摒弃复杂分布式的目的被设计
。它现在有四种大的分类:键值对存储(key-value)、列存储、文档数据库存储、图形数据库存储。
常见数据库名称:
Redis、MongoDB、Memcache
Redis: 目前最火的非关系型数据库,数据类型丰富 功能强大
MongoDB: 最像关系型数据库的非关系型数据库,主要用于爬虫和大数据
Memcache: 被redis取代
'数据(Data)':事物的状态
'记录':一组数据构成一条记录,相当于文件中的一行内容,如1,jason,male,18
'表':文件
'库(DataBase,简称DB)':文件夹
'数据库管理系统(DataBase Management System 简称DBMS)':管理数据的套接字软件,CS架构
'数据库服务器':运行有DBMS服务端的计算机,该计算机对内存和硬盘要求都相对较高
MySQL是一种关系型数据库管理系统,所使用的 SQL 语言是用于访问数据库的最常用标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库
MySQL 版本
5.6X:使用最为广泛的稳定版本
5.7X:目前正在逐步过渡使用的版本
8.0X:最新版本 暂时不投入正常生产环境使用
下载使用
官网:https://www.mysql.com/
下载流程如下图
或者直接访问该地址: https://downloads.mysql.com/archives/community/
bin 目录:
mysqld.exe 服务端
mysql.exe 客户端
data 目录: 存放数据
docs 目录: 用于存放一些文档
include 目录: 用于放置一些头文件
lib 目录: 用于放置一系列库文件
share 目录: 用于存放字符集、语言等信息
my-default.ini文件: 默认配置文件
必须要先启动服务端,再启动客户端链接。(补充:SQL语句使用分号作为结束符)
1.运行cmd,切换到MySQL文件bin路径下
2.启动服务端mysqld,启动服务端之后在打开一个cmd,启动客户端mysql。
可以将bin文件路径添加到环境变量,就不需要在去切换路径了。
由于要启动服务端和客户端才能使用,我们可以将服务端设置为开机自启动,这样简化了步骤且消耗的资源并
不多。使用的命令是:' mysqld --install '。结果如下图所示
上面只是成功注册了服务,但是初次启动服务还需要人为干预,使用命令:' net start mysql '
相对应的,关闭服务端和移除服务的命令是:
1.先停止服务端
'net stop mysql' 管理员身份运行cmd
2.移除系统服务
'mysqld --remove'
1. 直接在cmd中直接输入mysql登录是属于游客登录的情况,因此可实现的功能很少
区分游客模式
可以使用命令'show database',游客只显示俩条内容,四条及以上是用户
2. 可以使用用户名和密码的方式登录,也可以使用管理员登录,如果是新下载的MySQL且第一次使用管理员登
录,此时管理员是没有密码的,直接登录即可。
登录的命令
'mysql -h 主机名 -u 用户名 -p'
-h : 指定客户端所要登录的 MySQL 主机名, 登录本机(localhost 或 127.0.0.1)该参数可以省略;
-u : 登录的用户名;
-p : 告诉服务器将会使用一个密码来登录, 如果所要登录的用户名密码为空, 可以忽略此选项。
3. 初次使用管理员登录直接输入: 'mysql -u root -p' ,输入后会得到以下反馈:
'Enter password: ',提示你输入密码,由于没有密码直接回车即可。
4. 修改管理员密码:
(1). 在cmd窗口下直接修改,不需要登录
"mysqladmin -u用户名 -p旧密码 password 新密码"
// 管理员没有旧密码可以不写,例如: mysqladmin -uroot -p password 新密码
(2) 已经登录的情况下修改
"set password=PASSWORD('新密码');"
'\c': 中断当前输入语句
\r:重新连接到服务器
\d:设置语句sql结束符 mysql> \d ]
\e:编辑命令 输出输入的内容 echo
\p:打印当前命令并执行
'\G': 垂直显示结果
'\q': 退出mysql,等于 quit exit
\g:表示结束,等于 ; 的作用
'\h': 显示此帮助,等于 help。 #help还可以查看命令语法 help create database;
\t:不写入outfile
'\T': 将所有内容附加到给定的输出文件中(只支持本次会话) \T /tmp/a.log
#\n:禁用寻呼机,打印到标准输出。
#\P:设置寻呼机[到寻呼机]。通过寻呼机打印查询结果。
'\R': 更改mysql提示符 \R mysql>>>
'\.': 执行一个sql文件,等于 source
'\s': 从服务器获取状态信息,等于 status
'\u': 切换数据库,等于 use #查看当前所在数据库select database();
\C:切换到另一个字符集,一般不使用
\W:在每个语句后显示警告
\w:不在每个语句后显示警告
mysql> help
mysql> help contents help 用法
mysql> help select
mysql> help create
mysql> help create user
mysql> help status
mysql> help show
查看MySQL默认字符编码的命令: '\s' 如下图
如果是5.X系列 显示的编码有多种比如:latin1、gbk
如果是8.X系列 显示的统一是utf8mb4,utf8mb4是utf8优化版本 支持存储表情
统一字符编码(由于5.X默认编码有多种,可能会导致乱码的情况,所以应该统一编码)
操作的是 my-default.ini配置文件
步骤1:拷贝一份该配置文件并修改名称为my.ini
步骤2:清空my.ini文件内的内容
步骤3:添加固定的配置信息即可
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
步骤4:保存并重启服务端即可生效
net stop mysql
net start mysql
存储引擎说白了就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操作此表的类型)。查看存储引擎的方法show engines;
如下图
默认引擎
MyISAM:5.1之前版本MySQL默认的存储引擎
InnoDB:5.1之后版本MySQL默认的存储引擎
InnoDB:支持事务,其设计目标主要面向联机事务处理(OLTP)的应用。
MyISAM:只是读取和插入,不做修改和删除使用
Memory:存储引擎中的数据都存放在内存中,数据库重 启或发生崩溃,表中的数据都将消失。
BLACKHOLE:黑洞存储引擎,可以应用于主备复制中的分发主库
...
自定义选择存储引擎(示例)
create table t1(id int)engine=myisam;
create table t2(id int)engine=innodb;
create table t3(id int)engine=blackhole;
create table t4(id int)engine=memory;
5.0以上版本支持三种sql_mode模式: ANSI、TRADITIONAL和STRICT_TRANS_TABLES。
'ANSI模式': 宽松模式,对插入数据进行校验,如果不符合定义类型或长度,对数据类型调整或截断保存,报warning警告
'TRADITIONAL模式': 严格模式,当向mysql数据库插入数据时,进行数据的严格校验,保证错误数据不能插入,报error
错误。用于事物时,会进行事物的回滚。
'STRICT_TRANS_TABLES模式': 严格模式,进行数据的严格校验,错误数据不能插入,报error错误。
'查看 sql_mode 的方法'
方法1: show variables like '%sql_mode%';
方法2: select @@sql_mode;
"""
sql_mode补充
1.如何查看
show variables like '%mode%'
2.单次修改(仅限于当前登录)
set session sql_mode = '...'
3.临时修改(仅限于当前服务)
set global sql_mode = '...'
4.永久修改
在配置文件中的[mysqld]下面添加
"""
MySQL 注释的方式: # --
补充: MySQL 不区分大小写
MySQL 操作的基本方法就是 “增、删、改、查”。
使用的命令
show databases; 查看所有的库名称
show create database 库名; 指定查看某个库的信息
如下图所示
使用的命令
create database 库名; 创建数据库
如下图所示
使用的方法
alter database 库名 charset='gbk'; 修改字符编码
使用的方法
drop database 库名; 删除数据库
首先要了解一个方法,use 库名;
,由于表存放在库里面,要想对表进行操作就需要先获取指定的库。如果不知道当前所在的库名,可以使用命令select database();
来查看,如下图所示
使用的方法
create table 表名(
字段名称 字段类型(数字) 约束条件 ,
字段名称 字段类型(数字) 约束条件
);
1.字段名和字段类型是必须的
2.数字和约束条件是可选的
3.约束条件可以写多个,空格隔开即可
示例: 字段名 字段类型(数字) 约束条件1 约束条件2 约束条件3
4.最后一行字段结尾不能加逗号
如下图所示(简易表)
使用的方法
show tables; 查看当前库下所有的表名称
show crate table 表名; 指定查看某个表的信息
describe 表名; 指定查看表的字段信息(简写 desc 表名;)
如下图所示
使用的方法
alter table 表名 rename 新表名; 修改表名
alter table 表名 add 字段信息; 添加字段默认是尾部追加字段
alter table 表名 add 字段信息 first; 在头部追加字段
alter table 表名 add 字段信息 after 指定的字段名; 在指定字段后追加字段
alter table 表名 change 旧字段名 新字段名 新字段类型; 修改字段名称和类型
alter table 表名 modify 字段名 新字段类型; 修改字段类型
alter table 表名 drop 删除的字段名; 删除指定字段
使用的方法
drop table 表名; 删除表
如下图所示
如果要操作记录,首先要有的是库和表,因为记录时在表里
使用的方法
insert into 表名 values(值1,值2...); 单条数据
insert into 表名 values(值1,值2...),(值3,值4...); 多条数据
insert into 表名(字段名, 字段名...) values(字段值.字段值...); 顺序可以调换
如下图所示
使用的方法
select * from 表名; 查看表里面所有的数据
select * from 库名.表名; 查看指定库下面的指定表里面的所有数据
select 字段名 from 表名; 查看指定字段
如下图所示
使用的方法
update 表名 set 字段=新值 where 筛选条件;
如下图所示
使用的方法
delete from 表名 where 筛选条件;
如下图所示
类型 | 大小(Bytes ) | 范围(有符号) | 范围(无符号) |
---|---|---|---|
TINYINT | 1 | (-128,127) | (0,255) |
SMALLINT | 2 | (-32 768,32 767) | (0,65 535) |
MEDIUMINT | 3 | (-8 388 608,8 388 607) | (0,16 777 215) |
INT | 4 | (-2 147 483 648,2 147 483 647) | (0,4 294 967 295) |
BIGINT | 8 | (-9,223,372,036,854,775,808,9 223 372 036 854 775 807) | (0,18 446 744 073 709 551 615) |
上述整型的区别在于从上往下能够存储的数字范围越来越大,尽量选最小的,够用就行。
存储时还需要考虑正负数的问题,如果需要存储负数,则需要占据一个比特位。
有时候看似需要使用数字类型存储的数据其实可能使用的是字符串,因为字符串可以解决不同语言对数字不精
确的缺陷。#注意手机号如果使用整型来存储,需要使用bigint才可以
1. 在上图中,给tinyint类型传了数值128,已经超出了其范围,但是在5.6版本不会报错,会自动处理成最
大范围数字,也就是失真了。而在5.7及以上版本,则会直接报错(更加合理)
2. 要想改变这种情况就需要设置'严格模式',如上图示例,使用的语句是
set global sql_mode = 'STRICT_TRANS_TABLES';
'(这只是临时修改,重启会失效。可以将此语句加到my.ini配置文件即可永久生效)'
3. 然后退出客户端,重新登录即可。就会发现传入超出范围的数值就会报错。
4. 所有的整型都默认带有正负号,如何修改不带正负号,需要用到一个约束条件'unsigned',它的作用是
将字段类型变成无符号的形式,所以在上图中的tinyint范围最大变成了255,超过还是要报错。
类型 | 大小 | 用途 |
---|---|---|
FLOAT | 4 Bytes | 单精度浮点数值 |
DOUBLE | 8 Bytes | 双精度浮点数值 |
DECIMAL | - | 包含小数和整数的精确值 |
使用示例
float(255,30) 总共255位 小数位占30位
double(255,30) 总共255位 小数位占30位
decimal(65,30) 总共65位 小数位占30位
上述浮点型从上往下精确度越来越高
验证精度实验如上图所示,虽然三者精确度有差距,但是具体用哪个应该结合实际情况。
比如正常业务,使用float足够
如果是高精尖,可以使用decimal
类型 | 大小 | 用途 |
---|---|---|
CHAR | 0-255 bytes | 定长字符串 |
VARCHAR | 0-65535 bytes | 变长字符串 |
TINYBLOB | 0-255 bytes | 不超过 255 个字符的二进制字符串 |
TINYTEXT | 0-255 bytes | 短文本字符串 |
BLOB | 0-65 535 bytes | 二进制形式的长文本数据 |
TEXT | 0-65 535 bytes | 长文本数据 |
MEDIUMBLOB | 0-16 777 215 bytes | 二进制形式的中等长度文本数据 |
MEDIUMTEXT | 0-16 777 215 bytes | 中等长度文本数据 |
LONGBLOB | 0-4 294 967 295 bytes | 二进制形式的极大文本数据 |
LONGTEXT | 0-4 294 967 295 bytes | 极大文本数据 |
1. char 属于定长字符串类型,使用方法 char(数字),该数字是最大字符长度。
2. 在5.6版本且没有修改严格模式时输入的字符长度超过指定长度的话,char会将超出的部分舍弃,若有
严格模式则直接报错。若长度不够时内部存储使用空格填充。
3. 若字段本身末尾存在空格,检索出来自动移除末尾空格;若字段本身前端存在空格,是不会移除的
适用情况
适合存储很短的或者长度接近最大长度限制的字符串。
对于非常短的信息例如性别,char比varchar在存储空间上也更有效率。
若字段经常需要更新,则优先考虑CHAR类型,由于CHAR类型为定长,因此不容易产生碎片
1. varchar是可变长字符串,使用方法是 varchar(数字),该数字是最大字符长度。
2. 输入字符超出最大范围时和char一样,当长度不够的时候,varchar是有几个存几个
3. varchar 需要额外的1个或2个字节记录字符串的长度,varchar在取数据的时候先会读取额外字节中真实
的长度,再去取数据。
使用情况
字符串列的最大长度比平均长度大很多
字段更新的次数少的情况
使用了像UTF-8这样复杂的字符集,每个字符都使用不同的字节数进行存储
像如果要表示中国人的姓名就不能用固定的长度了应该用varchar
1. varchar类型,在存储数据是,会先判断字符长度,然后合理分配存储空间。而 char,不会判断,立即
分配空间。
2. 取数据的时候,因为分不清空格是字段含有的还是填充产生的所以char类型会去掉末尾多余的空格,而
varchar是不会的。
3. char的存取数度要比varchar要快得多;varchar 空间利用率则比char 更充分。
4. 以前几乎使用的都是char,现在varchar使用频率也越来越高。
1. 在字符串类型中,括号内的数字大部分情况下是用来限制存储的长度,例如varchar(10)
2. 在整型中并不是用来限制长度,而是用来控制展示长度,其储存的长度是默认自带的。
3. 例如在上图演示中,类型为 int(3),但是传入的值不是3位也没有报错,说明其并不是限制长度的作用
4. 在上图示例中使用了一个约束条件'zerofill',它的作用是在展示数据的时候将长度少于括号内指定的长
度的数据用0填充,超出指定长度的正常展示。
枚举('enum'): ENUM 是一个字符串对象,其值通常选自一个允许值列表中且是多选一
集合('set'): 以在事先定义好的各个可取值中选择若⼲个进⾏组合的数据类型
1. 上图演示是枚举的示例,就是指定几个选项,该字段传入的值必须在枚举准备的选项中,且只能是一个。
2. 但是在传入不存在的值时,会被处理成'空字符串'。而在5.7版本以上等有严格模式时会直接报错''' ERROR 1265
(01000): Data truncated for column 'genter' at row 1;'''
3. 在实际使用不推荐使用枚举。
4. 上图演示的是集合类型,事先定义了多个选项,可以同时选择多个选项传入。
5. 由于版本的原因,传入不存在的选项没有报错,而是自动处理了,如果是5.7版本就会报错。
类型 | 大小(bytes) | 范围 | 格式 | 用途 |
---|---|---|---|---|
DATE | 3 | 1000-01-01/9999-12-31 | YYYY-MM-DD | 日期值 |
TIME | 3 | ‘-838:59:59’/‘838:59:59’ | HH:MM:SS | 时间值或持续时间 |
YEAR | 1 | 1901/2155 | YYYY | 年份值 |
DATETIME | 8 | 1000-01-01 00:00:00/9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
TIMESTAMP | 4 | 1970-01-01 00:00:00/2038结束时间是第 2147483647 秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 |
1. 时间数据一般都是通过代码自动获取并添加,上图只是手动模拟
约束条件就是基于字段类型之上的额外限制
'unsigned': 对于数字类型,使其接收的值无符号,即不为负数。
'comment': 创建表的时候用于给字段添加别名,相当于注释。(可以加等号也可以不加)
'zerofill': 展示数字的时候使用 0 来填充空位。
非空'not null': 设置该字段不能为空,必须传入数据
空'null ':与非空相反,允许不传入数据
设置了非空后,不给非空字段传数据就会报错,就算传了一个空字符串也不会报错。
'default': 设置该字段的默认值,不用传入数据不会报错。
还有一种传值方式是: insert into 表名(字段名, 字段名...) values(字段值,字段值...)
有了默认值后,不传数据就会使用默认值。
'unique': 分为单列唯一和多列唯一
'''单列唯一:某个字段下对应的数据不能重复,是唯一的'''
'''多列唯一:多个字段下对应的数据组合到一起的结果不能重复 是唯一的'''
主键时表中一个或多个字段,它的值⽤于唯⼀的标识表中的某⼀条记录。一般将序号字段设置主键
'primary key':
1. 单从约束层面上而言,相当于not null + unique(非空且唯一)
2. 是InnoDB存储引擎规定的一张表必须有且只能有一个主键,用于构建表
3. 如果创建表创建的时候没有设置主键,那么InnoDB会采用一个隐藏的字段作为表的主键(不能使用)
4. 如果没有主键但是有非空且唯一的字段,那么会自动升级成主键(第一个)
5. 主键可以加快数据的查询速度
'auto_increment': 专门配合主键一起使用,用户以后在添加数据的时候就不需要自己记忆主键值可以实现自动增长
1. 上图演示说明了添加了自增的字段不用传入值也可以,每增加一条记录,值加1
2. 上图演示的是自增的特性: 自增的顺序是一直往前的,不会回过头看序号是否删除,也就是就算删除了该字段,继续插入
数据时也会继续往下,已经使用的序号不会在使用。
3. 由上俩图比较可知,即使用delete删除了表内容,其自增值还是不变,而使用了'truncate'删除后自增值被重置。
truncate
truncate: 只能作用于表,其主要用于删除表内容,并且会重置自增值,与delete语句有些不同。
truncate与drop是DDL语句,执行后无法回滚;而delete是DML语句,可回滚
其语法是: truncate table 表名; (其中的table可以不写)
4. 设置自增的起始值:
方法1(创建表示设置):
create table t1(
id int primary key auto_increment,
name varchar(32)) auto_increment=100;
方法2(创完表之后设置):
alter table 表名 auto_increment = 200;
外键是是两个表数据之间建⽴连接的⽅式,可以是⼀列也可以是多列。⼀个表中可以设置⼀个或多个外键,可以表示表与表之
间的关系。
在实际工作中外键也可能不会使用,因为外键会消耗额外的资源,并且会增加表的复杂度
所以在很多的情况下,我们也可以通过SQL语句的形式建立逻辑意义上的表关系
表与表之间的关系主要有这几种:
1.一对多
2.多对多
3.一对一
4.没有关系
1. 什么是一对多关系呢,我们可以举个例子理解。
假设有俩张表,分别为员工表和部门表。我们应该能想到员工一般只属于一个部门,而部门有很多的员工,即部门是一,员工
是多。因此俩表之间的关系就属于一对多的关系。
2. 在一对多的情况下,外键推荐放在多的那一边,也就是员工表中。
3. 创建部门表(dep)语句:
create table dep(
id int primary key auto_increment comment '部门编号',
dep_name varchar(32) comment '部门名称'
);
4. 创建员工表(emp)语句:
create table emp(
id int primary key auto_increment comment '员工编号',
name varchar(32) comment '员工姓名',
dep_id int comment '部门编号',
foreign key(dep_id) references dep(id)
);
'''
设置一对多的外键语句就在员工表中,是 foreign key(dep_id) references dep(id) 语句,此语句将员工表中的部
门编号字段 dep_id 与部门表中的 id 字段绑定。
'''
创建表时,需要将没有外键的表先被创建,即上述的部门表。同样的插入数据也是先插入没有外键的表。创建的演示如下图。
5. 在上图示例中可以发现,删除部门表会出现错误,因为员工表绑定了部门表。但是可以使用级联更新和级联删除的方式
6. 级联更新和级联删除都是放在有外键的表中,也就是员工表。使用的语句如下:
' on update cascade # 级联更新 '
' on delete cascade # 级联删除 '
7. 添加级联更新、级联删除的语句:
create table dep(
id int primary key auto_increment comment '部门编号',
dep_name varchar(32) comment '部门名称'
);
create table emp(
id int primary key auto_increment comment '员工编号',
name varchar(32) comment '员工姓名',
dep_id int comment '部门编号',
foreign key(dep_id) references dep(id)
on update cascade
on delete cascade
);
8. 演示如下图所示
1. 同样的,可以用一个例子来理解。
假设有俩张表,分别为作者表和图书表,一个作者有可能写了好多书,而一本书也可以是由好多作者一起编写的,所以作者和
图书都是多,他们之间的关系就是多对多的关系。
2. 多对多关系的外键不能放在表内,需要创建一个新表用于存放多对多表之间的关系,三张表的关系如图下图
3. 相当于id为1的书本是由id为1、2的作者编写,而id为2的书本由id为2的作者编写。也就是说id为2的作者写了俩本书
4. 创建图书表语句:
create table book(
id int primary key auto_increment,
name varchar(32)
);
5. 创建作者表语句:
create table author(
id int primary key auto_increment,
name varchar(32)
);
6. 创建关系表语句:
create table book_author(
id int primary key auto_increment,
author_id int,
book_id int,
foreign key(author_id) references author(id)
on update cascade on delete cascade, # 级联更新、删除
foreign key(book_id) references book(id)
on update cascade on delete cascade # 级联更新、删除
);
1. 一对一的比如像是信息的展示,展示出来的部分信息,以及点击获取全部信息的例子。
就是说有俩张表,将用户所有的信息分成俩份分别存放。
2. 对于一对一关系来说,互相只能对应一个,也就是一条字段对应一条字段。此时的外键放在哪里都行,但是推荐放在信息
使用比较频繁的那边。
3. 创建用户信息表1:
create table UserInfo1(
id int primary key auto_increment,
name varchar(32),
user_id int unique,
foreign key(user_id) references UserInfo2(id)
on update cascade on delete cascade
);
4. 创建用户信息表2:
create table UserInfo2(
id int primary key auto_increment,
phone bigint
);
5. 演示如下图
select: 用于指定查询的字段
from: 用于指定查询的表
示例: select id,name from 库名.表名;
where: 用于查询条件筛选
示例
1. 查询 id 大于等于3、小于等于6的数据
select * from 表名 where id>=3 and id<=6;
select * from 表名 where id between 3 and 6;
2. 查询 id 小于3或者大于6的数据
select * from 表名 where id not between 3 and 6;
3. 查询 id 是 100 或 200 或者 300 的数据
select * from 表名 where id=100 or id=200 or id=300;
select * from 表名 where id in (100,200,300);
4. 查询 id 不是 100 或 200 或者 300 的数据
select * from 表名 where id!=100 and id!=200 and id!=300;
select * from 表名 where id not in (100,200,300);
5. 查询字段为空的数据
select * from 表名 where 字段名 is null;
select * from 表名 where 字段名 <=> null;
模糊查询
关键字: like:开启模糊查询的关键字
关键符号: % 匹配任意个数的任意字符
_ 匹配单个个数的任意字符
示例
1. 查询指定字段值中包含特定字符的数据,下面示例的特定字符为 xwx
select * from 表名 where 字段名 like '%xwx%';
2. 查询指定字段中包含指定位数的字段值的数据,下面示例的指定位数是3位
select * from 表名 where 字段名 like '___';
select * from 表名 where char_length(字段名)=3;
GROUP BY 语句根据一个或多个列对结果集进行分组,例如按照性别分组、按照年龄分组...
分组的好处在于可以快速统计出某些数据例如最大薪资、平均年龄、最小年龄、总人数。
1. 先创建一张表
create table t1(
id int primary key auto_increment,
name varchar(32),
age int
);
2. 插入表数据
insert into t1(name,age) values('ccc',12),('aaa',13),('bbb',18),('bbb',17),('aaa',10);
执行完后会得到下表
3. 首先将名字分组并查看,语句如下
select name from t1 group by name;
会得到下图示例
4. 很明显的发现,按名字分组会将同名的名字划分在一起,如下图关系示例
5. 由上图不难看出,分组后我们就能获取想要的数据了,这里有几个方法用于数据统计
max 统计最大值
min 统计最小值
sum 统计求和
count 统计计数
avg 统计平均值
由于mysql5.7及以上版本默认自带sql_mode=only_full_group_by严格模式,所以分组后不能直接获取其他字段数据,
需要使用上面的方法间接获取。如下图演示
6. 'group_concat': 此函数方法会将组中的字符串连接成为具有各种选项的单个字符串
如下图示例
having: 与where作用一样的,都是对数据进行筛选,只不过where用在分组之前的筛选,havng用在'分组之后'的筛选
所以将where说成筛选,havng说成过滤
1. 用法: select 字段名 from 表名 where 筛选条件 group by 分组字段 having 过滤条件
2. 给定 t2 表如下所示,要求将 num>=12 的 type 分组并获取分组后 num 的平均值,筛选平均值要大于 13 的数据
+----+------+------+
| id | type | num |
+----+------+------+
| 1 | aaa | 10 |
| 2 | bbb | 11 |
| 3 | aaa | 12 |
| 4 | ccc | 13 |
| 5 | aaa | 14 |
| 6 | bbb | 15 |
+----+------+------+
使用的语句是: select type,avg(num) from t2 where num>=12 group by type having avg(num)>13;
也可以起别名: select type,avg(num) as avg_num from t2 where num>=12 group by type having avg_num>13;
distinct: 它的作用是去重。前提是有一样的数据,如果是主键的话就没有作用。
1. 用法: select distinct 字段名 from 表名; # 将指定字段的数据去重
2. 给定 t2 表如下所示,我们可以使用 'distinct'将表内重复的数据去除。
+----+------+------+
| id | type | num |
+----+------+------+
| 1 | aaa | 10 |
| 2 | bbb | 10 |
| 3 | aaa | 12 |
| 4 | ccc | 10 |
| 5 | aaa | 14 |
| 6 | bbb | 10 |
+----+------+------+
使用的语句: select distinct num from t2;
order by: 它可以对字段数据进行排序,默认为升序。降序使用 desc 关键词
1. 用法1: select 字段名 from 表名 order by 排序字段; # 默认为 asc 升序
用法2: select 字段名 from 表名 order by 排序字段 asc; # 升序,可以不写 asc
用法3: select 字段名 from 表名 order by 排序字段 desc; # 降序
用法4: select 字段名 from 表名 order by 排列字段1 asc, 排列字段2 desc; # 排序后进一步排序
用法5: select 字段名 from 表名 order by 排列字段1 desc, 排列字段2 asc; # 排序后进一步排序
2. 给定 t2 表如下所示,按照 num 字段数据大小对信息进行升序和降序排列
+----+------+------+
| id | type | num |
+----+------+------+
| 1 | aaa | 14 |
| 2 | bbb | 20 |
| 3 | aaa | 36 |
| 4 | ccc | 56 |
| 5 | aaa | 20 |
| 6 | bbb | 39 |
+----+------+------+
使用的语句:
升序: select * from t2 order by num;
升序: select * from t2 order by num asc;
降序: select * from t2 order by num desc;
3. 我们发现,由于表中有相同大小的 num 所以造成了并列现象,可以设置相同数值时进一步比较
使用的语句:
升序后按照从大到小排列相同的值: select * from t2 order by num,id desc;
升序后按照从大到小排列相同的值: select * from t2 order by num asc,id desc;
降序后按照从小到大排列相同的值: select * from t2 order by num desc,id asc;
limit: 分页作用,可以限制展示的数据条数,当数据特别多的时候,使用limit可以节省资源,防止系统崩溃
1. 用法1: select 字段名 from 表名 limit y; //' 展示表中从第1行数据开始的 y 个数据(等于 0,y)'
用法2: select 字段名 from 表名 limit x,y; //' 展示包含表中索引为x的第x+1行数据开始的y个数据'
3. 给定 t2 表如下表所示,'其第一条数据表示为 0'
+----+------+------+
| id | type | num |
+----+------+------+
| 1 | aaa | 14 | ==》 索引为 0 的表中第 1 行数据
| 2 | bbb | 20 | ==》 索引为 1 的表中第 2 行数据
| 3 | aaa | 36 | ==》 索引为 2 的表中第 3 行数据
| 4 | ccc | 56 | ==》 索引为 3 的表中第 4 行数据
| 5 | aaa | 20 | ==》 索引为 4 的表中第 5 行数据
| 6 | bbb | 39 | ==》 索引为 5 的表中第 6 行数据
+----+------+------+
使用的语句:
select * from t2 limit 3; // '展示表中前3行数据相当于 limit 0,3 '
select * from t2 limit 2,5; // '展示包含表中索引为2的第3行数据开始的5个数据'
select * from t2 limit 2,2; // '展示包含表中索引为2的第3行数据开始的2个数据'
4. 查询表中最大值的方法,同样以上表为例子
使用的语句: select * from t2 order by num desc limit 1;
即对数值字段进行从大到小排序,并展示第一条也就是最大值那条
regexp: regexp可以用来表示正则语句,相当于模糊查询的作用
1. 用法: select * from 表名 where 字段名 regexp '正则表达式';
if: 作为判断条件使用
1. 用法: if(判断条件, 条件为true返回的值, 条件为false返回的值)
2. 给定 t2 表如下表所示,将其 num 字段中大于20的修改成 1 ,小于20的修改成 0
+----+------+------+
| id | type | num |
+----+------+------+
| 1 | aaa | 14 |
| 2 | bbb | 20 |
| 3 | aaa | 36 |
| 4 | ccc | 56 |
| 5 | aaa | 20 |
| 6 | bbb | 39 |
+----+------+------+
使用的语句: update t2 set num=if(num>20,1,0) ;
在很多时候,信息是被存放在多个表中,因此需要获得数据就需要去别的表中寻找,于是有了多表查询的方法。
多表查询的方法总共就两种:
1. 子查询: 相当于将查询第一张表的语句使用括号包裹作为另外一张表查询语句的条件
2. 连表操作: 将多个表连在一起成为一个新表,在使用单表查询查找数据。
1. 首先创建俩张表
部门表
create table dep(
id int primary key auto_increment,
name varchar(32)
);
员工表
create table emp(
id int primary key auto_increment,
name varchar(32),
age int,
dep_id int comment '部门编号',
foreign key(dep_id) references dep(id)
on update cascade on delete cascade
);
插入数据
insert into dep(name) values('行政部'),('生产部'),('技术部');
insert into emp(name,age,dep_id) values('aaa',11,1),('bbb',13,1),('ccc',15,2),('ddd',19,2),('fff',21,3);
会得到于以下俩张表
dep表,用于存放部门 emp表用于存放员工
+----+-----------+ +----+------+------+--------+
| id | name | | id | name | age | dep_id |
+----+-----------+ +----+------+------+--------+
| 1 | 行政部 | | 1 | aaa | 11 | 1 |
| 2 | 生产部 | | 2 | bbb | 13 | 1 |
| 3 | 技术部 | | 3 | ccc | 15 | 2 |
+----+-----------+ | 4 | ddd | 19 | 2 |
| 5 | fff | 21 | 3 |
+----+------+------+--------+
2. 在俩张表中设置了外键,员工表中的部门id绑定了部门表中的id字段,现在有要求就是获取指定员工所在的部门
首先按照子查询的思路就是先去获得指定员工的部门编号,在通过这个部门编号去部门表中查找即可
3. 子查询语句:
select name from dep where id=(select dep_id from emp where emp.name='ccc');
用于获得指定员工所在部门
笛卡尔乘积是指在数学中,两个集合X和Y的笛卡尔积(Cartesian product),又称直积,表示为X × Y,第一个对象是X
的成员而第二个对象是Y的所有可能有序对的其中一个成员
假设集合A={a, b},集合B={0, 1, 2}
则两个集合的笛卡尔积为{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。
1. 使用笛卡尔积连接表,同样使用的是下面的表
dep表,用于存放部门 emp表用于存放员工
+----+-----------+ +----+------+------+--------+
| id | name | | id | name | age | dep_id |
+----+-----------+ +----+------+------+--------+
| 1 | 行政部 | | 1 | aaa | 11 | 1 |
| 2 | 生产部 | | 2 | bbb | 13 | 1 |
| 3 | 技术部 | | 3 | ccc | 15 | 2 |
+----+-----------+ | 4 | ddd | 19 | 2 |
| 5 | fff | 21 | 3 |
+----+------+------+--------+
连接的语句: select * from dep,emp;
该语句会将俩张表联合在一起,并且表里面的数据会被重复对应,相当于一个值需要和另外一张表多个值结合,如下
+----+-----------+----+------+------+--------+
| id | name | id | name | age | dep_id |
+----+-----------+----+------+------+--------+
| 1 | 行政部 | 1 | aaa | 11 | 1 |
| 2 | 生产部 | 1 | aaa | 11 | 1 |
| 3 | 技术部 | 1 | aaa | 11 | 1 |
| 1 | 行政部 | 2 | bbb | 13 | 1 |
| 2 | 生产部 | 2 | bbb | 13 | 1 |
| 3 | 技术部 | 2 | bbb | 13 | 1 |
| 1 | 行政部 | 3 | ccc | 15 | 2 |
| 2 | 生产部 | 3 | ccc | 15 | 2 |
| 3 | 技术部 | 3 | ccc | 15 | 2 |
| 1 | 行政部 | 4 | ddd | 19 | 2 |
| 2 | 生产部 | 4 | ddd | 19 | 2 |
| 3 | 技术部 | 4 | ddd | 19 | 2 |
| 1 | 行政部 | 5 | fff | 21 | 3 |
| 2 | 生产部 | 5 | fff | 21 | 3 |
| 3 | 技术部 | 5 | fff | 21 | 3 |
+----+-----------+----+------+------+--------+
2. 我们可以发现连接后的表数据重复匹对,但实际上有些数据是错误的我们不需要(部门与员工不对应),但是可以通过筛
选条件获得需要的数值。
使用的语句:
select emp.name as '员工姓名',dep.name as '部门名称' from dep,emp where dep.id=emp.dep_id;
会得到以下表
+--------------+--------------+
| 员工姓名 | 部门名称 |
+--------------+--------------+
| aaa | 行政部 |
| bbb | 行政部 |
| ccc | 生产部 |
| ddd | 生产部 |
| fff | 技术部 |
+--------------+--------------+
但是使用笛卡尔积的效率会低些,因为它要匹配多次,不适合数据多的情况。
inner join: 内连接,可以获取两个表中字段匹配关系的记录
1. 用法: select 字段名 from 表名1 inner join 表名2 on 有关联的字段关系; # inner单词可省略
(图片引自网络)
2. 同样引入下面俩张表
dep表,用于存放部门 emp表用于存放员工
+----+-----------+ +----+------+------+--------+
| id | name | | id | name | age | dep_id |
+----+-----------+ +----+------+------+--------+
| 1 | 行政部 | | 1 | aaa | 11 | 1 |
| 2 | 生产部 | | 2 | bbb | 13 | 1 |
| 3 | 技术部 | | 3 | ccc | 15 | 2 |
+----+-----------+ | 4 | ddd | 19 | 2 |
| 5 | fff | 21 | 3 |
+----+------+------+--------+
若是不使用 on 直接用 inner join 连接的话,其结果和笛卡尔积一样。类似于 select * from emp join dep;
3. 使用 ~ inner join ~ on ~ 来获取员工对应的部门
使用的语句:
select emp.name as '员工姓名',dep.name as '部门名称' from emp inner join dep on emp.dep_id=dep.id;
也会得到以下结果:
+--------------+--------------+
| 员工姓名 | 部门名称 |
+--------------+--------------+
| aaa | 行政部 |
| bbb | 行政部 |
| ccc | 生产部 |
| ddd | 生产部 |
| fff | 技术部 |
+--------------+--------------+
4. 有了连表操作之后,其实就可以将N多张表拼接到一起
思路:可以将两张表拼接之后的结果起别名当做一张虚拟表然后再去跟另外一张表拼接...
类似于:
select * from emp inner join
(select emp.id as eid, emp.name as ename, dep.name as dname
from emp inner join dep on emp.dep_id=dep.id)
as t on emp.id=t.eid;
'''需要注意的是,要给表起别名'''
结果如下所示(左半边是新连接的表,右半边是俩张表连接后的结果)
+----+------+------+--------+-----+-------+-----------+
| id | name | age | dep_id | eid | ename | dname |
+----+------+------+--------+-----+-------+-----------+
| 1 | aaa | 11 | 1 | 1 | aaa | 行政部 |
| 2 | bbb | 13 | 1 | 2 | bbb | 行政部 |
| 3 | ccc | 15 | 2 | 3 | ccc | 生产部 |
| 4 | ddd | 19 | 2 | 4 | ddd | 生产部 |
| 5 | fff | 21 | 3 | 5 | fff | 技术部 |
+----+------+------+--------+-----+-------+-----------+
left join: 会读取左边数据表的全部数据,即便右边表无对应数据
1. 用法: select 字段名 from 表名1 left join 表名2 on 有关联的字段关系;
2. 引入下面俩张表
dep表 emp表
+----+-----------+ +----+------+--------+
| id | name | | id | name | dep_id |
+----+-----------+ +----+------+--------+
| 1 | 行政部 | | 1 | aaa | 1 |
| 2 | 生产部 | | 2 | bbb | 5 |
| 3 | 技术部 | | 3 | ccc | 2 |
| 4 | 后勤部 | | 4 | ddd | 2 |
+----+-----------+ | 5 | fff | NULL |
+----+------+--------+
使用的语句: select emp.name,dep.name from emp left join dep on emp.dep_id=dep.id;
以 left join 语句左边的表作为基准,展示左边的所有数据,并将右边的表的数据匹配,不能匹配的显示为null
结果如下,用户表的数据均展示,即使对应的部门不存在,也会显示,并填充 null
+------+-----------+
| name | name |
+------+-----------+
| aaa | 行政部 |
| ccc | 生产部 |
| ddd | 生产部 |
| bbb | NULL |
| fff | NULL |
+------+-----------+
right join: 会读取右边数据表的全部数据,即便右边表无对应数据
1. 用法: select 字段名 from 表名1 right join 表名2 on 有关联的字段关系;
2. 引入下面俩张表
dep表 emp表
+----+-----------+ +----+------+--------+
| id | name | | id | name | dep_id |
+----+-----------+ +----+------+--------+
| 1 | 行政部 | | 1 | aaa | 1 |
| 2 | 生产部 | | 2 | bbb | 5 |
| 3 | 技术部 | | 3 | ccc | 2 |
| 4 | 后勤部 | | 4 | ddd | 2 |
+----+-----------+ | 5 | fff | NULL |
+----+------+--------+
使用的语句: select emp.name,dep.name from emp right join dep on emp.dep_id=dep.id;
以 right join 语句右边的表作为基准,展示右边表的所有数据,并将左边的表的数据匹配,不能匹配的显示为null
结果如下,部门表的数据均展示,即使部门里没有员工不存在,也会显示,并填充 null
+------+-----------+
| name | name |
+------+-----------+
| aaa | 行政部 |
| ccc | 生产部 |
| ddd | 生产部 |
| NULL | 技术部 |
| NULL | 后勤部 |
+------+-----------+
通过SQL语句的执行会得到一张虚拟表,虚拟表位于内存,并没有保存到硬盘,如果将其保存,称之为视图。
1. 视图语句
create view 视图名 as sql语句
2. 假设有以下俩张真实表
dep表,用于存放部门 emp表用于存放员工
+----+-----------+ +----+------+------+--------+
| id | dep_name | | id | name | age | dep_id |
+----+-----------+ +----+------+------+--------+
| 1 | 行政部 | | 1 | aaa | 11 | 1 |
| 2 | 生产部 | | 2 | bbb | 13 | 2 |
| 3 | 技术部 | | 3 | ccc | 15 | 2 |
| 4 | 后勤部 | | 4 | ddd | 19 | 3 |
+----+-----------+ | 5 | fff | 21 | 4 |
+----+------+------+--------+
使用sql语句将俩表根据emp表的dep_id和dep的id来结合,会获得以下表
+------+-----------+
| name | dep_name |
+------+-----------+
| aaa | 行政部 |
| bbb | 生产部 |
| ccc | 生产部 |
| ddd | 技术部 |
| fff | 后勤部 |
+------+-----------+
但是此表并没有保存为视图,若想保存需要使用以下语句格式
create view vname1 as
select name,dep_name from dep inner join emp on emp.dep_id=dep.id;
此时就会保存成名称为vname1的视图,表结构和sql查询的一样
+------+-----------+
| name | dep_name |
+------+-----------+
| aaa | 行政部 |
| bbb | 生产部 |
| ccc | 生产部 |
| ddd | 技术部 |
| fff | 后勤部 |
+------+-----------+
3. 虽然视图保存后可以方便使用,但是视图和表是放在一起的(navivat会将其分开),会造成一定的混乱,并且由于视图
本身没有数据,其数据都是源于基表,所以一般不允许增删改,因为会导致基表的数据也会出现改变。
基于单表的视图:可以增删改数据,并会影响基表。
基于多表的视图:可以增加单表数据,不能同时改变多个基表,
可以更新数据并影响基表,不能删除数据。
触发器: 是一种通过事件触发而被执行的,即不是主动调用而执行的特殊类型的存储过程。在触发器中有俩个关键字,用于
表示数据的操作状态,数据操作之前的状态用 'old' 接收,操作之后的用 'new' 接收。
1. 种类
'INSERT' 型触发器 没有 old,只有 new,用于表示将要(插入前)或者已经增加(插入后)的数据
'UPDATE' 型触发器 俩者均有,old 表示更新之前的数据,new 表示更新之后的数据
'DELETE' 型触发器 没有 new,只有 old,用于表示将要(删除前)或者已经被删除(删除后)的数据
2. 触发器使用的语句
show triggers; 查看所有触发器
show create trigger 触发器名; 查看指定触发器信息
drop trigger 触发器名; 删除触发器
3. 触发器时间、事件
时间:before/after 事件发生前/事件发生后
事件:insert/update/delete 插入/更新/删除
即:
before insert(插入前)、 before update(更新前)、 before delete(删除前)
after insert(插入后) 、 after update(更新后) 、 after delete(删除后)
4. 触发器模板
delimiter 自定义结束符号
create trigger 触发器名字 触发时间 触发事件 on 表 for each row
begin
# SQL语句,触发器的内容主体
end
自定义的结束符号
delimiter ;
5. 案例(cmd表插入数据的success如果值为no 则去errlog表中插入一条记录)
# 创建表
CREATE TABLE cmd (
id INT PRIMARY KEY auto_increment,
USER CHAR (32),
priv CHAR (10),
cmd CHAR (64),
sub_time datetime, #提交时间
success enum ('yes', 'no') #0代表执行失败
);
CREATE TABLE errlog (
id INT PRIMARY KEY auto_increment,
err_cmd CHAR (64),
err_time datetime
);
# 创建触发器
delimiter $$ # 将mysql默认的结束符由;换成$$
create trigger tri_after_insert_cmd after insert on cmd for each row
begin
if NEW.success = 'no' then # 新记录都会被MySQL封装成NEW对象
insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
end if;
end $$
delimiter ;
# 向表中插入数据
INSERT INTO cmd (
USER,
priv,
cmd,
sub_time,
success
)VALUES
('kevin','0755','ls -l /etc',NOW(),'yes'),
('kevin','0755','cat /etc/passwd',NOW(),'no'),
('kevin','0755','useradd xxx',NOW(),'no'),
('kevin','0755','ps aux',NOW(),'yes');
事务:事务包含了诸多SQL语句,就像是一连串的动作组成的动作单元并且这些动作必须执行成功,一般用于处理操作量大复
杂度高的数据。
1. 事务的四大特性
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。
事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规
则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉
执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交、读提交、可重复读和串行化
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
2. 事务处理方法
'start transaction;' 开启事务
'commit' 提交事务并使已对数据库进行的所有修改成为永久性的
'rollback/rollback work' 事务回滚回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
savepoint identifier 允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
release savepoint identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
rollback to identifier 把事务回滚到标记点;
set transaction 用来设置事务的隔离级别。
3. 事务在执行中发生错误或者手动回滚都会恢复执行之前的状态,这里有一个小案例
# 创建表
create table user(
id int primary key auto_increment,
name char(32),
balance int
);
# 插入数据
insert into user(name,balance) values('xxx',1000),('yyy',1000),('zzz',1000);
# 开启事务
start transaction;
# 第一次修改表数据
update user set balance=900 where name='xxx';
update user set balance=700 where name='yyy';
update user set balance=500 where name='zzz';
#查看表
select * from user;
# 回滚操作
rollback;
#查看表
select * from user;
# 第二次修改
update user set balance=900 where name='xxx';
# 提交事务
commit;
# 回滚操作
rollback;
#查看表
select * from user;
在案例中总共有三次查看表的数据,分别如下
第一次查看:第一次修改之后的结构如下
+----+-------+---------+
| id | name | balance |
+----+-------+---------+
| 1 | xxx | 900 |
| 2 | yyy | 700 |
| 3 | zzz | 500 |
+----+-------+---------+
第二次查看:第一次修改后的回滚结果如下
+----+-------+---------+
| id | name | balance |
+----+-------+---------+
| 1 | xxx | 1000 |
| 2 | yyy | 1000 |
| 3 | zzz | 1000 |
+----+-------+---------+
第三次查看:第二次修改表之后使用了 commit 提交事务,此时回滚的结果如下
+----+-------+---------+
| id | name | balance |
+----+-------+---------+
| 1 | xxx | 1000 |
| 2 | yyy | 1000 |
| 3 | zzz | 500 |
+----+-------+---------+
很明显在没有使用 commit提交事务时使用回滚会回到修改前的状态,但是使用了commit之后在回滚也不能回到原来状态
存储过程:存储过程就是一组SQL语句集,可以实现一些比较复杂的逻辑功能,类似于python中的自定义函数
1. 存储过程的好处:
由于数据库执行动作时,是先编译后执行的。然而存储过程是一个编译过的代码块,所以执行效率要比T-SQL语句高。
一个存储过程在程序在网络中交互时可以替代大堆的T-SQL语句,所以也能降低网络的通信量,提高通信速率。
通过存储过程能够使没有权限的用户在控制之下间接地存取数据库,从而确保数据的安全。
2. 存储过程创建模板
# 创建存储过程
delimiter 自定义结束符号
create procedure 存储名()
begin
sql语句
end 自定义结束符号
delimiter ;
# 调用存储过程
call 存储名()
3. 存储过程其他方法
存储过程传参
in out inout
查看存储过程具体信息
show create procedure pro1;
查看所有存储过程
show procedure status;
删除存储过程
drop procedure p1;
4. 案例
# 创建库
create database d8;
# 创建表
create table t8(
id int primary key auto_increment,
name varchar(32),
balance int);
# 插入数据
insert into t8(name,balance) values('xxx',900),('yyy',700),('zzz',500);
# 事先定义变量
set @res=10;
# 查看该变量
select @res;
# 创建存储过程
delimiter $$
create procedure p1(
in m int, # in表示这个参数必须只能是传入不能被返回出去
in n int,
out res int # out表示这个参数可以被返回出去,还有一个inout表示即可以传入也可以被返回出去
)
begin
select name from t8 where id > m and id < n;
set res=0; # 用来标志存储过程是否执行
end $$
delimiter ;
# 调用存储过程
call p1(2,600,@res);
# 查看变量
select @res;
调用案例中的存储过程如同调用函数,需要注意的是 out 定义的变量需要事先创建。
函数:mysql内置的函数,只能在sql语句中使用,可以通过help来看其用法
1. 移除指定字符 'Trim、LTrim、RTrim'
'TRIM'
SELECT TRIM(' bar ');
-> 'bar'
mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');
-> 'barxxx'
mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');
-> 'bar'
mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz');
-> 'barx'
'LTRIM'
mysql> SELECT LTRIM(' barbar');
-> 'barbar'
'RTRIM'
SELECT RTRIM('barbar ');
-> 'barbar'
2. 大小写转换 'Lower、Upper'
'LOWER'
SELECT LOWER('QUADRATICALLY');
-> 'quadratically'
'UPPER'
SELECT UPPER('Hej');
-> 'HEJ'
3. 获取左右起始指定个数字符 'Left、Right'
'LEFT'
SELECT LEFT('foobarbar', 5);
-> 'fooba'
4. 返回读音相似值(对英文效果) 'Soundex'
'SOUNDEX'
SELECT SOUNDEX('Hello');
-> 'H400'
SELECT SOUNDEX('Quadratically');
-> 'Q36324'
5. 返回字符串 s 的字符数 'CHAR_LENGTH(s)'
6. 字符串 s1,s2 等多个字符串合并为一个字符串 'CONCAT(s1,s2...sn)'
7. 日期格式 'date_format' ,其作用是以不同的格式显示日期/时间数据
'在MySQL中表示时间格式尽量采用2000-11-11形式'
案例(将内容按日期分组)
# 创建表
CREATE TABLE blog (
id INT PRIMARY KEY auto_increment,
NAME CHAR (32),
sub_time datetime
);
# 插入数据
INSERT INTO blog (NAME, sub_time)
VALUES
('第1篇','2015-03-01 11:31:21'),
('第2篇','2015-03-11 16:31:21'),
('第3篇','2016-07-01 10:21:31'),
('第4篇','2016-07-22 09:23:21'),
('第5篇','2016-07-23 10:11:11'),
('第6篇','2016-07-25 11:21:31'),
('第7篇','2017-03-01 15:33:21'),
('第8篇','2017-03-01 17:32:21'),
('第9篇','2017-03-01 18:31:21');
# 按照日期格式分组
select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');
# 查看的结果
+-------------------------------+-----------+
| date_format(sub_time,'%Y-%m') | count(id) |
+-------------------------------+-----------+
| 2015-03 | 2 |
| 2016-07 | 4 |
| 2017-03 | 3 |
+-------------------------------+-----------+
7. 'date_format'的日期表示格式
%a 缩写星期名
%b 缩写月名
%c 月,数值
%D 带有英文前缀的月中的天
%d 月的天,数值(00-31)
%e 月的天,数值(0-31)
%f 微秒
%H 小时 (00-23)
%h 小时 (01-12)
%I 小时 (01-12)
%i 分钟,数值(00-59)
%j 年的天 (001-366)
%k 小时 (0-23)
%l 小时 (1-12)
%M 月名
%m 月,数值(00-12)
%p AM 或 PM
%r 时间,12-小时(hh:mm:ss AM 或 PM)
%S 秒(00-59)
%s 秒(00-59)
%T 时间, 24-小时 (hh:mm:ss)
%U 周 (00-53) 星期日是一周的第一天
%u 周 (00-53) 星期一是一周的第一天
%V 周 (01-53) 星期日是一周的第一天,与 %X 使用
%v 周 (01-53) 星期一是一周的第一天,与 %x 使用
%W 星期名
%w 周的天 (0=星期日, 6=星期六)
%X 年,其中的星期日是周的第一天,4 位,与 %V 使用
%x 年,其中的星期一是周的第一天,4 位,与 %v 使用
%Y 年,4 位
%y 年,2 位
1. MySQL if 判断格式
if 条件 then
子代码
elseif 条件 then
子代码
else
子代码
end if;
2. MySQL while 循环格式
WHILE 条件 DO
主体代码
END WHILE ;
索引是存储引擎用于快速查找记录的一种数据结构。需要额外开辟空间和数据维护工作。
索引可以提升查询速度,会影响where查询,以及order by排序
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构
1. 分类
从索引存储结构划分:B Tree索引、Hash索引、FULLTEXT全文索引、R Tree索引
从应用层次划分:普通索引、唯一索引、主键索引、复合索引
从索引键值类型划分:主键索引、辅助索引(二级索引)
从数据存储和索引键值逻辑关系划分:聚集索引(聚簇索引)、非聚集索引(非聚簇索引)
2. 聚集索引(primary key)
主键是一种特殊的唯一索引,不允许有空值,一般使用在 id 字段,查询速度快
创建主键的方式:
CREATE TABLE tablename ( [...], PRIMARY KEY (字段名) );
ALTER TABLE tablename ADD PRIMARY KEY (字段名);
3. 辅助索引(unique key)
使用 unique key 索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一
创建唯一键的方式:
CREATE UNIQUE INDEX <索引的名字> ON tablename (字段名);
ALTER TABLE tablename ADD UNIQUE INDEX [索引的名字] (字段名);
CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (字段名) ;
4. 辅助索引(index key)
普通索引,基于普通字段建立的索引,没有任何限制。
创建普通索引的方法
CREATE INDEX <索引的名字> ON tablename (字段名);
ALTER TABLE tablename ADD INDEX [索引的名字] (字段名);
CREATE TABLE tablename ( [...], INDEX [索引的名字] (字段名) );
5. 全文索引
查询操作在数据量比较少时,可以使用like模糊查询,但是对于大量的文本数据检索,效率很低。如果使用全文索引,查询
速度会比like快很多倍。'全文索引必须在字符串、文本字段上建立。'
创建全文索引的方法
CREATE FULLTEXT INDEX <索引的名字> ON tablename (字段名);
ALTER TABLE tablename ADD FULLTEXT [索引的名字] (字段名);
CREATE TABLE tablename ( [...], FULLTEXT KEY [索引的名字] (字段名) ;
使用的方式
select * from user where match(name) against('aaa');
查询数据的时候不可能都是用id作为筛选条件,也可能会用name,password等字段信息,
那么这个时候就无法利用到聚集索引的加速查询效果。就需要给其他字段建立索引,这些索引就叫辅助索引