MySQL必知必会学习历程(一)
- 前言
- 第1章 了解SQL
- 1.1 关键术语介绍
- 1.2 数据表解释实例
- 第2章 MySQL简介
- 2.1 关键术语介绍
- 2.2 安装mysql命令行实用程序(可选)
- 2.3 安装mysql_community(最优)
- 2.3.1 下载mysql_community
- 2.3.2 安装过程
- 第3章 使用MySQL
- 3.1 连接到数据库
- 3.2 建立样例数据库
- 3.2.1 创建空的数据库
- 3.2.2 下载样例表生成脚本
- 3.2.3 使用样例表生成脚本
- 3.3 选择数据库(USE)
- 3.4 了解数据库和表(SHOW)
- 3.5 命令及注释汇总
- 第4章 检索数据(SELECt)
- 4.1 检索单个列
- 4.2 检索多个列
- 4.3 检索所有列
- 4.4 检索不相同的行(DISTINCT)
- 4.5 限制检索出的行数(LIMIT)
- 4.6 使用完全限定的表名
- 4.7 命令及注释汇总
- 第5章 排序检索数据(ORDER BY)
- 5.1 简单排序数据
- 5.2 按多个列排序
- 5.3 指定排序的方向(DESC)
- 5.4 命令及注释汇总
- 第6章 过滤数据(WHERe)
- 6.1 简单过滤
- 6.2 单个值过滤(=><)
- 6.3 不匹配筛选(!=或<>)
- 6.4 严格的范围筛选(BETWEEN AND)
- 6.4 空值检查
- 6.5 命令及注释汇总
- 第7章 数据过滤(WHERe高级应用)
- 7.1 使用AND操作符
- 7.2 使用OR操作符
- 7.3 运算次序
- 7.4 使用IN操作符
- 7.5 使用NOT操作符
- 7.6 命令及注释汇总
- 第8章 用通配符进行过滤(LIKE)
- 8.1 使用%通配符
- 8.2 深入%通配符
- 8.3 使用_通配符
- 8.4 代码及注释总结
- 第9章 用正则表达式进行搜索(REGEXP)
- 9.1 基本字符匹配
- 9.2 区分大小写(BINARY)
- 9.3 进行OR匹配(|)
- 9.4 匹配几个字符之一([])
- 9.5 否定字符(^)
- 9.6 匹配范围([X-Y])
- 9.7 特殊字符匹配(\\)
- 9.8 匹配字符类
- 9.9 匹配多个实例
- 9.10 使用定位符
- 9.11 命令及注释汇总
- 第10章 创建计算字段
- 10.1 拼接字段(Concat())
- 10.2 去除空格(Trim()等)
- 10.3 使用AS关键字
- 10.4 执行算术运算(+-*/)
- 10.5 命令及注释汇总
- 第11章 使用数据处理函数
- 11.1 文本处理函数
- 11.2 日期和时间处理函数
- 11.3 数值处理函数
- 11.4 命令及注释汇总
- 第12章 汇总/聚集数据
- 12.1平均值函数(AVG())
- 12.2 行数总结函数(COUNT())
- 12.3 最大值函数(MAX())
- 12.4 最小值函数(MIN())
- 12.5 求和函数(SUM())
- 12.6 排除重复元素(DISTINCT)
- 12.7 组合聚集函数
- 12.8 命令及注释汇总
- 第13章 分组数据(GROUP BY+HAVINg)
- 13.1 创建分组
- 13.2 汇总级别(WITH ROLLUP)
- 13.3 过滤数据(HAVINg)
- 13.4 排序数据(ORDER BY)
- 13.5 命令及注释总结
- 第14章 使用子查询(IN)
- 14.1 利用子查询进行过滤
- 14.2 使用计算字段建立子查询
- 14.3 命令及注释汇总
- 第15章 联结表
- 15.1 关系表(关系数据库)的理解
- 15.2 创建联结
- 15.3 笛卡尔积(没意义)
- 15.4 内部联结
- 15.5 联结多个表
- 15.6 替代子查询
- 15.7 命令及注释汇总
- 第16章 创建高级联结
- 16.1 使用表别名
- 16.2 自联结
- 16.3 自然联结
- 16.4 外部联结
- 16.5 带聚集函数联结
- 16.6 命令及注释汇总
- 第17章 组合查询(UNIOn)
- 17.1 使用UNIOn
- 17.2 UNIOn使用规则
- 17.2 包含重复行(UNIOn ALL)
- 17.3 排序查询结果
- 17.4 命令及注释汇总
- 转到第18-30章
前言
重现《MySQL必知必会》这本书是个挺大的工程,肯定有些地方存在纰漏,若发现问题,希望各位多多指教。
这篇博客不能简单地认为是《MySQL必知必会》的电子版,它是我对这本书中知识的实践过程,充满了例子,已经提前把坑踩过了。
博客中对书中文字进行了简化,能用图的绝不用文字,因此生动些,但是内容也缩减了,如果希望深入了解,建议还是买书来读。
最后祝愿所有人一学就会,成为数据库管理大师!
第1章 了解SQL
1.1 关键术语介绍
数据库(database):保存有组织数据的容器
模式(schema):关于数据库和表的布局特性的信息(注:MySQL中将模式等同于数据库)
表(table):某种特定类型数据的结构化清单,放在数据库中(注:一个数据库中可以有多个表)
列(column):表中的一个字段,表均为一个列或多个列组成,一个列存储一类信息(注:例如身份信息中的名字、民族)
数据类型(data type):一个列所容许的数据类型,限制了列中存储数据的种类
行(row):一行就是表中的一个记录,表中的数据是按照行储存的(注:如身份信息中,一个人的所有身份信息)
主键(primary key):一个列(或一组列),这个列中的数据(或组和信息)不能重复,能够唯一区分表中的行
SQL(发音S-Q-L或者sequel,即 谁(sei)哭欧):是结构化查询语言(Structured Query Language),专门用来与数据库通信的语言
DBMS(database management system):数据库管理系统,对于数据进行检索、存储、管理,我们通过发送命令控制其工作
1.2 数据表解释实例
例子:
假如这是一个数据库中的一个表(一个数据库可以有多个表),存储了6个人的身份信息。
那么籍贯是一个列的列名,这一列存储了相同数据类型的信息,这里是CHAR类型的。
行是一个记录,是没有行名的,区别不同的行需要使用主键,我们可以指定 序号列 或 身份证号列 为主键。
第2章 MySQL简介
2.1 关键术语介绍
MySQL:是一款DBMS软件,它拥有免费、高性能、可信赖、简单等特点。
运作方式:MySQL是一款基于客户机-服务器的DBMS,需要安装客户机和服务软件。
服务器软件:MySQL DBMS,可以安装到本地或者远程服务器中。
客户机软件:MySQL Query Browser、Perl、JSP、C、C++、JAVA等。
2.2 安装mysql命令行实用程序(可选)
学习MySQL的话,暂时没必要。
2.3 安装mysql_community(最优)
mysql_community是官方提供的数据库管理软件集,包含了服务器和客户机的功能,我们只需要安装这个全家桶就好了
2.3.1 下载mysql_community
下载地址:MySQL安装程序
首先选择需要下载的程序,我们选择本地安装的,安装过程中不需要联网。
这里我们不需要注册和登录,直接下载即可
2.3.2 安装过程
双击下载的文件,开始安装(全程傻瓜式安装,基本上就点下一步就好了)。
选择安装类型,因为我们是为了学习,所以直接选择全家桶。
选择安装文件夹
确认需求
查看要安装的程序,启动安装过程。(时间比较久)
配置安装的各个子软件
剩下的就一直下一步就好了。
第3章 使用MySQL
3.1 连接到数据库
在启动菜单栏中找到 MySQL Workbench 8.0 CE,打开,界面如下
连接数据库
输入密码并记住密码
常用面板介绍
3.2 建立样例数据库
3.2.1 创建空的数据库
可以看到导航栏里面增加了名为crashcourse的数据库,双击它。
3.2.2 下载样例表生成脚本
下载链接:样例表脚本
解压,里面有两个文件,create.sql和populate.sql
3.2.3 使用样例表生成脚本
首选创建数据表
create.sql为数据库建立了数据表(表中包含了列),但是并未填入数据。
之后,以同样的方式打开populate.sql,并以同样方式运行。polulate.sql做的工作就是填入数据。
至此,样例表创建完成。
3.3 选择数据库(USE)
新建脚本文件:
在脚本中输入命令:
USE crashcourse;
执行命令(执行单行命令的快捷键是 Crtl+Enter )
注意1:MySQL的一行命令结尾总是分号(;),必须以分号作为结尾,否则无法执行。
注意2:MySQL的代码不区分大小写,但是我们一般将系统保留字(即命令或者关键字)写作大写。
这意味着我们正在使用名为crashcourse的数据库(不会返回任何结果),和双击数据库名效果一致。
3.4 了解数据库和表(SHOW)
输入命令:
SHOW DATABASES;
执行命令,显示当前可用的数据库,结果如下:
3.5 命令及注释汇总
下面是可用的查询语句及其注释(注释可以用 “–” 或者 “#” ),可以自己尝试使用。
#使用数据库
USE crashcourse;
#显示当前可用数据库
SHOW DATABASES;
#显示当前数据库可用的数据表
SHOW TABLES;
#显示customers数据表中可用的列信息。
SHOW COLUMNS FROM customers;
#同上,显示customers数据表中可用的列信息。
DESCRIBE customers;
#显示服务器的状态信息
SHOW STATUS;
#显示当前用户享有的权限信息
SHOW GRANTS;
#显示服务器的错误信息
SHOW ERRORS;
#显示服务器的警告信息
SHOW WARNINGS;
第4章 检索数据(SELECt)
SELECT是最常用的命令,用于选择数据。
4.1 检索单个列
键入命令并运行:
SELECT prod_name FROM products;
意味着从products数据表中,选择prod_name这一列数据,作为返回结果。结果如下:
4.2 检索多个列
键入命令并运行:
SELECt prod_name,prod_id,vend_id FROM products;
只需要在不同的列名之间加入逗号(,)即可,结果如下:
4.3 检索所有列
键入命令并运行:
SELECt * FROM products;
星号(*)是一个通配符,意味着选择所有列。结果如下:
4.4 检索不相同的行(DISTINCT)
键入命令并运行:
SELECt DISTINCT vend_id FROM products;
使用了DISTINCT关键字,去除了重复的结果。结果如下:
4.5 限制检索出的行数(LIMIT)
键入命令并运行:
SELECt prod_name FROM products LIMIT 5;
使用了LIMIT关键字,限制输出5行结果。结果如下:
还可以施加更高级的限制,代码和注释如下:
# 指定检索的行起始位置以及行数(第一个数字是位置,第二个是行数)(初始位置为0)
SELECt prod_name FROM products LIMIT 1,3;
# 效果同上
SELECt prod_name FROM products LIMIT 3 OFFSET 1;
4.6 使用完全限定的表名
键入命令并运行:
SELECt prod_name FROM products;
SELECt products.prod_name FROM products;
SELECt products.prod_name FROM crashcourse.products;
分别运行三行命令,发现结果一样。此处留下一个概念即可,我们在15章联结表会用到这个。
4.7 命令及注释汇总
# 使用表crashcourse
USE crashcourse;
# 检索单个列
SELECt prod_name FROM products;
# 检索多个列
SELECt prod_name,prod_id,vend_id FROM products;
# 检索所有列
SELECt * FROM products;
# 检索出不相同的行
SELECt DISTINCT vend_id FROM products;
# 限制检索出的行数
SELECt prod_name FROM products LIMIT 5;
# 指定检索的行起始位置以及行数(第一个数字是位置,第二个是行数)(初始位置为0)
SELECt prod_name FROM products LIMIT 1,3;
# 效果同上
SELECt prod_name FROM products LIMIT 3 OFFSET 1;
# 完全限定列名
SELECt products.prod_name FROM products;
# 完全限定列名和表名
SELECt products.prod_name FROM crashcourse.products;
第5章 排序检索数据(ORDER BY)
ORDER BY 是将检索出的数据按照校对(第27章涉及)顺序进行排序
5.1 简单排序数据
键入下面命令并运行:
SELECt prod_name FROM products ORDER BY prod_name;
这里使用prod_name的内容,即prod_name的字符顺序对输出结果进行排序。结果如下:
可以看到,对此字符校对方式排序的优先顺序是:’.’,‘0-9’,‘A-Z’,‘a-z’。
5.2 按多个列排序
键入下面命令并运行:
SELECt prod_id,prod_price,prod_name FROM products ORDER BY prod_price,prod_name;
这里先用prod_price排序,排序之后的结果再用prod_name排。结果如下:
可以看到,prod_price是完全有序的,而prod_name是在prod_price排序的基础上进行二次排序的。
5.3 指定排序的方向(DESC)
键入以下命令并运行:
SELECt prod_id,prod_price,prod_name FROM products ORDER BY prod_price DESC, prod_name;
我们使用DESC指定prod_price按照降序进行排序,结果如下:
注意1:DESC只用于其前面的一个列,如果需要列均降序排列,那么需要一一指定。
注意2:ASC是升序排序,但是升序排序时默认的,因此没什么用。
5.4 命令及注释汇总
USE crashcourse;
# 未排序检索
SELECt prod_name FROM products;
# 以prod_name列的字母顺序进行排序
SELECt prod_name FROM products ORDER BY prod_name;
# 按照多个列进行排序,先按照第一个参数排,再按照第二个排
SELECt prod_id,prod_price,prod_name FROM products ORDER BY prod_price,prod_name;
# 按照降序顺序进行排序
SELECt prod_id,prod_price,prod_name FROM products ORDER BY prod_price DESC;
# DESC只应用于其前面挨着的参数,如果有多个降序,需要用多个DESC,所以可以多样排序
#(ASC是升序排序,默认也是升序排序,因此没什么用)
SELECt prod_id,prod_price,prod_name FROM products ORDER BY prod_price DESC, prod_name;
# 找出表中最贵的物品的价钱
SELECt prod_price FROM products ORDER BY prod_price DESC LIMIT 1;
第6章 过滤数据(WHERe)
WHERe子句置于SELECt语句的后面,将SELECT获得数据进行筛选
6.1 简单过滤
键入下面代码并运行:
SELECT prod_name,prod_price FROM products WHERe prod_price=2.50;
这里WHERe后面跟的条件是prod_price=2.50,那么返回的结果只包括符合条件的。结果如下图:
6.2 单个值过滤(=><)
键入下面代码并运行:
SELECt prod_name,prod_price FROM products WHERe prod_name='fuses';
SELECt prod_name,prod_price FROM products WHERe prod_price<=10;
第一句只返回prod_name=‘fuses’(注:字符筛选默认不区分大小写)的结果,是全等筛选:
第二句采用了小于等于(<=)符号,这种也是MySQL所支持的。结果分别如下:
6.3 不匹配筛选(!=或<>)
键入下面命令并运行:
SELECt vend_id,prod_name FROM products WHERe vend_id<>1003;
SELECt vend_id,prod_name FROM products WHERe vend_id!=1003;
这里筛选出的是vend_id不等于1003的结果,两句效果相同。结果如下:
6.4 严格的范围筛选(BETWEEN AND)
键入以下命令并运行:
SELECt prod_name,prod_price FROM products WHERe prod_price BETWEEN 5 AND 10;
显然,这里寻找的是prod_price∈[5,10]的结果。结果如下图:
6.4 空值检查
键入以下命令并运行:
SELECt cust_id,cust_email FROM customers WHERe cust_email IS NULL;
SELECt cust_id,cust_email FROM customers WHERe cust_email IS NOT NULL;
第一句使用 IS NULL 筛选出cust_email为空的结果。如下所示:
第二句加了NOT,得到相反的结果:
6.5 命令及注释汇总
USE crashcourse;
# 使用where进行一般筛选
SELECt prod_name,prod_price FROM products WHERe prod_price=2.50;
# 使用相等和大于小于条件进行where筛选
SELECt prod_name,prod_price FROM products WHERe prod_name='fuses';
SELECt prod_name,prod_price FROM products WHERe prod_price<=10;
# 不匹配筛选,找出不符合条件的(<>和!=一样)
SELECt vend_id,prod_name FROM products WHERe vend_id<>1003;
SELECt vend_id,prod_name FROM products WHERe vend_id!=1003;
# 范围值检查,使用between
SELECt prod_name,prod_price FROM products WHERe prod_price BETWEEN 5 AND 10;
# 空值null检查,找出空的行或者非空的行
SELECt cust_id,cust_email FROM customers WHERe cust_email IS NULL;
SELECt cust_id,cust_email FROM customers WHERe cust_email IS NOT NULL;
第7章 数据过滤(WHERe高级应用)
7.1 使用AND操作符
键入下面命令并运行:
SELECt vend_id,prod_id,prod_name,prod_price FROM products WHERe vend_id=1003 AND prod_price<=10;
很显然,必须符合两个条件的结果才能显示出来,如下所示:
7.2 使用OR操作符
键入以下命令并执行:
SELECt prod_name,prod_price,vend_id FROM products WHERe vend_id=1003 OR vend_id=1002;
显然,只要符合两个条件之一的结果就会显示,结果如下:
7.3 运算次序
键入以下命令并执行:
SELECt prod_name,prod_price,vend_id FROM products WHERe vend_id=1003 OR vend_id=1002 AND prod_price<=10;
SELECt prod_name,prod_price,vend_id FROM products WHERe (vend_id=1003 OR vend_id=1002) AND prod_price<=10;
注意1:MySQL中AND运算符优先运算,所以为了保证计算正确,一般用()保证其运算次序。
第一行我们没有用(),因此先执行AND运算,再执行OR运算,结果如下(一般这个结果不符合我们的预期):
第二行用了(),获得了我们期待的结果:
7.4 使用IN操作符
键入下面代码并执行:
SELECt prod_name,prod_price,vend_id FROM products WHERe vend_id IN(1002,1003);
IN操作符的效果类似于OR,筛选出vend_id为1002和1003的结果,比OR用起来方便。结果如下:
7.5 使用NOT操作符
键入以下命令并运行:
SELECt prod_name,vend_id FROM products WHERe NOT vend_id=1002;
SELECt prod_name,prod_price,vend_id FROM products WHERe vend_id NOT IN(1002,1003);
NOT起到一个取反的功能,第一行我们就是取vend_id不等于1002的情况,结果如下:
NOT也可用在IN、BETWEEN、EXIST之前,起到取反作用。第二行脚本结果如下:
7.6 命令及注释汇总
USE crashcourse;
# 使用AND操作符
SELECt vend_id,prod_id,prod_name,prod_price FROM products WHERe vend_id=1003 AND prod_price<=10;
# 使用OR操作符
SELECt prod_name,prod_price,vend_id FROM products WHERe vend_id=1003 OR vend_id=1002;
# AND操作符优先运算,可能会导致错误,因此需要用圆括号
SELECt prod_name,prod_price,vend_id FROM products WHERe vend_id=1003 OR vend_id=1002 AND prod_price<=10;
SELECt prod_name,prod_price,vend_id FROM products WHERe (vend_id=1003 OR vend_id=1002) AND prod_price<=10;
# 使用IN操作符指定筛选范围,类似于OR
SELECt prod_name,prod_price,vend_id FROM products WHERe vend_id IN(1002,1003);
# 使用NOT操作符制定筛选范围,可用于IN,BETWEEN,EXISTS和一般位置
SELECt prod_name,vend_id FROM products WHERe NOT vend_id=1002;
SELECt prod_name,prod_price,vend_id FROM products WHERe vend_id NOT IN(1002,1003);
第8章 用通配符进行过滤(LIKE)
LIKE操作符用于指定通配符匹配
8.1 使用%通配符
键入以下代码并执行:
SELECt prod_name,prod_price,prod_id FROM products WHERe prod_name LIKE 'jet%';
%通配符代表任何字符出现任意字数,这里只要prod_name的结果中,前三个字符是jet即可。结果如下:
8.2 深入%通配符
键入以下代码并执行:
SELECt prod_name,prod_price,prod_id FROM products WHERe prod_name LIKE '%anvil%';
SELECt prod_name,prod_price,prod_id FROM products WHERe prod_name LIKE 's%e';
第一句意味着只要prod_name中存在“anvil”字符串,就符合匹配条件。结果如下:
第二句意味着只要prod_name的首尾字符分别是‘s’和‘e’,就符合匹配条件。结果如下:
8.3 使用_通配符
键入以下代码并执行:
SELECt prod_name,prod_price,prod_id FROM products WHERe prod_name LIKE '_ ton anvil';
下划线(_)通配符只匹配一个字符,条件比较苛刻。结果如下:
8.4 代码及注释总结
USE crashcourse;
# %通配符,表示任何字符出现任何次数
SELECt prod_name,prod_price,prod_id FROM products WHERe prod_name LIKE 'jet%';
# %通配符出现在两端,只要有这几个连续字符就满足条件
SELECt prod_name,prod_price,prod_id FROM products WHERe prod_name LIKE '%anvil%';
# %通配符出现在中间,意味着首尾字符是确定的
SELECt prod_name,prod_price,prod_id FROM products WHERe prod_name LIKE 's%e';
# _匹配单个字符,不多不少
SELECt prod_name,prod_price,prod_id FROM products WHERe prod_name LIKE '_ ton anvil';
第9章 用正则表达式进行搜索(REGEXP)
REGEXP用于声明匹配模式为正则表达式匹配。
注意1:正则表达式和通配符一般用于字符串匹配,数字的匹配意义不大
9.1 基本字符匹配
键入以下命令并执行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '1000' ORDER BY prod_name;
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '.000' ORDER BY prod_name;
第一句指令中,筛选出prod_name中含‘1000’这个字符串的结果,如下所示:
第二句指令中,使用特殊字符点(.),匹配任何一个存在的字符,即结果必须包含”X000“,结果如下所示:
这里看到,其实通配符匹配的LIKE也可以实现相同的功能,具体实现可以自己写一下。
算了,我写一下吧,大家可以运行自己对比一下:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name LIKE '%1000%' ORDER BY prod_name;
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name LIKE '%_000%' ORDER BY prod_name;
9.2 区分大小写(BINARY)
键入下面代码并运行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP BINARY 'jetpack .000';
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP BINARY 'JetPack .000';
这里加入了关键字BINARY,意味着大小写有效。第一句指令显然不能匹配到任何结果:
第二句指令我们大小写准确,因此可以得到结果:
9.3 进行OR匹配(|)
键入以下代码并运行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '1000|2000|3000';
我们使用或(|)符号表明筛选出符合三个条件中至少一个条件的结果,如下:
9.4 匹配几个字符之一([])
键入以下代码并运行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[123] ton';
这里的意味着‘1 ton’和‘2 ton’,还有‘3 ton’都可以匹配,与OR(|)的功能相似,结果如下:
用OR(|)实现的代码如下,大家自己尝试:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '1 ton|2 ton|3 ton';
9.5 否定字符(^)
键入以下代码并运行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[^123] ton';
使用^否定了‘1 ton’、‘2 ton’、‘3 ton’,只能匹配 ‘其他的字符+ton’,结果如下:
9.6 匹配范围([X-Y])
键入以下代码并运行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[1-3] ton';
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP 't[a-z]n';
显然,第一句的[1-3]意味着1,2,3这三个字符可以匹配,结果如下:
第二句指令的[a-z]意味着可以匹配26个小写英文字母,结果如下:
9.7 特殊字符匹配(\)
键入以下代码并运行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '\\.';
'.'是MySQL的保留的特殊字符(见9.1),我们此处需要用前导符‘\’来使用它,结果如下:
9.8 匹配字符类
键入以下代码并运行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[:digit:] ton';
这里的‘[:digit:]’是一个字符类,代表[0-9]的任意字符,因此匹配结果如下:
常用的字符类有:
[:alnum:]:任意字母或数字;
[:alpha:]:任意字母;
[:digit:]:任意数字;
[:lower:]:任意小写字母;
[:upper:]:任意大写字母;
[:print:]:任意可打印字符;
[:space:]:包括空格在内的任意空白字符;
[:xdigit:]:任意16进制数字
9.9 匹配多个实例
键入以下代码并运行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '\\([0-9] sticks?\\)';
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[:digit:]{4}';
第一句指令中的‘?‘意味着’stick‘后面的’s‘只能出现0次或者1次,结果如下:
第二句指令中的’{4}‘代表[:digit:]字符类中的字符至少出现4次,结果如下:
这里使用的匹配使用的是 重复元字符 ,用于限制某个匹配字符出现的次数。
重复元字符有:
*:0个或者多个匹配;
+:1个或者多个匹配;
?:0个或者1个匹配;
{n} :指定匹配n个
{n,}:不少于指定数目的匹配;
{n,m}: 匹配数目的范围,n-m之内都可以
9.10 使用定位符
键入以下代码并运行:
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '^[0-9\\.]';
这里使用的’^‘是文本开始位置的定位符,因此只匹配开始位置有[0-9]或者’.'的结果,如下所示:
常用的文本定位符有:
^:文本的开始;
$:文本的结尾;
[[:<:]]:词的开始;
[[:>:]]:词的结尾
注意1:SELECt不一定非要对表进行操作,我们可以自定义字符串进行表达式测试,例如下面的代码:
SELECT 'hello123' REGEXP '[0-9]';
SELECT 'hello' REGEXP '[0-9]';
第一行代码中的’hello123’符合匹配条件,那么就会返回true=1的结果:
第二行代码的’hello’显然不符合条件,所以返回false=0的结果:
9.11 命令及注释汇总
USE crashcourse;
# 基本字符匹配,用REGEXP; .符号可以匹配任意字符
SELECT prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '1000' ORDER BY prod_name;
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '.000' ORDER BY prod_name;
# 通配符方法实现
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name LIKE '%1000%' ORDER BY prod_name;
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name LIKE '%_000%' ORDER BY prod_name;
# 区分大小写的匹配,用BINARY
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP BINARY 'jetpack .000';
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP BINARY 'JetPack .000';
# 进行OR匹配,使用|符号
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '1000|2000|3000';
# 匹配几个字符之一,OR的进阶,用[]
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[123] ton';
# 使用|实现上面的操作
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '1 ton|2 ton|3 ton';
# 否定字符,取相反的,用^号(匹配除123的任何字符)
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[^123] ton';
# 匹配范围,数字或者字符
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[1-3] ton';
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP 't[a-z]n';
# 匹配特殊字符,列如 .符号,使用\\作为前导
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '\\.';
SELECt vend_name FROM vendors WHERe vend_name REGEXP '\\.' ORDER BY vend_name;
# 匹配字符类。常用的如:[:alnum:]:任意字母或数字;[:alpha:]:任意字母;[:digit:]:任意数字;[:lower:]:任意小写字母;
# [:upper:]:任意大写字母;[:print:]:任意可打印字符;[:space:]:包括空格在内的任意空白字符;[:xdigit:]:任意16进制数字
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[:digit:] ton';
# 匹配多个实例 * :0个或者多个匹配;+:1个或者多个匹配; ?:0个或者1个匹配;{n} 指定匹配n个
# {n,}: 不少于指定数目的匹配; {n,m}: 匹配数目的范围,n-m之内都可以
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '\\([0-9] sticks?\\)';
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '[:digit:]{4}';
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '0{3,}';
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP 'car{1,2}';
SELECt prod_name FROM products;
# 使用定位符 ^:文本的开始;$:文本的结尾;[[:<:]]:词的开始;[[:>:]]:词的结尾
SELECt prod_name,prod_price,vend_id FROM products WHERe prod_name REGEXP '^[0-9\\.]';
SELECt 'hello123' REGEXP '[0-9]';
SELECT 'hello' REGEXP '[0-9]';
第10章 创建计算字段
本章的任务是对查询结果执行拼接、算术运算等操作。
10.1 拼接字段(Concat())
键入以下命令并执行:
SELECT concat(vend_name,'(',vend_country,')') FROM vendors ORDER BY vend_name;
这里使用Concat()函数对vend_name、(、vend_country、)四个字符串进行了拼接,结果如下:
10.2 去除空格(Trim()等)
键入以下命令并执行:
SELECt concat(RTrim(vend_name),'(',RTrim(vend_country),')') FROM vendors ORDER BY vend_name;
这里仍旧对10.1的字符进行拼接,只不过使用了RTrim函数去除了字符串中右边的空格,结果如下:
除空格的函数有:
Ltrim():去除左边空格。
Rtrim():去除右边空格。
Trim():去除左右两边的空格。
10.3 使用AS关键字
键入以下命令并运行:
SELECt concat(vend_name,'(',vend_country,')') AS vend_title FROM vendors ORDER BY vend_name;
这里使用了AS关键字,将拼接的结果命名为了vend_title,结果如下:
这里的AS不是似乎不是很有用,但是请记住它,后续它会简化我们的书写。
10.4 执行算术运算(±*/)
键入以下命令并运行:
SELECt prod_id,quantity,item_price,quantity*item_price AS expanded_price FROM orderitems WHERe order_num=20005;
这里我们使用了乘号(*),计算订单号为20005的商品的数量与价格的乘积,并将其命名为expanded_price,结果如下:
10.5 命令及注释汇总
USE crashcourse;
# 拼接字段,使用Concat()函数
SELECt concat(vend_name,'(',vend_country,')') FROM vendors ORDER BY vend_name;
# 去掉空格,使用Ltrim,Rtrim和trim函数
SELECt concat(RTrim(vend_name),'(',RTrim(vend_country),')') FROM vendors ORDER BY vend_name;
SELECt Trim(' hello 123 ');
# 别名,使用AS关键字
SELECT concat(vend_name,'(',vend_country,')') AS vend_title FROM vendors ORDER BY vend_name;
# 执行算术运算,+-*/
SELECt prod_id,quantity,item_price,quantity*item_price AS expanded_price FROM orderitems WHERe order_num=20005;
第11章 使用数据处理函数
MySQL支持系统提供的函数,私人不可定义函数。
11.1 文本处理函数
键入以下代码并运行:
SELECt vend_name,upper(vend_name) AS vend_name_upcase FROM vendors ORDER BY vend_name;
SELECt cust_name,cust_contact,Left(cust_name,2) AS l_name FROM customers;
SELECt cust_name,cust_contact FROM customers WHERe locate('se',cust_name,4);
SELECt cust_name,cust_contact,locate('se',cust_name,5) AS se_location FROM customers;
SELECt cust_name,cust_contact FROM customers WHERe soundex(cust_contact)=soundex('Y. Lie');
第一句指令使用了upper()函数,将vend_name的字母全部变为大写字母,结果如下:
第二句指令使用left()函数,返回cust_name的左边两个字符,结果如下:
第三句使用了locate(),它在WHERe之后,用于筛选出从第4个字符开始,存在字符串’se‘的cust_name,结果如下:
第三句仍旧使用了lacate(),它在SELECt之后,用于定位从第5个字符开始,’se‘在cust_name中的位置,结果如下:
第四句使用了soundex(),这个函数把字母(词)转成发音,用于模糊查询,效果如下:
常用的文本处理函数有:
Left():返回串左边的n个字符;
Length():返回串的长度;
Locate():找出串的一个子串;
Lower():转为小写字母;
LTrim():去掉左边空格;
RTrim():去掉右边空格;
Right():返回串右边的字符;
Soundex():返回串的SOUNDEX值(就是发音);
SubString():返回子串的字符;
Upper():转为大写字母;
11.2 日期和时间处理函数
键入以下代码并运行:
SELECT cust_id,order_num,order_date FROM orders WHERe Date(order_date)='2005-09-01';
SELECt cust_id,order_num,order_date FROM orders WHERe Date(order_date) BETWEEN '2005-09-01' AND '2005-09-30';
SELECt cust_id,order_num,order_date,adddate(order_date,7) AS date_add_week FROM orders WHERe Date(order_date) BETWEEN '2005-09-01' AND '2005-09-30';
SELECt cust_id,order_num,order_date,curdate(),datediff(curDate(),order_date) AS date_diff FROM orders;
SELECt cust_id,order_num,order_date,dayofweek(order_date) AS week_of_date FROM orders;
SELECt curdate(),curtime(),now();
第一句指令,使用了Date()函数,将order_date转成标准日期,筛选出2005年9月1号订购的订单,结果如下:
第二句指令,使用Date()和BETWEEN AND,筛选出一段时间内的订单,结果如下:
第三句指令,使用了adddate()函数,将返回的order_date增加一个周(7天),结果如下:
第四句指令,使用了datediff()函数和curdate()函数,计算出下单日期与当前日期的差值(天数),结果如下:
第五句指令,使用了dayofweek()函数,返回订单当天是一个周的周几,结果如下:
第六句指令,使用curdate(),curtime(),now(),分别返回当前日期、时间、日期+时间,结果如下:
常用日期处理函数:
AddDate(): 增加一个日期(天);
AddTime():增加时和分;
CurDate():返回当前日期;
CurTime():返回当前时间;
Now():返回当前日期和时间
Date():返回日期时间的日期部分;
Time():返回日期时间的时间部分;
DateDiff():计算两个日期之差;
Data_Add():高度灵活的日期运算函数,没用过,不懂;
Data_Format():返回一个格式化的日期或者时间串;
DayOfWeek():返回一个日期是星期几;
hour():返回小时部分;
minute():返回分钟部分;
Second():返回秒;
Day():返回一个日期的天数部分;
month():返回月份;
Year():返回年份
注意1:年份一般推荐用四位数字表示,如2020。但是MySQL仍可处理2位年份,如00-69认为是2000-2069,70-99认为是1970-1999。
11.3 数值处理函数
键入以下代码并运行:
SELECT prod_name,prod_price,Mod(prod_price,2) AS price_Mod2 FROM products;
使用了Mod()函数,对prod_price取2的余数,得到结果如下:
常用的数值处理函数有:Abs();cos();Exp();Mod();Pi();Rand();Sin();Sqrt();Tan();
函数的名字取得太清楚了,不过多解释。
11.4 命令及注释汇总
USE crashcourse;
# 文本处理函数 Left:返回串左边的n个字符;Length:返回串的长度;Locate:找出串的一个子串;Lower:转为小写字母;
# LTrim:去掉左边空格; Right:返回串右边的字符;RTrim;Soundex:返回串的SOUNDEX值(就是发音);SubString:返回子串的字符;Upper
SELECt vend_name,upper(vend_name) AS vend_name_upcase FROM vendors ORDER BY vend_name;
SELECt cust_name,cust_contact FROM customers WHERe cust_contact='Y. Lie';
SELECt cust_name,cust_contact FROM customers WHERe soundex(cust_contact)=soundex('Y. Lie');
SELECt cust_name,cust_contact,Length(cust_name) AS name_length FROM customers;
SELECt cust_name,cust_contact,Left(cust_name,2) AS l_name FROM customers;
SELECt cust_name,cust_contact FROM customers WHERe locate('se',cust_name,4);
SELECt cust_name,cust_contact,locate('se',cust_name,5) AS se_location FROM customers;
# 日期和时间处理函数 AddDate: 增加一个日期(天、周);AddTime:增加时和分;CurDate:返回当前日期;CurTime:返回当前时间;
# Date:返回日期时间的日期部分;DateDiff:计算两个日期之差;Data_Add:高度灵活的日期运算函数;Data_Format:返回一个格式化的日期或者时间串;
# Day:返回一个日期的天数部分;DayOfWeek:返回星期几;hour:返回小时部分;minute:返回分钟部分;month:返回月份;Now:返回当前日期和时间
# Second:返回秒;Time;Year;
SELECt cust_id,order_num,order_date FROM orders WHERe Date(order_date)='2005-09-01';
SELECt cust_id,order_num,order_date FROM orders WHERe Date(order_date) BETWEEN '2005-09-01' AND '2005-09-30';
SELECt cust_id,order_num,order_date,adddate(order_date,7) AS date_add_week FROM orders WHERe Date(order_date) BETWEEN '2005-09-01' AND '2005-09-30';
SELECt cust_id,order_num,curdate() AS cur_data FROM orders;
SELECt cust_id,order_num,order_date,curdate(),datediff(curDate(),order_date) AS date_diff FROM orders;
SELECt cust_id,order_num,order_date,dayofweek(order_date) AS week_of_date FROM orders;
SELECt curdate(),curtime(),now();
# 数值处理函数 Abs();cos();Exp();Mod();Pi();Rand();Sin();Sqrt();Tan();
SELECT prod_name,prod_price,Mod(prod_price,2) AS price_Mod2 FROM products;
SELECt prod_name,prod_price,Pi()*prod_price AS price_pi FROM products;
SELECt prod_name,prod_price,Exp(prod_price) AS price_exp FROM products;
SELECt rand();
第12章 汇总/聚集数据
汇总数据是对数据进行总体的评估,无需返回表内数据。
注意1:汇总的值一般只有1个,因此一条命令不能既汇总又返回一般的表中数据。
12.1平均值函数(AVG())
键入以下代码并运行:
SELECT AVG(prod_price) AS price_avg FROM products WHERe vend_id=1003;
这里使用AVG函数计算vend_id为1003的商品价格的平均值,如下:
12.2 行数总结函数(COUNT())
键入以下代码并运行:
SELECt COUNT(*) AS num_cust FROM customers;
SELECt COUNT(cust_email) FROM customers;
第一行使用了COUNT(*),用于数出表格的总行数,结果如下:
第二行使用COUNT()函数数出cust_email的行数,不包括NULL值,结果如下:
12.3 最大值函数(MAX())
键入以下代码并执行:
SELECt Max(prod_price) AS max_price FROM products;
SELECt Max(prod_name) AS max_name FROM products;
第一行命令,我们将MAX()函数应用于prod_price,它是数值型变量,找到最大值:
第二行命令,我们将MAX()函数应用于prod_name,它是char型变量,返回的是最后一行的prod_name:
12.4 最小值函数(MIN())
键入以下代码并执行:
SELECt Min(prod_price) AS min_price FROM products;
SELECt Min(prod_name) AS min_name FROM products;
第一行命令的MIN()应用于数值型数据,返回最小值:
第二行命令的MIN()应用于CHAR型数据,返回第一行:
12.5 求和函数(SUM())
键入以下代码并执行:
SELECt SUM(quantity*item_price) AS total_price FROM orderitems;
本行命令首先将数量和价格相乘,得到的新列再使用SUM()函数求和,得到所有商品的总价格,结果如下:
12.6 排除重复元素(DISTINCT)
键入以下代码并执行:
SELECt AVG(prod_price) AS avg_price FROM products WHERe vend_id=1003;
SELECt AVG(DISTINCT prod_price) AS avg_price FROM products WHERe vend_id=1003;
第一句命令没有使用DISTINCT排除重复元素,因此计算了所有vend_id为1003的商品价格均值,结果如下:
第二句使用了DISTINCT排除了重复的价格,结果如下:
12.7 组合聚集函数
键入以下代码并执行:
SELECt COUNT(*) AS num_items,MIN(prod_price) AS price_min,Max(prod_price) AS price_max,AVG(prod_price) AS price_avg FROM products;
这里我们使用了多个聚集函数,因为返回值都是一个,所以聚集函数是可以一起使用的。结果如下:
12.8 命令及注释汇总
USE crashcourse;
# 平均值函数AVG(),计算列的均值(NULL数据不计入)
SELECt AVG(prod_price) AS price_avg FROM products WHERe vend_id=1003;
# 数出表的行数,或者单个列的行数(不计null),使用COUNT()函数
SELECt COUNT(*) AS num_cust FROM customers;
SELECt COUNT(cust_email) FROM customers;
# 指定列的最大值max();应用于文本时,返回最后一行数据
SELECt Max(prod_price) AS max_price FROM products;
SELECt Max(prod_name) AS max_name FROM products;
# 指定列的最小值min();应用于文本时,返回第一行数据
SELECt Min(prod_price) AS min_price FROM products;
SELECt Min(prod_name) AS min_name FROM products;
# 指定列的和SUM()
SELECt SUM(quantity) AS sum_order FROM orderitems;
SELECt SUM(quantity*item_price) AS total_price FROM orderitems;
# 使用DISTINCT关键字排除重复元素
SELECt AVG(prod_price) AS avg_price FROM products WHERe vend_id=1003;
SELECt AVG(DISTINCT prod_price) AS avg_price FROM products WHERe vend_id=1003;
SELECt COUNT(DISTINCT prod_price) AS count_price FROM products;
# 组合聚集函数
SELECt COUNT(*) AS num_items,MIN(prod_price) AS price_min,Max(prod_price) AS price_max,AVG(prod_price) AS price_avg FROM products;
第13章 分组数据(GROUP BY+HAVINg)
GROUP BY是对数据分组的关键字
HAVINg是对分组数据过滤的关键字,类似于WHERe。
注意1:分组一般和聚集函数一起使用,不然分组的意义不大
13.1 创建分组
键入以下命令并执行:
SELECt vend_id,COUNT(*) AS num_prods FROM products GROUP BY vend_id;
GROUP BY指定我们按照vend_id对结果分组。这里COUNT(*)对每组数据进行汇总,得到每组的行数:
13.2 汇总级别(WITH ROLLUP)
键入以下命令并执行:
SELECt vend_id,COUNT(*) AS sum_prods FROM products GROUP BY vend_id WITH ROLLUP;
最后添加的WITH ROLLUP关键字可以帮助我们得到每个分组及其汇总级别(我也不清楚啥意思),如下:
13.3 过滤数据(HAVINg)
键入以下代码并执行:
SELECt vend_id,COUNT(*) AS num_prods FROM products WHERe prod_price>=10 GROUP BY vend_id HAVINg COUNT(*)>=2;
分组之前,WHERe子句筛选出prod_price大于等于10的结果,分组之后,HAVINg过滤出分组行数大于等于2的结果:
13.4 排序数据(ORDER BY)
键入以下代码并执行:
SELECt order_num,SUM(quantity*item_price) AS ordertotal FROM orderitems GROUP BY order_num HAVINg SUM(quantity*item_price)>=50;
SELECt order_num,SUM(quantity*item_price) AS ordertotal FROM orderitems GROUP BY order_num HAVINg SUM(quantity*item_price)>=50 ORDER BY ordertotal;
GROUP BY有时候会对数据进行排序,但是我们为了保证数据的顺序,最好还是手动ORDER BY进行排序。
13.5 命令及注释总结
USE crashcourse;
# 分组的语句使用GROUP BY
SELECt vend_id,COUNT(*) AS num_prods FROM products GROUP BY vend_id;
# 使用WHTH ROLLUP关键字,得到每个分组及分组的汇总级别值
SELECt vend_id,COUNT(*) AS sum_prods FROM products GROUP BY vend_id WITH ROLLUP;
# 使用HAVINg过滤分组(WHERe是在分组之前进行的过滤)
SELECt cust_id,COUNT(*) AS orders FROM orders GROUP BY cust_id HAVINg COUNT(*)>=2;
SELECt vend_id,COUNT(*) AS num_prods FROM products WHERe prod_price>=10 GROUP BY vend_id HAVINg COUNT(*)>=2;
# GROUP BY并不总是排序数据,仍需搭配ORDER BY
SELECt order_num,SUM(quantity*item_price) AS ordertotal FROM orderitems GROUP BY order_num HAVINg SUM(quantity*item_price)>=50;
SELECt order_num,SUM(quantity*item_price) AS ordertotal FROM orderitems GROUP BY order_num HAVINg SUM(quantity*item_price)>=50 ORDER BY ordertotal;
第14章 使用子查询(IN)
子查询即嵌套在其他查询中的查询,允许多个SELECt语句。
14.1 利用子查询进行过滤
键入以下代码并执行:
SELECT cust_name,cust_contact FROM customers WHERe cust_id IN(SELECt cust_id FROM orders WHERe order_num IN(SELECt order_num FROM orderitems WHERe prod_id='TNT2'));
这里我们使用IN关键字嵌套了两层子查询。
第一层为:SELECt order_num FROM orderitems WHERe prod_id='TNT2'
,选择prod_id为’TNT2‘的订单号order_num;
第二层为:SELECt cust_id FROM orders WHERe order_num IN()
,将第一层查询的结果,即订单号order_num作为条件,查出了顾客编号cust_id;
主查询将第二层查询的结果,即顾客编号cust_id作为条件,查出了顾客名字和联系方式。
整个查询的最终效果是,找到了订购TNT2商品的顾客姓名与联系方式:
这三个查询并不在一个表中进行,实现了跨表的查询,而表与表之间存在关系是关系型数据库最大的特点。
14.2 使用计算字段建立子查询
键入以下代码并运行:
SELECt cust_name,cust_state,cust_id,(SELECT COUNT(*) FROM orders WHERe orders.cust_id=customers.cust_id) AS orders FROM customers ORDER BY cust_name;
这里没有IN关键字。子查询的作用是在把orders表的顾客的订单数汇总,传递给主查询进行显示:
14.3 命令及注释汇总
USE crashcourse;
# 利用子查询进行过滤,使用IN关键字
SELECt cust_name,cust_contact FROM customers WHERe cust_id IN(SELECt cust_id FROM orders WHERe order_num IN(SELECt order_num FROM orderitems WHERe prod_id='TNT2'));
# 使用计算字段建立子查询
SELECt cust_name,cust_state,cust_id,(SELECT COUNT(*) FROM orders WHERe orders.cust_id=customers.cust_id) AS orders FROM customers ORDER BY cust_name;
第15章 联结表
联结是跨表查询的基础,关系数据库的特点决定了它需要实现跨表查询。
15.1 关系表(关系数据库)的理解
理解关系表的最好方式是举例:
假设有一张数据表,每种类别物品占一行,存储商品的描述和价格以及供应商编号。
一个供应商可以生产多种物品,那么此时如何存储供应商的名称、地址、联系方法等信息呢?
将供应商信息与商品信息分开存储的理由如下:
-
同一供应商生产的每个产品供应商信息都是相同的,一个产品存储一次供应商信息显然浪费存储的时间和空间;
-
如果供应商信息改变,若我们分开存储,只需改动一次即可;
-
若不分开存储,很难保证每次输入该数据的方式都相同,不一致的数据在报表中很难用。
可以看出,相同数据多次出现不是一件有益的事,这就决定了我们需要设计关系数据库。
关系表的设计就是要把信息分解成多个表,一类数据一个表,各表通过常用值(即关系)相关联。
上述常用值关联为主键-外键对应关系,一个表的主键对应另一个表的外键,由此得到两者的关系。
如,输入以下代码并执行:
SELECt cust_id FROM customers;
SELECt order_num,cust_id FROM orders;
第一行代码从顾客customers表中选择顾客编号cust_id列,这个是主键,在customers表中是唯一的:
第二行代码从订单orders表中选择订单编号order_num和顾客编号cust_id列,cust_id在这个表中是外键,并不是唯一的:
15.2 创建联结
键入以下代码并运行:
SELECt vend_name,prod_name,prod_price FROM vendors,products WHERe vendors.vend_id=products.vend_id ORDER BY vend_name,prod_name;
创建联结的操作很简单,FROM 后面跟上需要联结的表,WHERe后面将主键-外键对应,结果如下:
联结的作用是将关系表的关系对应展开。这里就是找出了供应商名称vend_name及其提供的商品名称prod_name和价格prod_price.
15.3 笛卡尔积(没意义)
键入以下代码并运行:
SELECt vend_name,prod_name,prod_price FROM vendors,products ORDER BY vend_name,prod_name;
这里没有用WHERe限定主键与外键,生成的结果是每个供应商对应了每一个产品,并不是我们需要的结果:
这告诉我们,使用联结时必须要用WHERe子句对结果进行限定。
15.4 内部联结
键入以下代码并运行:
SELECt vend_name,prod_name,prod_price FROM vendors INNER JOIN products ON vendors.vend_id=products.vend_id ORDER BY vend_name,prod_name;
这里使用了INNER JOIN ON关键字代替了WHERe子句,和WHERe效果是一致的,这是联结的标准语法。结果如下:
15.5 联结多个表
键入以下代码并运行:
SELECt order_num,orderitems.prod_id,products.vend_id,vend_name,vend_address FROM orderitems,products,vendors WHERe products.vend_id=vendors.vend_id AND orderitems.prod_id=products.prod_id AND order_num=20005;
本句我们联结了3个表,首先从ordersitems中找到订单号为20005的订单商品编号prod_id,然后联结到products表,找到商品编号prod_id对应的供应商编号vend_id,最后通过联结到vendors表找到供应商的名字vend_name和地址vend_address。结果如下:
显然,也是将三个表的关系展开。
15.6 替代子查询
键入以下命令并执行:
SELECt cust_name,cust_contact FROM customers WHERe cust_id IN(SELECt cust_id FROM orders WHERe order_num IN(SELECt order_num FROM orderitems WHERe prod_id='TNT2'));
SELECt cust_name,cust_contact FROM customers,orders,orderitems WHERe prod_id='TNT2' AND orderitems.order_num=orders.order_num AND customers.cust_id=orders.cust_id;
第一句命令是我们在14.1节使用子查询实现的,这里我们用联结更加简单的实现了同样的查询,联结是更易理解的:
15.7 命令及注释汇总
USE crashcourse;
# 关系表演示
SELECt cust_id FROM customers;
SELECt order_num,cust_id FROM orders;
# 创建联结,只需要同时FROM 两个表且用WHERe限定主键和外键即可
SELECt vend_name,prod_name,prod_price FROM vendors,products WHERe vendors.vend_id=products.vend_id ORDER BY vend_name,prod_name;
# 笛卡尔积,没有使用where子句限定主键和外键,数据没有太大意义
SELECt vend_name,prod_name,prod_price FROM vendors,products ORDER BY vend_name,prod_name;
# 内部连接,使用INNER JOIN ON子句,效果和where一致
SELECt vend_name,prod_name,prod_price FROM vendors INNER JOIN products ON vendors.vend_id=products.vend_id ORDER BY vend_name,prod_name;
# 联结多个表
SELECt order_num,orderitems.prod_id,products.vend_id,vend_name,vend_address FROM orderitems,products,vendors WHERe products.vend_id=vendors.vend_id AND orderitems.prod_id=products.prod_id AND order_num=20005;
# 替代子查询的例子
SELECt cust_name,cust_contact FROM customers WHERe cust_id IN(SELECt cust_id FROM orders WHERe order_num IN(SELECt order_num FROM orderitems WHERe prod_id='TNT2'));
SELECt cust_name,cust_contact FROM customers,orders,orderitems WHERe prod_id='TNT2' AND orderitems.order_num=orders.order_num AND customers.cust_id=orders.cust_id;
第16章 创建高级联结
介绍表别名、自联结、自然联结、外部联结、带聚集函数的联结。
16.1 使用表别名
键入以下代码并执行:
SELECt prod_id,oi.order_num,o.cust_id,cust_name,cust_contact FROM customers AS c,orders AS o,orderitems AS oi WHERe c.cust_id=o.cust_id AND oi.order_num=o.order_num AND prod_id='TNT2';
这里创建了3个表的联结,找到了订购了产品编号为‘TNT2’的顾客的名字和联系方式。我们使用AS分别给3个表赋予了别名,简化了书写:
16.2 自联结
键入以下代码并执行:
SELECt vend_id,prod_id,prod_name FROM products WHERe vend_id=(SELECt vend_id FROM products WHERe prod_id='DTNTR');
SELECt p1.vend_id,p1.prod_id,p1.prod_name FROM products AS p1,products AS p2 WHERe p1.vend_id=p2.vend_id AND p2.prod_id='DTNTR';
第一句使用子查询首先找到提供商品编号prod_id为‘DINTR’的供应商编号vend_id,然后主查询查找到这个供应商提供的商品编号和名称。
第二句使用联结实现了同样的功能,可以看到,联结的两个表同为products(自联结),这时为了区分两个表,对两者都赋予了别名。
16.3 自然联结
键入以下代码并执行:
SELECt c.*,o.order_num,o.order_date,oi.prod_id,oi.quantity,oi.item_price FROM customers AS c,orders AS o,orderitems AS oi WHERe c.cust_id=o.cust_id AND oi.order_num=o.order_num AND prod_id='FB';
在联结中,至少有一个列不止出现在一个表中(如表的主键和外键),我们使用自然联结来避免出现重复列,结果如下:
自然联结实现方法:通配符*只对第一个表使用,所有其他的列明确列出,就保证没有重复列。
我们建立的内部联结都是自然联结,因此这个地方讲的意义不大,只需要知道而不用关注自然联结即可。
16.4 外部联结
键入以下代码并执行
SELECt orders.order_num,customers.cust_id,cust_name FROM customers INNER JOIN orders ON customers.cust_id=orders.cust_id;
SELECt orders.order_num,customers.cust_id,cust_name FROM customers LEFT OUTER JOIN orders ON customers.cust_id=orders.cust_id;
SELECt orders.order_num,customers.cust_id,cust_name FROM customers RIGHT OUTER JOIN orders ON customers.cust_id=orders.cust_id;
第一句命令使用内部联结,找出订单号order_num对应的顾客名字cust_name,结果如下:
外部联结根据基本表,列出基本表的所有行,其中就包括没有关联行的那些行。
第二句命令使用了LEFT OUTER JOIN ON,即左外部联结,以customers表为基本表,下面为结果:
cust_id为10002的顾客没有任何订单,即在orders表中没有关联行,这里依旧列出了其信息。
第三句命令使用了右外部联结,以orders表为基本表,下面为结果:
16.5 带聚集函数联结
键入以下代码并运行:
SELECt customers.cust_name,customers.cust_id,COUNT(orders.order_num) AS num_orders FROM customers INNER JOIN orders ON customers.cust_id=orders.cust_id GROUP BY customers.cust_id;
SELECt customers.cust_name,customers.cust_id,COUNT(orders.order_num) AS num_orders FROM customers LEFT OUTER JOIN orders ON customers.cust_id=orders.cust_id GROUP BY customers.cust_id;
这里我们使用联结和分组、聚集函数COUNT()实现了这样的功能:数出每个顾客下的的订单总数。
第一句命令,我们使用内部查询,只能输出有订单的顾客订单信息:
第二句命令,我们使用外部查询,给出了所有顾客的订单信息:
16.6 命令及注释汇总
USE crashcourse;
# 使用表别名创建联结
SELECt prod_id,oi.order_num,o.cust_id,cust_name,cust_contact FROM customers AS c,orders AS o,orderitems AS oi WHERe c.cust_id=o.cust_id AND oi.order_num=o.order_num AND prod_id='TNT2';
# 自联结,如果要使用相同的表两次时需要用到.(此时会用到表别名)
SELECt vend_id,prod_id,prod_name FROM products WHERe vend_id=(SELECt vend_id FROM products WHERe prod_id='DTNTR');
SELECt p1.vend_id,p1.prod_id,p1.prod_name FROM products AS p1,products AS p2 WHERe p1.vend_id=p2.vend_id AND p2.prod_id='DTNTR';
# 自然联结:每个列只出现一次,这个只能自己手工实现!!!(通过第一个表使用通配符,其余表使用明确的列名来实现)
SELECt c.*,o.order_num,o.order_date,oi.prod_id,oi.quantity,oi.item_price FROM customers AS c,orders AS o,orderitems AS oi WHERe c.cust_id=o.cust_id AND oi.order_num=o.order_num AND prod_id='FB';
# 建立外部联结,使用RIGHT(LEFT) OUTER JOIN ON 来实现。下面的结果可以看出来与内部联结的区别(外部联结会列出无关行的结果,根据LEFT和RIGHT确定基本表)
SELECt orders.order_num,customers.cust_id,cust_name FROM customers INNER JOIN orders ON customers.cust_id=orders.cust_id;
SELECt orders.order_num,customers.cust_id,cust_name FROM customers LEFT OUTER JOIN orders ON customers.cust_id=orders.cust_id;
SELECt orders.order_num,customers.cust_id,cust_name FROM customers RIGHT OUTER JOIN orders ON customers.cust_id=orders.cust_id;
# 带聚集函数的联结
SELECt customers.cust_name,customers.cust_id,COUNT(orders.order_num) AS num_orders FROM customers INNER JOIN orders ON customers.cust_id=orders.cust_id GROUP BY customers.cust_id;
SELECt customers.cust_name,customers.cust_id,COUNT(orders.order_num) AS num_orders FROM customers LEFT OUTER JOIN orders ON customers.cust_id=orders.cust_id GROUP BY customers.cust_id;
第17章 组合查询(UNIOn)
UNIOn用于连接多个SELECt查询语句,将多个查询组合成一个结果集。
注意1:UNIOn完全可以由WHERe代替。
17.1 使用UNIOn
键入以下命令并运行:
SELECT vend_id,prod_id,prod_price FROM products WHERe prod_price<=5;
SELECt vend_id,prod_id,prod_price FROM products WHERe vend_id IN (1001,1002);
SELECt vend_id,prod_id,prod_price FROM products WHERe prod_price<=5 UNIOn SELECt vend_id,prod_id,prod_price FROM products WHERe vend_id IN (1001,1002);
SELECt vend_id,prod_id,prod_price FROM products WHERe prod_price<=5 OR vend_id IN (1001,1002);
第一句命令我们筛选出prod_price<=5的结果,如下:
第二句命令筛选出vend_id=(1001,1002)的结果,如下:
第三句命令使用UNIOn关键字连接了两个SELECt语句,整合了结果并去除了重复结果:
第四句命令使用WHERe实现了UNIOn同样的功能:
17.2 UNIOn使用规则
(1)连接多个SELECT;
(2)每个查询包含相同的列、表达式、聚集函数;
(3)列类型不必完全相同,但是必须要兼容,可以隐式转换。
17.2 包含重复行(UNIOn ALL)
键入以下代码并执行:
SELECT vend_id,prod_id,prod_price FROM products WHERe prod_price<=5 UNIOn ALL SELECt vend_id,prod_id,prod_price FROM products WHERe vend_id IN(1001,1002);
这里我们把UNIOn换成了UNIOn ALL,达到了输出所有查询结果的效果:
17.3 排序查询结果
键入以下代码并执行:
SELECt vend_id,prod_id,prod_price FROM products WHERe prod_price<=5 UNIOn ALL SELECt vend_id,prod_id,prod_price FROM products WHERe vend_id IN(1001,1002) ORDER BY vend_id,prod_price;
对组和查询进行排序,ORDER BY只能放到最后一个查询后面,排序所有结果:
17.4 命令及注释汇总
USE crashcourse;
# 使用UNIOn(会自动去除重复行),可以实际上用WHERe代替。
SELECt vend_id,prod_id,prod_price FROM products WHERe prod_price<=5;
SELECt vend_id,prod_id,prod_price FROM products WHERe vend_id IN (1001,1002);
SELECt vend_id,prod_id,prod_price FROM products WHERe prod_price<=5 UNIOn SELECt vend_id,prod_id,prod_price FROM products WHERe vend_id IN (1001,1002);
SELECt vend_id,prod_id,prod_price FROM products WHERe prod_price<=5 OR vend_id IN (1001,1002);
# UNIOn规则:(1)连接多个SELECt(2)每个查询包含相同的列、表达式、聚集函数(3)列类型不必完全相同,但是必须要兼容,可以隐式转换。
# 包含重复行的关键字 UNIOn ALL
SELECT vend_id,prod_id,prod_price FROM products WHERe prod_price<=5 UNIOn ALL SELECt vend_id,prod_id,prod_price FROM products WHERe vend_id IN(1001,1002);
# 排序组合查询结果,只能放在最后一条SELECt后面,排序所有结果。
SELECT vend_id,prod_id,prod_price FROM products WHERe prod_price<=5 UNIOn ALL SELECt vend_id,prod_id,prod_price FROM products WHERe vend_id IN(1001,1002) ORDER BY vend_id,prod_price;