MySQL学习笔记
基础篇
数据库概述
什么是数据库?
- 数据库就是【存储数据的仓库】,其本质是一个【文件系统】,数据按照特定的格式将数据存储起来,用户可以通过SQL对数据库中的数据进行【增删改查】操作。
什么是关系型数据库?
- 数据库中的【记录是有行有列的数据库】就是关系型数据库,与之相反的就是 NoSQL 数据库了。
数据库和表?
什么是数据库管理系统?
- 指的是一种【操作和管理数据库】的大型软件,用于建立、使用和维护数据库,对数据库进行统一的管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问数据库中表内的数据。
有哪些常见的数据库管理系统?
- MySQL:开源免费的数据库,小型数据库。已经被Oracle收购了,MySQL5.5版本之后都是由Oracle发布的版本。
- Oracle:收费的大型数据库,Oracle公司的产品。Oracle收购SUN公司,收购MySQL
- DB2:IBM公司的数据库产品,收费的。常应用在银行系统中。在中国的互联网公司,要求去IOE(IBM小型机、Oracle数据库,EMC存储设备)
- SQL Server:Microsoft 公司收费的中型数据库。C#、.NET等语言常使用
- SyBase:已经淡出舞台,提供了一个非常专业的数据建模工具 PowerDesigner
- SQLite:嵌入式的小型数据库,应用于手机端。
MySQL与SQL介绍
- MySQL是最流行的【关系型数据库管理系统】,在WEB应用方面 MySQL是最好的 RDBMS 应用软件之一。
MySQL发展历程
- MySQL的历史可以追溯到1979年,一个名为Monty Widenius的程序员在为TcX的小公司打工,并且用BASIC设计了一个报表工具,使其可以在4MHz主频和16KB内存的计算机上运行。当时,这只是一个很底层的且仅面向报表的存储引擎,名叫Unireg。
- 1990年,TcX公司的客户中开始有人要求为他的API提供SQL支持。Monty直接借助于mSQL的代码,将它集成到自己的存储引擎中。令人失望的是,效果并不太令人满意,决心自己重写一个SQL支持。
- 1996年,MySQL 1.0发布,它只面向一小拨人,相当于内部发布。
- 到了1996年10月,MySQL 3.11.1发布(MySQL没有2.x版本),最开始只提供Solaris下的二进制版本。一个月后,Linux版本出现了。在接下来的两年里,MySQL被依次移植到各个平台。
- 1999~2000年,MySQL AB公司在瑞典成立。Monty雇了几个人与Sleepycat合作,开发出了Berkeley DB引擎,由于BDB支持事务处理,因此MySQL从此开始支持事务处理了。
- 2000,MySQL不仅公布自己的源代码,并采用GPL(GNU General Public License)许可协议,正式进入开源世界。同年4月,MySQL对旧的存储引擎ISAM进行了整理,将其命名为MyISAM。
- 2001年,集成Heikki Tuuri的存储引擎【InnoDB】,这个引擎不仅能【支持事务处理,并且支持行级锁】。后来该引擎被证明是最为成功的MySQL事务存储引擎。【MySQL与InnoDB的正式结合版本是4.0】
- 2003年12月,【MySQL 5.0】版本发布,提供了视图、存储过程等功能。
- 2008年1月,MySQL AB公司被Sun公司以10亿美金收购,MySQL数据库进入Sun时代。在Sun时代,Sun公司对其进行了大量的推广、优化、Bug修复等工作。
- 2008年11月,MySQL 5.1发布,它提供了分区、事件管理,以及基于行的复制和基于磁盘的NDB集群系统,同时修复了大量的Bug。
- 2009年4月,Oracle公司以74亿美元收购Sun公司,自此MySQL数据库进入Oracle时代,而其第三方的存储引擎InnoDB早在2005年就被Oracle公司收购。
- 2010年12月,MySQL 5.5发布,其主要新特性包括半同步的复制及对SIGNAL/RESIGNAL的异常处理功能的支持,【最重要的是InnoDB存储引擎终于变为当前MySQL的默认存储引擎】。MySQL 5.5不是时隔两年后的一次简单的版本更新,而是加强了MySQL各个方面在企业级的特性。Oracle公司同时也承诺MySQL 5.5和未来版本仍是采用GPL授权的开源产品。
SQL简介
- 【SQL是Structured Query Language的缩写】,它的前身是著名的关系数据库原型系统System R所采用的SEQUEL语言。作为一种访问【关系型数据库的标准语言】,SQL自问世以来得到了广泛的应用,不仅是著名的大型商用数据库产品Oracle、DB2、Sybase、SQL Server支持它,很多开源的数据库产品如PostgreSQL、MySQL也支持它,甚至一些小型的产品如Access也支持SQL。近些年蓬勃发展的NoSQL系统最初是宣称不再需要SQL的,后来也不得不修正为Not Only SQL,来拥抱SQL。
- 蓝色巨人IBM对关系数据库以及SQL语言的形成和规范化产生了重大的影响,第一个版本的SQL标准SQL86就是基于System R的手册而来的。Oracle在1979年率先推出了支持SQL的商用产品。随着数据库技术和应用的发展,为不同RDBMS提供一致的语言成了一种现实需要。
- 对SQL标准影响最大的机构自然是那些著名的数据库产商,而具体的制订者则是一些非营利机构,例如【国际标准化组织ISO、美国国家标准委员会ANSI】等。各国通常会按照 ISO标准和ANSI标准(这两个机构的很多标准是差不多等同的)制定自己的国家标准。中国是ISO标准委员会的成员国,也经常翻译一些国际标准对应的中文版。标准为了避免采用具体产品的术语,往往会抽象出很多名词,从而增加了阅读和理解的难度,翻译成中文之后更容易词不达意。对于数据库系统实现者和用户而言,很多时候还不如直接读英文版本为好。
- 虽然正式的标准不像RFC那样可以从网络上免费获得,标准草案还是比较容易找到的(例如:http://www.jtc1sc32.org/doc/)。待批准的标准草案和最终的标准也没有什么实质上的区别,能够满足日常工作的需要。
SQL发展的简要历史
- 1986年,ANSI X3.135-1986,ISO/IEC 9075:1986,SQL-86
- 1989年,ANSI X3.135-1989,ISO/IEC 9075:1989,SQL-89
- 1992年,ANSI X3.135-1992,ISO/IEC 9075:1992,SQL-92(SQL2)
- 1999年,ISO/IEC 9075:1999,SQL:1999(SQL3)
- 2003年,ISO/IEC 9075:2003,SQL:2003
- 2008年,ISO/IEC 9075:2008,SQL:2008
- 2011年,ISO/IEC 9075:2011,SQL:2011
- 如果要了解标准的内容,比较推荐的方法是【泛读SQL92】(因为它涉及了SQL最基础和最核心的一些内容),然后增量式的阅读其他标准。
- 不只是mysql还有其他数据库,在SQL92或者SQL99这些国际SQL标准基础之上,它们还扩展了自己的一些SQL语句,比如MySQL中的limit关键字。
SQL语言分类
- 数据定义语言【DDL】:用来定义数据库对象(数据库、表、列等),关键字:create、alter、drop等
- 数据操作语言【DML】:用于对数据库中标的记录进行更新,关键字:insert、delete、update等
- 数据控制语言【DCL】:用于定义数据库的访问权限和安全级别,及创建用户,关键字:grant等
- 数据查询语言【DQL】:用来查询数据库中表的记录,关键字:select等
MySQL单机安装
- 环境:
* 操作系统:CentOS 7
* MySQL版本:5.6
卸载MySQL
- 查看是否还有MySQL软件,有的话继续删除。软件卸载完毕后,如果需要可以删除 MySQL的数据库:/var/lib/mysql。
# 查看MySQL软件
rpm -qa | grep mysql
# 卸载MySQL
yum remove -y mysql mysql-libs mysql-common
rm -rf /var/lib/mysql
rm /etc/my.cnf
安装MySQL
wget http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm
rpm -ivh mysql-community-release-el6-5.noarch.rpm
yum install -y mysql-community-server
- 安装好以后可以查看一下:
配置并启动
vim /etc/my.cnf
修改内容如下:
[mysqld]
# MySQL设置大小写不敏感,默认:区分表名的大小写,不区分列名的大小写
# 0:大小写敏感;1:大小写不敏感
lower_case_table_names=1
# 默认字符集
character-set-server=utf8mb4
- utf8 和 utf8mb4 的区别:
- MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。好在utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。当然,为了节省空间,一般情况下使用utf8也就够了。
- 那上面说了既然utf8能够存下大部分中文汉字,那为什么还要使用utf8mb4呢?原来mysql支持的 utf8 编码最大字符长度为 3 字节,如果遇到 4 字节的宽字符就会插入异常了。三个字节的 UTF-8 最大能编码的 Unicode 字符是 0xffff,也就是 Unicode 中的基本多文种平面(BMP)。也就是说,任何不在基本多文本平面的 Unicode 字符,都无法使用 Mysql 的 utf8 字符集存储。包括 Emoji 表情(Emoji 是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上),和很多不常用的汉字,以及任何新增的 Unicode 字符等等(utf8的缺点)。
- 启动MySQL:
systemctl start mysqld
- 启动可能会报错
- 根据提示我们可以输入命令:systemctl status mysqld.service 和 journalctl -xe 查看一下
- 但是并没有说明具体的失败原因,进一步查看日志,vi /var/log/mysqld.log:
- 最后发现是设置字符集有问题,注释掉 default-character-set=utf8 就可以正常启动了
密码设置与远程登录
# 设置root用户密码
/usr/bin/mysqladmin -u root password '123456'
# 登录MySQL
mysql -uroot -p
- 远程连接授权:grant 权限 on 数据库对象 to 用户
# 授予root用户对所有数据库对象的全部操作权限
grant all privileges on *.* to 'root'@'%' identified by '123456' with grant option;
- 命令说明:
- all privileges:表示授予所有的权限,此处可以指定具体的授权权限
- *.*:表示所有库中的所有表
- ‘root’@‘%’:root是数据库的用户名,%表示是任意ip地址,可以指定具体的ip地址
- identified by ‘123456’:123456是数据库的密码
# 关闭linux的防火墙
systemctl stop firewalld(默认)
systemctl disable firewalld.service(设置开机不启动)
DDL、DML、DQL
DDL语句
对数据库的基本操作
-- 创建数据库
create database 数据库名;
create database 数据库名 character set 字符集;
-- 查看数据库
-- 查看数据库服务器中所有的数据库
show databases;
-- 查看某个数据库的定义信息
show create database 数据库名;
-- 删除数据库(慎用)
drop database 数据库名;
-- 其他数据库操作命令
-- 切换数据库
use 数据库名;
-- 查看正在使用的数据库
select database();
对表的基本操作
-- 创建表 ------------------------------------------------------------
create table 表名(
字段名 类型(长度) 约束,
字段名 类型(长度) 约束,
...
);
-- 主键约束:primary key
-- 唯一约束:unique
-- 非空约束:not null
-- 注意:主键约束 = 唯一约束+非空约束
-- 查看表 ------------------------------------------------------------
-- 查看数据库中所有的表
show tables;
-- 查看表结构
desc 表名;
-- 删除表 ------------------------------------------------------------
drop table 表名;
-- 修改表 ------------------------------------------------------------
-- 修改表——添加列
alter table 表名 add 列名 类型(长度) 约束;
-- 修改表——修改列的类型、长度和约束
alter table 表名 modify 列名 类型(长度) 约束;
-- 修改表——修改列名
alter table 表名 change 旧列名 新列名 类型(长度) 约束;
-- 修改表——删除列
alter table 表名 drop 列名;
-- 修改表名
rename table 表名 to 新表名;
-- 修改表的字符集
alter table 表名 character set 字符集;
- 常见的字段类型有:
- 数字型:int
- 浮点型:double
- 字符型:varchar(可变长字符串)
- 日期类型:date(只有年月日,没有时分秒),datetime(年月日,时分秒)
- boolean类型:不支持,一般使用 tinyint 替代(值为0和1)
分类 | 类型名称 | 说明 |
---|---|---|
整数类型 | tinyint | 很小的整数 |
smallint | 小的整数 | |
mediumint | 中等大小的z整数 | |
int(integer) | 普通大小的整数 | |
小数类型 | float | 单精度浮点数 |
double | 双精度浮点数 | |
小数类型 | decimal(m, d) | 压缩严格的定点数–开发时用 |
日期类型 | year | YYYY 1901~2155 |
time | HH:MM:SS -838:59:59~838:59:59 | |
date | YYYY-MM-DD 1000-01-01~9999-12-3 | |
datetime-开发用 | YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~9999-12-31 23:59:59 | |
timestamp | YYYY-MM-DD HH:MM:SS 1970-01-01 00:00:01UTC~2038-01-19 03:14:07UTC | |
文本、二进制类型 | CHAR(M) | M 为0~255直接的整数 |
VARCHAR(M) | M 为0~65535直接的整数 | |
TINYBLOB | 允许长度0~255字节 | |
BLOB | 允许长度0~65535字节 | |
MEDIUMBLOB | 允许长度0~167772150字节 | |
LONGBLOB | 允许长度0~4294967296字节 | |
TINYTEXT | 允许长度0~255字节 |
DML语句
插入记录 INSERT
- 语法
-- 向表中插入某些列
insert into 表名 (列名1, 列名2, 列名3, ...) values (值1, 值2, 值3, ...);
-- 向表中插入所有列
insert into 表名 values (值1, 值2, 值3, ...);
insert into 表名 (列名1, 列名2, 列名3, ...) values select (列名1, 列名2, 列名3, ...) from 表名;
insert into 表名 values select * from 表名;
- 注意:
- 列名数与values后面的值的个数相等
- 列的顺序与插入的值的顺序一致
- 列名的类型与插入的值要一致
- 插入值的时候不得超过最大长度
- 值如果是字符串或者日期需要加 '' 号(一般是单引号)
更新记录 UPDATE
- 语法
update 表名 set 字段名1 = 值1, 字段名2 = 值2 [where 条件];
- 注意:
- 列名的类型要和修改的值要一致
- 修改值的时候不能超过最大长度
- 值如果是字符串或者日期需要加 '' 号
删除记录 DELETE
- 语法
delete from 表名 [where 条件];
- 面试题:删除表中所有记录,是用【delete from 表名】还是用【truncate table 表名】?
- delete:一条一条删除,不清空 auto_increment 记录数。
- truncate:直接将表删除,重新创建,auto_increment 将重置为零,重新开始。
DQL语句
- 数据准备:
-- 创建商品表
create table products(
pid int primary key auto_increment comment '商品自增主键id',
pname varchar(20) comment '商品名称',
price double comment '商品价格',
pdate datetime comment '日期',
sid varchar(20) comment '分类ID'
);
-- 添加一些数据
insert into products (pname, price, pdate, sid) values('泰国大榴莲', 98, '2018-01-01', 's001');
insert into products (pname, price, pdate, sid) values('新疆大枣', 38, '2018-11-23', 's002');
insert into products (pname, price, pdate, sid) values('新疆切糕', 68, '2019-02-25', 's003');
insert into products (pname, price, pdate, sid) values('十三香', 10, '2017-12-3', 's004');
insert into products (pname, price, pdate, sid) values('老干妈', 20, '2019-03-05', 's005');
DQL语法顺序
SELECT DISTINCT
<select_list>
FROM
<left_table> <join_type>
JOIN <right_table> ON <join_condition>
WHERE
<where_condition>
GROUP BY
<group_by_list>
HAVING
<having_condition>
ORDER BY
<order_by_condition>
LIMIT <limit_number>
简单查询
- 语法关键字:SELECT FROM
-- 查询所有商品
select * from products;
-- 查询商品名称和商品价格
select pname, price from products;
-- 别名查询,使用as关键字,as可以省略
-- 表别名
select * from products as p;
-- 列别名
select pname as pn from products;
-- 去重复值
select distinct price from products;
-- 查询结果是表达式(运算查询):将所有商品的价格加10进行显示
select pname, price+10 from products;
条件查询
- 语法关键字:WHERE
-- 查询商品名称为十三香的商品所有信息
select * from products where pname = '十三香';
-- 查询商品价格大于60元的所有商品信息
select * from products where price > 60;
- where后的条件写法:
>、<、=、>=、<=、<>:大于、小于、等于、大于等于、小于等于、不等于
like:使用占位符 _ 和 %
_ 代表一个字符
% 代表任意个字符
in:在某个范围中获得值(exists)
分类 | 类型名称 | 说明 |
---|---|---|
整数类型 | >、<、=、>=、<=、<> | 大于、小于、等于、大于等于、小于等于、不等于 |
BETWEEN … AND … | 显示在某一区间的值(含头含尾) | |
IN(set) | 显示in列表中的值,例:in(100, 200) | |
LIKE ‘张_’ | 模糊查询,like语句中,%代表零个或多个任意字符,_代表一个字符 | |
IS NULL | 判断是否为空 | |
逻辑运算符 | and | 多个条件同时成立 |
or | 多个条件任一成立 | |
not | 不成立 |
排序
- 语法关键字:ORDER BY ASC(升序) DESC(降序)
- 示例:
-- 查询所有商品,按价格进行排序
select * from products order by price;
-- 查询名称有【新】的商品信息,并按价格降序排序
select * from products where pname like '%新%' order by price desc;
聚合函数(组函数)
- 特点:只对单列进行操作
- 常用的聚合函数:
- sum():求某一列的和
- avg():求某一列的平均值
- max():求某一列的最大值
- min():求某一列的最小值
- count():求某一列的元素个数
-- 获取所有商品的价格的总和
select sum(price) from products;
-- 获得所有商品的平均价格
select avg(price) from products;
-- 获得所有商品的个数
select count(*) from products;
分组
- 语法关键字:GROUP BY、HAVING
-- 根据sid字段分组,分组后统计商品的个数
select sid, count(*) from products group by sid;
-- 根据sid字段分组,分组统计每组商品的平均价格,并且平均价格大于60
select sid, avg(price) from products group by sid having avg(price) > 60;
- 注意事项:
* select语句中的列(非聚合函数列),必须出现在 group by 子句中
* group by 子句中的列,不一定要出现在 select 语句中
* 聚合函数只能出现在 select 语句中,或者 having 语句中,一定不能出现在 where 语句中
分页查询
- 语法关键字:LIMIT [offset, ] rows,offset:偏移量,rows:每页多少行记录
- 分页分为逻辑分页和物理分页
* 逻辑分页:将数据库中的数据查询到内存之后再进行分页
* 物理分页:通过 LIMIT 关键字,直接在数据库中进行分页,最终返回的数据只是分页后的数据
子查询
- 定义:
* 子查询允许把一个查询嵌套在另一个查询当中
* 子查询,又叫内部查询,相当于内部查询,包含内部查询的就称为外部查询
* 子查询可以包含普通的 select,可以包含任何子句,比如:distinct、group by、order by、limit、join和union等
* 但是对应的外部查询必须是以下语句之一:select、insert、update、delete
- 位置:select中、from后、where中;group by 和 order by中无实用意义
其他查询语句
- union:集合的并集(不包含重复记录)
- unionall:集合的并集(包含重复记录)
SQL解析顺序
- 首先看一下示例语句:
SELECT DISTINCT
<select_list>
FROM
<left_table> <join_type>
JOIN <right_table> ON <join_condition>
WHERE
<where_condition>
GROUP BY
<group_by_list>
HAVING
<having_condition>
ORDER BY
<order_by_condition>
LIMIT <limit_number>
- 然而它的执行顺序是这样的:
1. FROM <left_table>
2. ON <join_condition>
3. <join_type> JOIN <right_table> 第2步和第3步会循环执行
4. WHERE <where_condition> 第4步会循环执行,每个条件的执行顺序是从左往右的
5. GROUP BY <group_by_list>
6. HAVING <having_condition>
7. SELECT DISTINCT <select_list> 分组之后才会执行 SELECT
8. ORDER BY <order_by_condition>
9. LIMIT <limit_number> LIMIT是MySQL独有的语法
准备工作
- 基础数据:
-- 创建数据库
create database testQuery;
use testQuery;
-- 创建测试表
create table table1(
uid varchar(10) not null,
name varchar(10) not null,
primary key(uid)
)engine=innodb default charset=utf-8;
create table table2(
oid int not null auto_increment,
uid varchar(10),
primary key(oid )
)engine=innodb default charset=utf-8;
-- 插入数据
insert into table1(uid, name) values('aaa', 'mike'), ('bbb', 'jack'), ('ccc', 'mike'), ('ddd', 'mike');
insert into table2(uid) values('aaa'), ('aaa'), ('bbb'), ('bbb'), ('bbb'), ('ccc'), (NULL);
- 最后需要的结果
select a.uid, count(b.oid) as total
from table1 as a
left join table2 as b on a.uid = b.uid
where a.name = 'mike'
group by a.uid
having count(b.oid) < 2
order by total desc
limit 1;
1. FROM
- 对FROM的左边的表和右边的表计算
笛卡尔积(CROSS JOIN)
,产生虚表VT1
。
2. ON过滤
- 对
虚表VT1
进行ON筛选,只有那些符合的行才会被记录在虚表VT2
中。 - 注意:这里因为语法限制,使用WHERE代替,从中读者也可以感受到两者之间微妙的关系。
3. OUTER JOIN添加外部列
- 如果指定了
OUTER JOIN(比如left join、right join)
,那么保留表中未匹配的行就会作为外部行添加到虚拟表VT2中,产生虚表VT3
。 - 如果FROM子句中包含两个以上的表的话,那么就会对上一个join连接产生的结果VT3和下一个表重复执行步骤1-3,直到处理完所有表为止。
4. WHERE
- 对
虚表VT3
进行WHERE条件过滤,只有符合的记录才会被插入到虚拟表VT4
中。
注意:此时因为分组,不能使用聚合运算,也不能使用 SELECT 中创建的别名
与ON的区别:- 如果有外部列,ON针对过滤的是关联表,主表(保留表)会返回所有的列;
- 如果没有外部列,两者的效果是一样的。
应用: - 对主表的过滤应该放在WHERE;
- 对于关联表,先条件查询后连接则用ON,先连接后条件查询则用WHERE。
5. GROUP BY
- 根据group by子句中的列,对VT4中的记录进行分组操作,产生
虚表VT5
。
注意:其后处理过程的语句,如SELECT、HAVING,所用到的列必须包含在 GROUP BY中。对于没有出现的得用聚合函数。 - 原因:GROUP BY改变了对表的引用,将其转换为新的引用方式,能够对其进行下一步逻辑操作的列会减少。
- 个人理解:根据分组字段,将具有相同分组字段的记录归并成一条记录,因为每一个分组只能返回一条记录,除非是被过滤掉了,而不在分组字段里面的字段可能会有多个值,多个值是无法放进一条记录的,所以必须通过聚合函数将这些具有多值的列转换为单值。
6. HAVING
- 对
虚表VT5
应用having过滤,只有符合的记录才能被插入到虚表VT6
中。
7. SELECT
- 对
虚表VT6
进行列筛选处理、计算表达式等,生成虚表VT7
。
8. DISTINCT
- 寻找
虚表VT7
中的重复列,并删除。如果在查询中指定了DISTINCT子句,则会创建一张内存临时表(如果内存放不下,就需要存放在硬盘了),这样临时表的表结构和上一步产生的虚表VT7
是一样的,不同的是对进行DISTINCT操作的列增加了一个唯一索引,以此来去除重复数据。
9. ORDER BY
- 从
虚表VT7
中,根据ORDER BY子句的条件对结果进行排序,生成虚表VT8
。 - 注意:唯一可使用SELECT中别名的地方
10. LIMIT(MySQL特有)
- LIMIT子句从上一步得到的
虚表VT8
中选出从指定位置开始的指定行数据
注意:offset 和 rows 的大小会带来影响:当偏移量很大的时效率会很低,可以这样做:- 采用子查询的方式优化,在子查询里从索引获取最大id,然后倒序排,再取N行结果集;
- 采用INNER JOIN优化,JOIN子句里也优先从索引获取ID列表,然后直接关联查询获得最终结果
解析顺序总结
- 图示:
- 流程分析:
* FROM(将最近的两张表,进行笛卡尔积)---VT1
* ON(将VT1按照它的条件进行过滤)---VT2
* LEFT JOIN(保留左边表的记录)---VT3
* WHERE(过滤VT3中的记录)---VT4...VTn
* GROUP BY(对VT4的记录进行分组)---VT5
* HAVING(对VT5中的记录进行过滤)---VT6
* SELECT(对VT6中的记录,选取指定的列)---VT7
* ORDER BY(对VT7的记录进行排序)---VT8
* LIMIT(对排序之后的值进行分页)
- 流程说明:
* 单表查询:根据WHERE条件过滤表中的数据,形成中间表(这个中间表对用户是不可见的);然后根据SELECT的选择列选择相应的列进行返回最终结果。
* 两表连接查询:对两表求积并用ON条件和连接类型进行过滤形成中间表;然后根据WHERE条件过滤中间表的记录,并根据SELECT指定列的返回结果
* 多表连接查询:先对第一个和第二个表按照两表连接查询,然后用查询结果和第三个表连接查询,以此类推,直到所有的表都连接上为止,最终形成一个中间的结果表,然后根据WHERE条件过滤中间表的记录,并根据SELECT指定列的返回结果
- WHERE条件的解析顺序
* MySQL:从左往右执行WHERE条件
* Oracle:从右往左执行WHERE条件
- 写WHERE条件的时候,优先级高的部分要去编写过滤力度最大的条件语句。
多表关联
表与表之间的关系
- 一对一:一夫一妻
- 一对多:用户和订单
- 多对多:商品和订单
- 外键:保证数据完整性(数据保存在多张表中的时候);在互联网项目中,一般情况下,不建议建立外键关系。
-- 如何操作外键
-- 主表添加外键
alter table 表名 add [constraint][约束名称] foreign key (主表外键字段) references 从表(从表主键);
-- 主表删除外键
alter table 表名 drop foreign key 外键约束名称
一对一关系(了解)
- 在实际工作中,一对一在开发中应用不多,因为一对一完全可以创建成一张表。例如:一个丈夫只能有一个妻子。
CREATE TABLE wife(
id INT PRIMARY KEY ,
wname VARCHAR(20),
sex CHAR(1)
);
CREATE TABLE husband(
id INT PRIMARY KEY ,
hname VARCHAR(20),
sex CHAR(1)
);
- 一对一关系创建方式1之外键唯一:
- 添加外键列wid,指定该列的约束为唯一(不加唯一约束就是一对多关系)
ALTER TABLE husband ADD wid INT UNIQUE;
// 添加外键约束
alter table husband add foreign key (wid) references wife(id);
- 一对一关系创建方式2之主键做外键:思路——使用主表的主键作为外键去关联从表的主键。
一对多关系
- 案例:一个分类对应多个商品
- 有外键的就是多的一方
- 注意事项:一对多关系和一对一关系的创建很类似,唯一区别就是外键不唯一。
- 一对多关系创建:添加外键列,添加外键约束。
* 在商品表中添加一条记录,该记录的cid在分类表中不存在。
* 在分类表中,删除一条记录,这条记录在商品表中有外键关联。
多对多关系
- 案例:同一个商品对应多个订单,一个订单对应多个商品。
- 注意事项:需要中间表去完成多对多关系的创建,多对多关系其实就是两个一对多关系的组合。
- 多对多关系创建:创建中间表,并在其中创建多对多关系中两张表的外键列;在中间表中添加外键约束;在中间表中添加联合主键约束。
* 用户和角色
* 1个用户对多个角色、1个角色对多个用户
* 中间表用户角色表 uid rid
多表关联查询
- JOIN 按照功能大致分为如下三类:
- CROSS JOIN(交叉连接)
- INNER JOIN(内连接 或 等值连接)
- OUTER JOIN(外连接)
- 基础数据准备:
create table category(
cid varchar(32) primary key,
cname varchar(100)
) charset = utf8 comment = '分类表';
create table product(
pid varchar(32) primary key,
pname varchar(40),
price double,
cid varchar(32)
) charset = utf8 comment = '商品表';
create table orders(
oid varchar(32) primary key,
totalprice double
) charset = utf8 comment = '订单表';
create table order_item(
oid varchar(32) primary key,
pid varchar(50)
) charset = utf8 comment = '订单项表';
insert into category(cid, cname) values('c001', '家电'), ('c002', '服装'), ('c003', '化妆品');
insert into product(pid, pname, price, cid) values('p001', '联想', 5000, 'c001'), ('p002', '海尔', 3000, 'c001'), ('p003', '雷神', 5000, 'c001'), ('p004', '阿迪', 1000, 'c002'), ('p005', '耐克', 1200, 'c002'), ('p006', 'NB', 800, 'c002'), ('p007', '彪马', 600, 'c002'), ('p008', '雪花秀', 1500, 'c003'), ('p009', '悦诗风吟', 1100, 'c003');
交叉连接
- 关键字:CROSS JOIN
- 交叉连接也叫笛卡尔积连接
-- 隐式交叉连接
select * from A, B
-- 显示交叉连接
select * from A CROSS JOIN B
内连接
- 关键字:INNER JOIN
- 内连接也叫做等值连接,内连接使用比较运算符根据每个表共有的列的值匹配两个表中的行
-- 隐式内连接
select * from A, B where A.id = B.id
-- 显示内连接
select * from A INNER B ON A.id = B.id
外连接
- 外连接可以分为:左外连接、右外连接和全外连接,外连接需要有主表或者保留表的概念
- 使用总结:
* 通过业务需求,分析主从表
* 如果使用 LEFT JOIN,则主表在左边
* 如果使用 RIGHT JOIN,则主表在右边
* 查询结果以主表为主,从表记录匹配不到,则补null
* 全外连接(FULL JOIN)MySQL不支持