目录 | 概述 |
---|---|
数据库概述 | 数据库、数据库管理系统与常见数据库 |
安装MySQL | 在 Linux 和 Docker 上安装 MySQL |
登录与重设密码 | 登录或用 SQLyog 连接 MySQL 数据库 |
数据库备份与恢复 | 备份与恢复 MySQL 数据库 |
SQL概述 | SQL 类型与常用数据类型 |
数据操作语言DML | 操作数据库对象与表 |
数据操作语言DML | 插入、更新与删除表记录 |
数据查询语言DQL | 简单查询、条件查询、排序查询、聚合查询与排序查询 |
SQL执行顺序 | FROM , ON , JOIN , WHERE , GROUP BY , HAVING , SELECT , DISTINCT , ORDER BY , LIMIT |
SQL约束 | 主键约束、非空约束、唯一约束、默认约束与外键约束 |
多表操作 | 表与表之间的关系,一对多、多对多 |
多表查询 | 连接、子查询 |
数据库概述
数据库(DataBase)就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用户可以对数据库中的数据进行增加,修改,删除及查询操作。
数据库管理系统(DataBase Management System,DBMS):指一种操作和管理数据库的大型软件,用于建立、使用和维护数据库,对数据库进行统一管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问数据库中表内的数据。
数据库管理系统可以创建并管理多个数据库。数据库以表为组织单位存储数据,每张表有多个字段,每个字段都有对应的数据类型。根据表字段所规定的数据类型,我们可以向其中填入一条条的数据,而表中的每条数据类似类的实例对象。表中的一行一行的信息我们称之为记录。
常见的数据库管理系统:
MYSQL:开源免费的数据库,已经被 Oracle 收购。
Oracle:收费的大型数据库,Oracle 公司的产品。
DB2:IBM 公司的数据库产品。
SQLServer:MicroSoft 公司的数据库,常在 C#、.net 以及 Azure 等微软系产品中使用。
SQLite:嵌入式的小型数据库,应用在手机端,比如 Android APP。
安装MySQL
Yum
对于本地源中包含 MySQL 的包或者服务器有外网连接的情况下,使用 yum 安装可以自动解决依赖问题。
- 使用 yum 在线安装 MySQL:
1 | $ yum -y install mysql mysql-server mysql-devel |
- 运行 mysql 安装自带脚本进行配置:
1 | $ /usr/bin/mysql_secure_installation |
- 进入 MySQL 客户端,进行授权:
1 | $ mysql -uroot -proot |
- 设置 MySQL 开机启动:
1 | $ chkconfig --add mysqld |
曾经在 cdh 集群安装过 MySQL 5.7(可以使用,但后来可能是由于版本问题无法与CM连接):
1 | # 添加MySQL5.7仓库 |
RPM
对于无外网连接的情况,可以提前手动下载 RPM 包,再上传到服务器安装。下载 MySQL 5.7(需注册甲骨文账号):https://dev.mysql.com/downloads/mysql/5.7.html
下载时可以选择整合包 mysql-5.7.36-1.el7.x86_64.rpm-bundle.tar
,解包后可以得到 10 个 .rpm
文件。也可以根据需要选择下载。使用 Oracle 的 RPM 软件包在 Linux 上安装 MySQL https://dev.mysql.com/doc/refman/5.7/en/linux-installation-rpm.html
各包的说明如下:
包名字 | 概括 |
---|---|
mysql-community-server |
数据库服务器及相关工具 |
mysql-community-client |
MySQL 客户端应用程序和工具 |
mysql-community-common |
服务器和客户端库的通用文件 |
mysql-community-devel |
MySQL数据库客户端应用程序的开发头文件和库 |
mysql-community-libs |
MySQL 数据库客户端应用程序的共享库 |
mysql-community-libs-compat |
先前 MySQL 安装的共享兼容性库 |
mysql-community-embedded |
MySQL 嵌入式库 |
mysql-community-embedded-devel |
将 MySQL 作为嵌入式库开发头文件和库 |
mysql-community-test |
MySQL 服务器的测试套件 |
1 | mysql-community-client-5.7.36-1.el7.x86_64.rpm:MySQL客户端应用程序和工具 |
安装时,需要根据依赖顺序 common
-> libs
-> client
-> server
-> libs-compat
-> devel
进行安装:
在大多数情况下,您需要安装 mysql-community-server
、 mysql-community-client
、 mysql-community-libs
、 mysql-community-common
和 mysql-community-libs-compat
软件包才能获得功能性的标准 MySQL 安装。
1 | # common/libs/client/server 这四个包为必需 |
对于 CDH6.1.1:
1 | wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm |
启动:
1 | $ systemctl start mysqld |
查看安装时生成的 root
密码:
1 | $ grep 'temporary password' /var/log/mysqld.log |
Docker
从仓库拉取 MySQL 镜像并创建容器:
1 | $ docker pull mysql:5.7 |
登录与重设密码
登录
MySQL 数据库需要账户名和密码进行登录,它提供了一个默认的 root
账号,使用安装时设置的密码即可登录:
1 | # 通过命令行登录 |
修改密码
登录 MySQL 后,可以使用下面两种方式修改密码:
1 | -- ALTER USER 'username'@'hostname' IDENTIFIED BY 'new_password'; |
重设密码
- 停止运行
mysqld
服务:
1 | $ service mysqld stop |
- 跳过访问授权表:
1 | $ mysqld_safe --user=mysql --skip-grant-tables --skip-networking & |
- 登录 MySQL:
1 | $ mysql -u root mysql |
- 修改密码
1 | -- mysql5.7 版本以下,newpassword 填入新的密码 |
- 重启
mysqld
服务并使用新密码登录:
1 | # 重启mysqld服务 |
添加用户
1 | -- 本地登录 |
删除用户
1 | Delete FROM mysql.user Where User="“tyUser”" and Host="”localhost”"; |
查看用户
1 | SELECT DISTINCT CONCAT('User: ''',user,'''@''',host,''';') AS query FROM mysql.user; |
数据库备份与恢复
备份
将 MySQL 数据库转换成对应的 .sql
文件:
1 | # 通过命令行备份,通过命令行备份的.sql文件不包含创建数据库的sql语句 |
恢复
使用备份产生的 .sql
文件恢复数据库:
1 | # 通过命令行恢复 |
SQL概述
结构化查询语言简称 SQL(Structured Query Language),是关系型数据库管理系统都需要遵循的规范。不同的数据库生产厂商都支持 SQL 语句,但都有特有内容。
SQL类型
- 数据定义语言 DDL(Data Definition Language):用来定义数据库对象,比如数据库,表,列等。关键字包括
CREATE
,ALTER
,DROP
等 - 数据操作语言 DML(Data Manipulation Language):用来对数据库中表的记录进行更新。关键字包括
INSERT
,DELETE
,UPDATE
等 - 数据控制语言 DCL(Data Control Language):用来定义数据库的访问权限和安全级别,及创建用户。关键字包括
GRANT
,REVOKE
等 - 数据查询语言 DQL(Data Query Language):用来查询数据库中表的记录。关键字包括
SELECT
,FROM
,WHERE
等
常用数据类型
类型名称 | 说明 |
---|---|
int(integer) | 整数类型 |
double | 小数类型 |
decimal(m,d) | 指定整数位与小数位长度的小数类型 |
date | 日期类型,格式为 yyyy-MM-dd ,包含年月日,不包含时分秒 |
datetime | 日期类型,格式为 YYYY-MM-DD HH:MM:SS ,包含年月日时分秒 |
timestamp | 日期类型,时间戳 |
varchar(M) | 文本类型, M 为 0~65535 之间的整数 |
数据定义语言DDL
操作数据库对象
1 | -- 创建数据库,使用默认编码utf8 |
操作表
1 | -- 创建表 |
数据操作语言DML
插入表记录
1 | -- 向表中插入某些字段 |
注意:
- 值与字段必须对应,个数相同,类型相同
- 值的数据大小必须在字段的长度范围内
- 除了数值类型外,其它的字段类型的值必须使用引号引起(建议单引号)
- 如果要插入空值,可以不写字段,或者插入
null
更新表记录
1 | -- 更新所有记录的指定字段 |
注意:
- 列名的类型与修改的值要一致
- 修改值得时候不能超过最大长度
- 除了数值类型外,其它的字段类型的值必须使用引号引起
删除表记录
1 | delete from 表名 [where 条件]; |
数据查询语言DQL
基本语法:
1 | select [distinct] * | 列名,列名 from 表 where 条件; |
简单查询
1 | -- 查询所有的商品 |
条件查询
运算符与关键字 | 作用 |
---|---|
< <= > >= != <> |
小于、小于等于、大于、大于等于、不等于、不等于 |
BETWEEN …AND… | 显示在某一区间的值(含头含尾) |
IN(set) | 显示在in列表中的值,例:in(100,200) |
LIKE | 模糊查询,Like语句中,% 代表零个或多个任意字符,_ 代表一个字符,例如:first_name like '_a%'; |
IS NULL | 判断是否为空 |
AND | 多个条件同时成立 |
OR | 多个条件任一成立 |
NOT | 不成立 |
示例:
1 | -- 查询商品名称为“花花公子”的商品所有信息: |
排序查询
通过 order by
语句,可以将查询出的结果进行排序:
1 | -- ASC 升序 (默认),DESC 降序 |
示例:
1 | -- 根据价格降序排序 |
聚合查询
之前的查询都是横向查询,它们都是根据条件一行一行地判断,而使用聚合函数的查询是纵向查询,它对一列的值进行计算,然后返回一个单一的值,另外聚合函数会忽略空值:
- count:统计指定列不为NULL的记录行数;
- sum:计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0;
- max:计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算;
- min:计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算;
- avg:计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0;
示例:
1 | -- 查询商品的总条数 |
分组查询
使用 group by
关键字对查询信息进行分组:
1 | SELECT 字段1,字段2… FROM 表名 GROUP BY 分组字段 HAVING 分组条件; |
分组操作中的 having
子句,是用于在分组后对数据进行过滤的,作用类似于 where
条件,但二者又有区别:
having
是在分组后对数据进行过滤,where
是在分组前对数据进行过滤having
后面可以使用分组函数(统计函数),where
后面不可以使用聚合函数
示例:
1 | -- 统计各个分类商品的个数 |
SQL执行顺序
1 | (8) SELECT<fields> (9)DISTINCT<fields> |
所有的查询语句都是从 from
开始执行的,在执行过程中,每个步骤都会为下一个步骤生成一个虚拟表,这个虚拟表将作为下一个执行步骤的输入。在执行多表连接时,MySQL 会根据连接算法(比如 Nested-Loop join)执行嵌套循环连接操作,而不是每次只连接两种表,在对最终结果集没影响的前提下,优先选择结果集最小的那张表作为驱动表。为了便于理解,通常使用“参与 join 的两张表生成笛卡尔积再过滤”来帮助理解 join 语义,实际上并不会这样执行(效率太差)。
- 首先对
from t1 join t2
中的前两个表执行一个笛卡尔乘积,此时生成虚拟表 vt1。 - 接下来便是应用
on
筛选器,on
中的逻辑表达式将应用到 vt1 中的各个行,筛选出满足on
逻辑表达式的行,生成虚拟表 vt2。 - 如果是
outer join
那么这一步就将添加外部行,left outer jion
就把左表在第二步中过滤掉的行添加进来,如果是right outer join
那么就将右表在第二步中过滤掉的行添加进来,这样生成虚拟表 vt3 。如果from
子句中的表数目多余两个表,那么就将 vt3 和第三个表连接从而计算笛卡尔乘积,生成虚拟表,该过程就是一个重复 1~3 的步骤,最终得到一个新的虚拟表 vt3。 (这样说明只是为了便于理解join
语义,实际执行时会进行优化) - 应用
where
筛选器,对上一步生产的虚拟表引用where
筛选器,生成虚拟表 vt4。对于包含outer join
子句的查询,就有一个让人感到困惑的问题,到底在on
筛选器还是用where
筛选器指定逻辑表达式呢?on
和where
的最大区别在于,如果在on
应用逻辑表达式,那么在第三步outer join
中还可以把移除的行再次添加回来,而where
的移除是最终的。 group by
子句将 vt4 按指定字段聚合得到多个分组,生成虚拟表 vt5。应用group by
后,后面的所有步骤都只能使用 vt5 的列和聚合函数,每个分组保留一行数据。- 应用
rollup
或cube
选项,对group by
分组结果进行汇总,为 vt5 生成超组,得到 vt6。rollup
与order by
为互斥关键字。 - 应用
having
筛选器,生成 vt7。having
是唯一一个应用在已分组数据的筛选器。 - 应用
select
子句,用select
中出现的列对 vt7 进行筛选,生成 vt8。 - 应用
distinct
子句,移除 vt8 中相同的行,生成 vt9。如果应用过group by
子句那么distinct
就是多余的。 - 应用
order by
子句。按照order_by_condition
排序 vt9,此时返回一个游标,而不是虚拟表。SQL 是基于集合理论的,集合中的行之间没有预先定义的顺序,它只是成员的一种逻辑组合,成员之间的顺序无关紧要。但是对于带有order by
子句的查询,它返回的是一个对象,其中的行按照顺序组织在一起,这个对象就叫游标。order by
是唯一一个可以使用select
列表中别名的步骤。 - 应用
limit
选项,返回结果给用户。
SQL约束
约束用于限制加入表的数据的类型。可以在创建表时通过 CREATE TABLE
语句规定约束,或者在表创建之后通过 ALTER TABLE
语句添加约束。SQL 约束分为以下几类:
- PRIMARY KEY:主键约束
- NOT NULL:非空约束
- UNIQUE:唯一约束
- DEFAULT:默认约束
- FOREIGN KEY:外键约束
主键约束
PRIMARY KEY
约束唯一标识数据库表中的每条记录,相当于唯一约束 + 非空约束:
- 主键必须包含唯一的值
- 主键列不能包含
NULL
值 - 每个表都应该有一个主键,并且每个表只能有一个主键
添加主键约束
1 | -- 方式一:创建表时,在字段描述处声明指定字段为主键 |
删除主键约束
1 | -- 通过修改表结构,删除主键约束。如果有自增要先删除自增 |
添加自动增长列
1 | -- 创建表时,对主键字段设置为自动增长列,默认的起始值为1 |
对于 `auto_increment`,使用 `delete from 表名` 和使用 `truncate table 表名` 这两种删除表中所有记录有什么区别?
- delete:删除表中记录,可以回滚,不会清空
auto_increment
记数 - truncate:清空表中记录,无法回滚,会将
auto_increment
记数重置为零
非空约束
NOT NULL
约束强制列不接受 NULL
值。如果不向字段添加值,就无法插入新记录或者更新记录。
添加非空约束
1 | -- 方式一:创建表时,设置非空字段 |
删除非空约束
1 | -- 通过修改表结构,删除非空约束 |
唯一约束
UNIQUE
约束唯一标识数据库表中的每条记录,为列或列集合提供了唯一性的保证。PRIMARY KEY
拥有自动定义的 UNIQUE
约束,每个表可以有多个 UNIQUE
约束,但是每个表只能有一个 PRIMARY KEY
约束。
添加唯一约束
1 | -- 方式一:创建表时,在字段描述处声明唯一 |
删除唯一约束
1 | -- 通过修改表结构,删除唯一约束 |
默认约束
default
约束用于指定字段默认值。当向表中插入记录时,如果没有明确的为字段赋值,则自动赋予默认值。
添加默认约束
1 | -- 方式一:创建表时候,在字段描述处添加默认约束 |
删除默认约束
1 | -- 通过修改表结构,删除默认约束 |
外键约束
现有分类表 category
和商品表 product
,为了表明商品属于哪个分类,我们将在商品表 product
上添加一列 category_id
,用于存放分类表中的 cid
信息,此列称为外键。此时分类表 category
称为主表,cid
为主键。商品表 product
称为从表,category_id
称为外键。通过主表的主键和从表的外键来描述主外键关系,呈现一对多关系。
FOREIGN KEY
约束用于保证数据完整性。外键的特点:
- 从表外键的值是对主表主键的引用
- 从表外键类型,必须与主表主键类型一致
添加外键约束
1 | -- 创建主表 |
删除外键约束
1 | -- 通过修改表结构,删除外键约束 |
多表操作
实际开发中,一个项目通常需要很多张表,而且这些表的数据之间存在一定的关系。
表与表之间的关系
一对多关系
常见实例:客户和订单,分类和商品,部门和员工。
一对多建表原则:在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键。
多对多关系
常见实例:学生和课程、用户和角色。
多对多关系建表原则:需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键。
一对一关系
在实际的开发中应用不多,因为一对一可以创建成一张表。
一对多
分类表 category
为一方,也就是主表,提供主键 cid
。
商品表 products
为多方,也就是从表,提供外键 category_id
。
建立一对多关系:
1 | -- 创建分类表 |
操作:
1 | -- 向分类表中添加数据 |
多对多
商品和订单为多对多关系,拆分成两个一对多:
- 商品表
products
,为其中一个一对多的主表,需要提供主键pid
- 订单表
orders
,为另一个一对多的主表,需要提供主键oid
- 中间表
orderproducts
,为专门建立的第三张表,需要提供两个外键oid
和pid
建立多对多关系:
1 | -- 商品表已存在(略去) |
操作:
1 | -- 向商品表中添加数据 |
多表查询
准备数据
创建表并插入数据:
1 | -- 分类表 |
多表查询
- 交叉连接查询(得到的是两个表的笛卡尔积,存在脏数据)
- 语法:
select * from A,B;
- 内连接查询(避免笛卡尔积)
- 隐式内连接:
select * from A,B where 条件;
- 显式内连接:
select * from A [inner] join B on 条件;
,inner
可省略
1 | -- 查询哪些分类的商品已经上架 |
- 外连接查询(以一侧为基底,返回表中所有行,如果另一侧没有匹配的行,其提供的字段返回
NULL
)
- 左外连接:
select * from A left [outer] join B on 条件;
,outer
可以省略 - 右外连接:
select * from A right [outer] join B on 条件;
,outer
可以省略
1 | -- 查询所有分类商品的个数 |
内外连接有什么区别?
子查询
一条 select
语句的结果作为另一条 select
语句的一部分(查询条件、查询结果、表等)。
语法:
1 | select ....查询字段 ... from ... 表.. where ... 查询条件 |
示例:
1 | -- 查询“化妆品”分类上架商品详情 |
函数
//TODO
参考资料
w3schools - SQL Tutorial
关于SQL和MySQL的语句执行顺序
MySQL 查询语句执行顺序疑问:多表关联时会先生成笛卡尔乘积?
嵌套循环连接算法