如果你喜欢,可以关注我的公众号
第一章:SQL语句及其种类
SQL用关键字,表明,列名等组合而成的一条语句来描述操作的内容,关键字是指那些含义或使用方法事先已经定义好的英语单词,例如"对表进行查询"或者"参考这张表"等包含各种意义的关键字
-
DDL(Data Defintion Language,数据定义语言)
DDL(数据库定义语言)用来创建或者删除存储数据使用的数据库以及数据库中的表等对象.DDL包含以下几种指令
- CREATE:创建数据库和表对象
- DROP:删除数据库和表对象
- ALTER:修改数据库和表对象的结构
-
DML(Data Manipulation Language,数据操作语言)
用来查询或变更表中的记录
- SELECT:查询表中的数据
- INSERT:向表中插入数据
- UPDATE:修改表中的数据
- DELETE:删除表中的数据
-
DCL(Data Control Language,数据控制语言)
用来确认或者取消对数据库中的数据进行的变更.除此之外,还可以用用户是否有权限操作数据库进行设定
- COMMIT: 确认对数据库中的数据进行的变更
- ROLLBACK: 取消对数据库中的数据进行的表更
- GRANT: 赋予用户操作的权限
- REVOKE: 取消用户的操作权限
-
实际应用中我们其实95%的时候都是用到DML
SQL书写基本规则
-
sql语句要以分号(;)结束
-
sql语句不区分大小写
sql语句不区分大小写,例如SELECT或者select,解释都是一样的,但是为了理解起来更容易,还是建议关键字都大写 -
常数的书写方式是固定的
常见的常数有字符串,日期或者数字等等
- sql语句中有字符串的时候,需要像'abc'这样,使用单引号括起来
- sql语句中含有日期的时候也是以单引号括起来
- sql语句中需要书写数字的时候,不需要使用任何记号标识,直接写成1000数字即可
-
单词需要用空格或者换行来分离
表的创建
商品编号 | 商品名称 | 商品分类 | 销售单价 | 进货单价 | 登记日期 |
---|---|---|---|---|---|
0001 | T恤衫 | 衣服 | 1000 | 500 | 2018-12-24 |
0002 | 打孔器 | 办公用品 | 500 | 320 | 2018-12-12 |
0003 | 运动T恤 | 衣服 | 4000 | 2800 | |
0004 | 菜刀 | 厨房用具 | 3000 | 2800 | 2018-12-24 |
0005 | 高压锅 | 厨房用具 | 6800 | 5000 | 2018-06-24 |
0006 | 叉子 | 厨房用具 | 500 | 2018-12-24 | |
0007 | 擦菜板 | 厨房用具 | 880 | 790 | 2018-04-01 |
0008 | 圆珠笔 | 办公用品 | 100 | 2018-10-01 |
- 接下来我们就创建上面的这张表,
-
数据库的创建(CREAT DATABASES 语句)
- 语法
CREATE DATABASES <数据库名称> ;
创建数据库shop的语句
CREATE DATABASES shop ;
-
表的创建(CREAT TABLE 语句)
- 语法
CREATE DATABASES <表名>
(<列名1> <数据类型> <该列所需的约束>,
<列名2> <数据类型> <该列所需的约束>,
<列名3> <数据类型> <该列所需的约束>,
<列名4> <数据类型> <该列所需的约束> ...
<该表的约束1>,<该表的约束2>...);
创建表Shopin的语句
CREATE TABLE Shopin (
shopin_id CHAR(4) NOT NULL,
shopin_mei VARCHAR(255) NOT NULL,
shopin_bunrui VARCHAR(255) NOT NULL,
hanbai_tanka INTEGER ,
shiire_tanka INTEGER ,
tprokubi DATE,
PRIMARY KEY (shopin_id) ) ;
-
命名规则
数据库名称,表名和列名等可以使用这种规则:
- 半角英文字母
- 半角数字
- 下划线(_)
- 名称必须以英文字母作为开头,不是说不能以数字开头,是可以的,但是还是要符合sql标准
- 名称不能重复
-
主键(PRIMARY KEY)
表中的每一行都应该有唯一标识自己的一列,其值能够区分表中的每一行,没有主键,更新或删除表中特定行很困难
- 任意两行都不具有相同的主键值
- 每个行都必须具有一个主键值(主键值不允许为NULL)
数据类型的指定
-
INTEGER
用来存储整数列的数据类型,不能存储小数 -
CHAR
CHAR是CHARACTER的简称,用来存储字符串的列的数据类型,括号中指定的是该列可以存储字符串的最大长度,字符串超出最大长度的部分是无法输入到该列中的,如果我们设置的长度是8,而我们存储的数据是'abc',数据库会以'abc| | | | | |'(abc后面有5个空格)的形式保存起来, -
VARCHAR
同CHAR一样,但是该类型是以可变长字符串形式来保存的,例如我们存入'abc'的时候,保存的就是字符串abc,不会有空格
表删除和更新
-
表的删除(DROP TABLE语句)
语法
DROP TABLE <表名>;
删除Shopin表
DROP TABLE Shopin;
-
表的更新操作(ALTER TABLE语句)
语法
ALTER TABLE <表名> ADD COLUMN <列的定义>;
- 添加一列可以存储100可变长的字符串mei_kana列
ALTER TABLE Shopin ADD COLUMN mei_kana VARCHAR(100);
- 删除列
ALTER TABLE <表名> DROP COLUMN <列名>;
删除我们刚才创建的列
ALTER TABLE Shopin DROP COLUMN mei_kana;
-
向表中插入数据
语法
INSERT INTO <表名> VALUES (....);
向shopin表中插入数据
这里是一共上面8条数据的inset语句
INSERT INTO `shop`.`shopin`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`, `tprokubi`) VALUES ('0001', 'T恤衫', '衣服', 1000, 500, '2018-12-24');
INSERT INTO `shop`.`shopin`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`, `tprokubi`) VALUES ('0002', '打孔器', '办公用品', 500, 320, '2018-12-12');
INSERT INTO `shop`.`shopin`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`) VALUES ('0003', '运动T恤', '衣服', 4000, 2800);
INSERT INTO `shop`.`shopin`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`, `tprokubi`) VALUES ('0004', '菜刀', '厨房用具', 3000, 2800, '2018-12-24');
INSERT INTO `shop`.`shopin`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`, `tprokubi`) VALUES ('0005', '高压锅', '厨房用具', 6800, 5000, '2018-06-24');
INSERT INTO `shop`.`shopin`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `tprokubi`) VALUES ('0006', '叉子', '厨房用具', 500, '2018-12-24');
INSERT INTO `shop`.`shopin`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`,`tprokubi`) VALUES ('0007', '擦菜板', '厨房用具', 880,790, '2018-04-01');
INSERT INTO `shop`.`shopin`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `tprokubi`) VALUES ('0008', '圆珠笔', '办公用品', 100, '2018-10-01');
SELECT语句基础
-
列的查询
- 在日常使用中,SELECT使用的最多,基本语法如下
SELECT <列名>,... FROM <表名>;
- 从Shopin表中查询商品编号,名称和进货单价
SELECT shopin_id,shopin_mei,shiire_tanka FROM Shopin;
- 在这里也可以使用'*',代表的是所有的列
-
为列设置别名
这个AS可以后面可以跟英文和中文,中文的话使用双引号引起来
SELECT
shopin_id AS "商品编号",
shopin_mei AS "商品名称",
shiire_tanka AS "进货单价"
FROM Shopin;
-
常数的查询
常熟查询就是查的时候多出来一列里面的内容就是我们自定义好的内容
SELECT
'商品' AS mo ,
shopin_id , shopin_mei
FROM Shopin;
通过上面的sql语句查出来的就会多出来一个mo的列,里面的值都是商品
-
从查询结果中删除重复行
这里需要说明下是从select语句中删除重复的行,而不是删除数据库中的数据,如果有两条都是null,也会删除重复
SELECT DISTINCT <列名>,<列名>... FROM <表名>
-
根据WHERE语句来选择记录
WHERE语句其实就是根据条件去查询,比如说,我们想查询成绩是65的学生等等,查看语法
SELECT <列名>... FROM <表名> WHERE <条件表达式>;
我们根据上面的Shopin表查询所有品类是衣服的
SELECT * FROM Shopin WHERE shipin_bunrui = '衣服';
这里是先通过where语句查询出所有的记录,然后再根据指定的条件去查询
-
限制结果
SELECT * FROM Shopin LIMIT 5;
此运行结果是查到第一行到第五行的数据,拿到下一个5行
SELECT * FROM Shopin LIMIT 5,5;
这个语句一般用于分页上,检索出第一行是0而不是1,因此LIMIT1,1将检索出第二行而不是第一行
-
注释的书写方法
注释的方法有以下几种
- 书写在"-- "之后,只能注释一行
- 书写在"# "之后,只能注释一行
- 书写在/* 和 */之间,可注释多行
第二章:算数运算符和比较运算符
-
算数表达式
含义 | 运算符 |
---|---|
加法运算 | + |
减法运算 | - |
乘法运算 | * |
除法运算 | / |
这里看一个例子
SELECT age*2 FROM person
这个语句就是查询了年龄乘以2后的结果,当然,也可以使用括号运算符,括号运算符可以指定优先级比如(1+2)*3
-
需要注意null
这里需要说明下,所有包含为null的计算,结果肯定的是null,那我们有的小伙伴就有点奇怪了,我们很多时候想通过 5 +null得到5这样的结果该怎么办呢,接着往下看,后面会介绍到
-
比较运算符
含义 | 运算符 |
---|---|
和~相等 | = |
和~不相等(等同于!=) | <> |
大于等于~ | >= |
大于~ | > |
小于等于~ | <= |
小于~ | < |
这些运算符可以对整数,字符,还有日期几乎所有的数据类型都可以进行比较
-
对字符串使用运算符时需要注意的事项
chr(字符串类型) |
---|
1 |
2 |
3 |
10 |
12 |
222 |
- 这里我们执行大于'2'的操作会得到什么样的结果呢
- 所得到的结果是 3,222
- 没想到吧,是不是觉得10和12都比2大,所有应该选出来呢,大家之所以会混淆大概是因为数字和字符串的缘故,也就是说2和'2'并不一样
- 之所以10和12没有选出来,是因为他们是以1开头的,这就像在字典中"提问","提议"."问题"按照以下顺序排列一样,"提问","提议","问题"
- 或者我们以书籍的章节为列也可以,1-2节包含在第一章当中,所以肯定比第二章更靠前
-
不能对null使用比较运算符
先举几个例子
- 查询某种单价不等于20的商品
SELECT * FROM Shopin WHERE shiire_tanka <> 20;
如果单价里面有null的时候,这里是不会显示出来的,大家可以去做下实验
- 那我们是否想到了另一种方法呢
SELECT * FROM Shopin WHERE shiire_tanka = null;
执行完之后返回一条记录都没有
- sql不识别"=null"和"<>null"
- sql给我们专门提供了判断是否为空的运算符IS NULL,想要选取null记录时如下
SELECT * FROM Shopin WHERE shiire_tanka IS NULL;
- 反之 IS NOT NULL运算符,对null的运算符还有很多,将会在后面做出说明
逻辑运算符
-
NOT运算符
NOT运算符是用来否定某一个条件,但是不能滥用
- 先看下下面的例子
SELECT * FROM Shopin WHERE hanbai_tanka >= 1000;
这个语句是查询单价大于等于1000的记录
- 在这里加上NOT
SELECT * FROM Shopin WHERE NOT hanbai_tanka >= 1000;
执行完之后,发现结果是单价小于1000的,等价于
SELECT * FROM Shopin WHERE hanbai_tanka < 1000;
通过上面的例子我们可以发现,不使用NOT运算符也可以编出有效果的相同的查询条件.不仅如此,不使用NOT运算符的查询条件更容易让人理解,使用NOT运算符时,我们不得不每次都在大脑中进行"大于等于1000以上这个条件的否定结果就是小于1000"这样的转换
虽然如此,但是也不能否定NOT运算符的作用,在编写复杂的sql时,经常会用到NOT的身影,这里只是希望大家了解NOT运算符的书写方法和工作原理,同时提醒大家不要滥用该运算符
-
AND和OR运算符
- AND运算符在其两侧的查询条件都成立时整个查询条件才成立,其意思相当于并且,语法
SELECT <列名> FROM <表名> WHERE <条件> AND <条件>;
- OR运算符是在其两侧的查询条件有一个成立时整个查询条件都成立,其意思是相当于或者
SELECT <列名> FROM <表名> WHERE <条件> OR <条件>;
- AND运算符优于OR运算符,想要先执行OR运算符时可以使用括号
第三章:对表进行聚合查询
-
聚合函数
通过对sql数据进行操作或计算时使用函数,计算表中全部数据行数时,可以使用COUNT函数.该函数就是使用COUNT来命名的,除此之外,SQL中还有很多其他用于合计的函数,请大家先记住以下5个常用的函数
- COUNT:计算表中的记录数(行数)
- SUM:计算表中数值列的数据总和
- AVG:计算表中数值列的数据的平均值
- MAX:计算表中数值列的数据的最大值
- MIN:计算表中数值列的数据的最小值
-
计算表中数据的行数
SELECT COUNT(*) FROM Shopin; //计算全部数据的行数
COUNT函数的结果根据参数的不同而不同,COUNT(*)会得到包含NULL的数据的行数,而COUNT(<列名>)会得到NULL之外的数据行数
-
计算总和
SELECT SUM(<列名>) FROM <表名>;
-
计算平均值
SELECT AVG(<列名>) FROM <表名>;
计算平均值,如果有的列是null,是不会计算在内的
-
计算最大值和最小值
SELECT MAX(<列名>) FROM <表名>; //最大值
SELECT MIN(<列名>) FROM <表名>; //最小值
max/min适用于所有数据类型的列,sum/avg函数只适用于数值类型的列
-
使用聚合函数删除重复值(关键字DISTINCT)
前面我们已经了解了删除重复值关键字DISTINCT
- 举个例子,我们上面的shopin表中有好多商品分类都是相同的,我们用sql语句查下她一共有几个分类
SELECT COUNT(DISTINCT shopin_bunrui) FROM Shopin;
这个就是删除重复值,在计算所有总行数之前先把重复的值删掉 在聚合函数的参数中使用DISTINCT,可以删除重复数据,对sum/avg都有效
对表进行分组
-
GROUP BY 子句
下面我们把表分成几组,然后再进行聚合处理,也就是按照"商品种类","登记日期"等进行聚合,语法如下
SELECT <列名>,<列名>... FROM <表名> GROUP BY <列名> ,<列名> ...
下面我们来统计下商品种类的数据行数
SELECT shopin_bunrui, COUNT(*)
FROM Shopin
GROUP BY shopin_bunrui
执行结果
shopin_bunrui | COUNT |
---|---|
衣服 | 2 |
办公用品 | 2 |
厨房用具 | 4 |
如上所示,未使用GROUP BY时,结果只有1行,而这次结果是多行,这个是因为不使用GROUP BY子句时,是将表中的所有数据作为一个组来对待的,使用了之后,会将表中的数据分为多个组进行处理,像切蛋糕一样进行了分组,在GROUP BY子句中指定的列称为聚合键或者分组列..GROUP BY就像是切分表的一把刀
当聚合键中存在有NULL的时候,在结果中会以空行的形式表现出来
-
使用WHERE子句时GROUP BY的执行结果
语法如下
SELECT <列名>,<列名>... FROM <表名> WHERE <条件表达式> GROUP BY <列名> ,<列名> ...
像这样使用WHERE进行聚合处理时,会先通过WHERE子句指定的条件进行过滤然后再进行聚合处理,看下面的例子
SELECT shiire_tanka,COUNT(*) FROM Shopin WHERE shopin_bunrui = '衣服' GROUP BY shiire_tanka
上面这段代码首先是使用了WHERE对记录进行了过滤,查询到数据库中的记录
shopin_bunrui(商品分类) | shopin_mei(商品名称) | shiire_tanka(进货单价) |
---|---|---|
衣服 | T恤衫 | 500 |
衣服 | 运动T恤 | 2800 |
然后再进行进货单价对两条记录进行处理,得到如下结果
shiire_tanka(进货单价) | count(*) |
---|---|
500 | 1 |
2800 | 1 |
GROUP BY与WHERE 并用时SELECT语句的执行顺序
FROM - WHERE - GROUP BY - SELECT
-
使用聚合函数和GROUP BY时常见的错误
-
常见错误1:在SELECT中书写了多余的列
在使用COUNT这样的聚合函数时,SELECT子句中元素室友严格的限制,实际上,使用聚合函数时,SELECT 子句中只能存在以下几种元素
- 常数
- 聚合函数
- GROUP BY 子句中指定的列名 这里我们想一想也就明白了,比如查询商品名称和总行数,那名称这一列该显示什么名字呢,那么多的名称都显示出来吗,显然是不可能的 使用GROUP BY子句时,SELECT子句中不能出现聚合键之外的列名
-
常见错误2:在GROUP BY中书写了别名
SELECT shiire_tanka AS sb , COUNT(*) FROM Shopin GROUP BY sb;
上述的代码在MySQL里执行是没有什么问题的,可以正常执行,但是这样的写法是不便于阅读的,因此还是请大家不要使用
-
常见错误3:在WHERE子句中使用聚合函数
先看下下面的代码
SELECT category_id ,COUNT(*) FROM blog WHERE COUNT(*) = 2 GROUP BY category_id
遗憾的是,这样的SELECT在执行时发生了错误 只有SELECT子句在HAVING子句或者ORDER BY子句中能够使用聚合函数
-
在这里就引出了一个问题
可能有的小伙伴就能发现,我们在前面使用的DISTINCT和所学的GROUP BY,都能够删除后序列中的重复数据,查看以下代码
SELECT DISTINCT shopin_hunrui FROM Shopin;
SELECT shopin_hunrui FROM Shopin GROUP BY shopin_hunrui;
这两个代码执行后的结果都是相同的,其实不仅是执行结果,执行速度上也基本差不多,到底使用哪一种呢?
不引用COUNT等聚合函数,而只使用GROUP BY子句的SELECT语句,会让人觉得非常奇怪,难免会让人产生"为什么要对表进行分组呢?这样有必要吗?"等疑问,SQL语句语法与英语十分相似,理解起来非常容易,希望大家不要浪费这一优势,编写出一些难以理解的SQL语句
为聚合结果指定条件
-
HAVING子句
给GROUP BY分组后的结果指定条件,语法如下
SELECT <列名>,<列名>... FROM <表名> GROUP BY <列名>,<列名>... HAVING <条件>;
HAVING子句必须在GROUP BY之后,HAVING要写在GROUP BY之后 接下来就让我们练习下HAVING子句吧
SELECT shopin_bunrui ,COUNT(*) FROM Shopin GROUP BY shopin_bunrui HAVING COUNT(*) = 2;
这里我就不写执行结果了,大家可以去测试,意思就是说对我们刚才的GROUP BY加了一层过滤条件,只有满足条件COUNT(*)=2的才能查询出来
-
HAVING子句的构成元素
HAVING子句和包含GROUP BY子句时的SELECT子句一样,能够使用的元素有一定的限制,限制内容也是完全相同的,HAVING能够使用的元素如下
- 常数
- 聚合函数
- GROUP BY 子句指定的列名
-
相对于HAVING子句,更适合写在WHERE子句中的条件
有些条件可以写在HAVING子句当中,又可以写在WHERE当中,得到的结果都是一样的,先看下下面的代码
SELECT shopin_bunrui ,COUNT(*) FROM Shopin GROUP BY shopin_bunrui HAVING shopin_bunrui = '衣服';
上面的sql是获取衣服这个分类的总条数,再看下面这个代码
SELECT shopin_bunrui ,COUNT(*) FROM Shopin WHERE shopin_bunrui = '衣服' GROUP BY shopin_bunrui;
上面两个执行完后的结果
shopin_bunrui | COUNT |
---|---|
衣服 | 2 |
虽然条件写在WHERE和HAVING子句当中,但是条件的内容,以及返回的结果完全相同,因此大家可能会觉得两种书写方式都没问题
如果仅从结果来看的话,确实如此,但是笔者认为,聚合键所对应的条件还是应该书写在WHERE子句当中
理由有两个
首先,根本原因是WHERE子句和HAVING子句的作用不同,如前所述,HAVING子句是用来指定组的条件的,因此"行"所对应的条件还是应该书写在WHERE子句当中,这样一来,书写出的SELECT语句不但可以分清两者各自的功能,理解起来还更容易
当然,WHERE比HAVING的执行速度会更快一点
对查询结果就行排序
-
ORDER BY子句
通常,从表中抽取数据时,如果没有特别指定顺序,最终排列顺序便无从得知,即使是同一条SELECT语句,每次执行时排列顺序很有可能发生改变
但是不进行排序很有可能出现结果混乱的情况,这时,便需要通过在SELECT语句 末尾加上ORDER BY子句来指定排列顺序,语法如下
SELECT <列名>,<列名>... FROM <表名> ORDER BY <排序基准列>,<排序基准列>...;
- 按照销售单价由低到高(升序)进行排列
SELECT * FROM Shopin ORDER BY hanbai_tanka;
执行结果我就不写了,大家可以做做实验
-
指定升序或降序
上面已经知道了升序排列是由小到大,降序就是由大到小,需要使用到关键字DESC,升序使用到的关键字是ASC,一般默认就是升序,所以可以省略
SELECT * FROM Shopin ORDER BY hanbai_tanka DESC;
-
指定多个键排序
SELECT * FROM Shopin ORDER BY hanbai_tanka , shopin_id;
做了实验的小伙伴应该可以仔细观察下,这个是根据单价进行由小到大的排序,如果单价有相同的话,会根据id由小到大的顺序排列
-
NULL的顺序
列为null时是不会进行排序的,但是也是会显示出来到结果里面,会在开头或者末尾进行汇总
第四章:数据更新
数据的插入(INSERT语句的使用方法)
-
INSERT语句的基本语法
INSERT INTO <表名> (<列名>,<列名>...) VALUES (值1,值2...);
这里举一个例子,比如说我们想往用户表中插入一条数据怎么操作呢
INSERT INTO user (id,name) VALUES (1,'张三');
这样就成功把数据添加到数据库了,这里其实我想说的是,可以省略表名后的列清单,这时VALUES子句的值会默认按照从左到右的顺序赋给每一列
INSERT INTO user VALUES (1,'张三');
-
从其他表中复制数据
插入数据的方法,除了使用VALUES子句指定具体的数据之外,还可以从其他表中复制数据,下面我们来看下如何从一张表复制到另一张表 我做实验的时候一共创建了两张表,先看下
- role表
id | name |
---|---|
1 | 张三 |
2 | 李四 |
3 | 王五 |
- user表
id | name | age |
---|---|---|
user表里是没有数据的,我是想把role表的内容复制到user里面, 接下来就让我们赶快尝试下吧
INSERT INTO user (id,name) SELECT (id,name) FROM role ;
执行完该INSERT .... SELECT语句之后,表中就多了3条数据,这个age字段其实是我有意加的,我想试试多加了字段可以添加成功吗,大家也可以试试,这个语句可以在需要进行数据备份时使用
其实这里复制数据也是可以使用WHERE.GROUP BY等等一些子句,举个例子,比如有个商品表,想查出该表的所有分类的销售总价和进货总价,然后添加到另一张表中,还是之前前面学说的商品表,创建个ShopinBunrui表,我们写个sql看看
INSERT INTO
ShopinBunrui (shop_bunrui,sum_hanbai_tanka,sum_shiire_tanka)
SELECT shop_bunrui,SUM(hanbai_tanka),SUM(shiire_tanka)
FROM Shopin
GROUP BY shop_bunrui;
数据的删除(DELETE语句的使用方法)
-
DELETE语句的基本用法
指定删除对象的DELETE的语句
DELETE FROM <表名> WHERE <条件>;
清空某张表,而想保留这张表的结构
DELETE FROM <表名>;
其实mysql还提供了一种只能删除全部数据的TRUNCATE语句
TRUNCATE <表名>;
与DELETE不同的是,TRUNCATE只能删除表中的全部数据,不能通过WHERE子句指定条件去删除部分数据,也正是因为它不能具体控制删除对象,所以其处理速度比DELETE要快很多,实际上,DELETE语句在DML语句中也属于处理时间比较长的,因此需要删除全部数据时,使用TRUNCATE可以缩短执行时间
数据的更新(UPDATE语句的使用方法)
-
UPDATE语句的基本用法
修改整张表的某个列
UPDATE <表名> SET <列名> = <表达式>;
指定条件的UPDATE语句
UPDATE <表名> SET <列名> = <表达式> WHERE <条件>;
事务
-
什么是事务
估计大家对事务这个词并不陌生,它通常都带有一些商务贸易或者经济活动的意味,但是在RDBMS中,事务代表了对表中数据进行更新的单位,简单来讲,事务就是需要在同一个处理单元中执行的一系列更新处理的集合
如前面所述,对表进行更新需要使用INSERT,DELETE或者UPDATE三种语句,但通常情况下,更新处理并不是执行一次就结束了,而是需要执行一系列连续的操作,这时,事务就能提现出她的价值了
说到事务的例子,请大家思考一下下述情况
现在,请大家把自己想象成管理shopin表的程序员或者软件工程师,销售部门的领导对你提出如下要求
"某某,经过会议讨论,我们决定把运动裤的销售单价下调1000元,同时把T恤的销售单价上浮1000元,麻烦你更新下数据库"
在这里只需要使用UPDATE进行更新就可以了,所以肯定会直接回答"知道了,你放心吧"
此时的事务就是由如下两条更新进行处理所组成
1.
UPDATE Shopin
SET hanbai_tanka = hanbai_tanka - 1000
WHERE shopin_mei = '运动裤';
2.
UPDATE Shopin
SET hanbai_tanka = hanbai_tanka + 1000
WHERE shopin_mei = 'T恤';
将上面的操作一定要作为同一个处理单元执行,如果只执行了1的操作而忘执行2的操作,或者反过来只执行了2的操作没有执行1的操作,一定会收到领导的严厉批评,遇到这种需要在同一个处理单元中执行一系列更新操作的情况,一定要使用事务来进行处理
事务是需要在同一处理单元中执行的一系列更新处理的集合
一个事务中包含多少个更新处理或者包含那些处理,并没有固定的标准,而是根据用户的要求决定的
-
创建事务
如果想在DBMS中创建事务,可以按照如下语法编写sql语句
START TRANSACTION;
DML语句;
DML语句;
DML语句;
...
事务结束语句(COMMIT或者ROLLBACK);
使用事务开始语句和事务结束语句,将一系列DML语句括起来,就实现了一个事务处理
我们对之前的商品信息创建出事务
START TRANSACTION;
UPDATE Shopin
SET hanbai_tanka = hanbai_tanka - 1000
WHERE shopin_mei = '运动裤';
UPDATE Shopin
SET hanbai_tanka = hanbai_tanka + 1000
WHERE shopin_mei = 'T恤';
COMMIT;
- COMMIT--提交处理 COMMIT是提交事务包含的全部更新处理的结束指令,相当于文件处理的覆盖保存,一旦提交,就无法恢复到事务开始前的状态了,因此,在提交之前一定要确认是否真的需要这些更新
- ROLLBACK--取消处理 ROLLBACK是取消事务包含的全部更新处理的结束指令.相当于文件处理中的放弃保存,一旦回滚,数据库就会恢复到事务开始之前的状态,通常回滚并不会像提交那样造成大规模的数据损失
事务回滚执行之后,表中的数据不会发生任何改变,这是因为执行最后一行的ROLLBACK之后,所有处理都被取消了
-
ACID特性
事务都遵循四种标准规格的约定,将这四种特性的首字母结合起来统称为ACID特性,这些约定都是必须遵守的规则
- 原子性
原子性是指在事务结束时,其中所包含的更新处理要么全部执行,要么完全不执行的特性,也就是要么占有一切,要么一无所有,例如,在之前的例子中,事务结束时,是绝对不可能出现运动裤价格下降了而T恤价格上涨了的情况该事物结束状态,要么两者都执行了,要么都不执行
- 一致性
一致性指的是事务包含的处理,要满足数据库提前设置的约束,如主键约束或者NOT NULL约束等,例如:设置了NOT NULL约束的列是不能更新为NULL的,试图插入违反主键约束的记录就会出错,无法执行.对事务来说,这些不合法的SQL会被回滚,也就是说这些SQL处理会被取消,不会执行
- 隔离性
隔离性指的是在不同的事务之间是互不干扰的特性,该特性保证了事务之间不会相互嵌套,此外,在某个事务中进行的更改,在该事务结束之前,对其他事务而言是不可见的,因此,即使某个事务向表中添加了记录,在没有提交之前,其他事务是看不到新添加的记录的
- 持久性
持久性也可以称为耐久性,指的是事务(不论是提交还是回滚)一旦结束,DBMS会保证该时点的数据状态得以保存的特性.即使由于系统故障导致数据丢失,数据库也一定能通过某种手段进行恢复
如果不能保证持久性,即使是正常提交结束的事务,一旦发生了系统故障,就会导致数据丢失,一切都需要从头再来的后果
保证持久性的方法根据实现的不同而不同,其中最常见的事务就是将事务的执行记录保存到硬盘等存储介质中(该执行记录称为日志),当发生故障时,可以通过日志恢复到故障发生前的状态
第五章:复杂查询
视图
-
视图是什么
通俗的讲,视图就是一条SELECT语句执行后返回的结果集。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上,视图充当的是虚拟表的角色。
-
视图的优点
可以将频繁使用的SELECT语句保存成视图,这样就不会每次都重新书写了,创建好视图后,只需在SELECT语句中进行调用,就可以方便的得到想要的结果了,视图也会随着表里数据的更新而更新,非常方便灵活
-
创建视图的方法
创建视图可以通过CREATE VIEW语句来完成
CREATE VIEW 视图名称 (<视图列名1>,<视图列名2>...)
AS
<SELECT语句>
SELECT语句需要书写在AS关键字之后.SELECT语句中列的排列顺序和视图中列的排列顺序相同,SELECT语句中的第一列就是视图中的第一列,以此类推,视图的列名在视图名称之后的列表中定义
接下来就让我们创建第一个视图吧,还是之前的Shopin表
CREAT VIEW ShopinSum (shopin_bunrui,cnt_shopin)
AS
SELECT shopin_bunrui,COUNT(*)
FROM Shopin
GROUP BY shopin_bunrui;
这样我们就在数据库中创建了一个名为ShopinSum的视图,此处的AS不能省略,也和使用别名时的AS不一样,虽然很容易混淆,但是语法就是这么规定的
- 使用视图
SELECT shopin_bunrui,cnt_shopin FROM ShopinSum;
执行结果
shopin_bunrui | cnt_shopin |
---|---|
衣服 | 2 |
办公用品 | 2 |
厨房用具 | 4 |
通过上述的视图ShopinSum定义的主体(SELECT语句)我们可以看出,该视图将根据商品种类(shopin_bunrui)统计出商品数量(cnt_shopin)作为结果保存起来,这样如果大家在工作中需要频繁进行统计时,就不用每次都书写使用GROUP BY和COUNT函数的SELECT语句
-
视图的限制
- 定义视图时不能使用ORDER BY子句
- 对视图进行更新 视图是随着主表的更新而更新的,对视图的更新也是有很多条件的,比如说我们使用了聚合建对视图进行了更改,这样就不会保证视图和表的一致性了,下面给大家列举一些具有代表性的条件
①.SELECT子句中未使用DISTINCT
②.FROM子句中只有一张表
③.未使用GROUP BY字句
④.未使用HAVING字句
只有满足了以上四个条件才能对视图进行增删改操作
当我们对视图就行增删改的时候表中的数据也是会发生改变,保持一致性,对视图进行更新归根接地就是对表就行更新
-
删除视图
DROP VIEW 视图名称;
子查询
-
子查询和视图
前面我们学习了视图这个非常方便的工具,本节将学习以视图为基础的子查询.子查询的特点概括起来就是一张一次性的视图,首先我们来看一组sql语句
SELECT shopin_bunrui,cnt_shopin
FROM ( SELECT shopin_bunrui,COUNT(*) AS cnt_shopin
FROM Shopin
GROUP BY shopin_bunrui
) AS ShopinSum;
首先解释下这个sql语句的执行流程,首先执行的是内层的查询
SELECT shopin_bunrui,COUNT(*) AS cnt_shopin
FROM Shopin
GROUP BY shopin_bunrui
执行完成之后结果是放在别名为ShopinSum的视图中
SELECT shopin_bunrui,cnt_shopin
FROM ShopinSum
这样就好理解了,当然也有多层的子查询,我们看个例子
SELECT shopin_bunrui,cnt_shopin
FROM ( SELECT * FROM
(SELECT shopin_bunrui,COUNT(*) AS cnt_shopin
FROM Shopin
GROUP BY shopin_bunrui) AS ShopinSum
WHERE cnt_shopin = 4) AS ShopinSum;
当然,随着子查询层数的增加,SQL语句会变得越来越难读懂,性能也会越来越差,因此还是请尽量避免多层查询
-
标量子查询
标量就是单一的意思,标量子查询就是返回单一值得子查询,说法好像有点抽象,还是看sql语句吧
SELECT shopin_id,shopin_mei,hanbai_tanka
FROM Shopin
WHERE hanbai_tanka >(SELECT AVG(hanbai_tanka) FROM Shopin);
这个时候我们看内层的查询,返回的结果就是单一的值,所以这个就是标量子查询,WHERE子句后面是不能跟聚合函数的,所以我们只能通过这种方式去查
标量子查询绝对不能返回多行结果,如果返回多行的话就会报错
关联子查询
我们试着去想一个场景,就是我们想查出比该分类商品平均价格大的商品,怎么取查询呢
-- 计算每个平均价格
SELECT AVG(hanbai_tanka) FROM Shopin GROUP BY shopin_bunrui;
SELECT * FROM Shopin
WHERE hanbai_tanka > (SELECT AVG(hanbai_tanka) FROM Shopin GROUP BY shopin_bunrui)
我们看下这个平均价格查出来返回的是多行,通过子链接去查的话肯定是会报错的,这个时候就要用到关联查询了,这个比较的抽象还是先观察代码吧
SELECT * FROM Shopin AS S1
WHERE hanbai_tanka > (SELECT AVG(hanbai_tanka) FROM Shopin AS S2
WHERE S1.shopin_bunrui = S2.shopin_bunrui
GROUP BY shopin_bunrui)
这里起到关联作用的就是在子查询中添加的where子句的条件,该条件的意思就是,在同一种类中对比各商品的单价和平均单价进行比较
在关联查询中,外面的表是不能访问内部的表,内部可以访问外面的
第六章:函数,谓词,CASE表达式
各种各样的函数
-
函数的种类
我们在之前已经看到了聚合函数,其实还有很多的函数,聚合函数基本上包括MAX,MIN,AVG,SUM,COUNT这五种,而其他种类的函数超过几百种,虽然函数众多,其实我们日常应用中不是很多,不熟悉的时候大家也可以去查查相关的文档,函数大致可以分为以下几种
- 算术函数(用来进行数值计算的函数)
- 字符串函数(用来进行字符串操作的函数)
- 日期函数(用来进行日期操作的函数)
- 转换函数(用来转换数据类型和值得函数)
- 聚合函数(用来进行数据聚合的函数)
-
算术函数
不知道大家对前面的+ - * /还有印象吗,其实那个就是算数函数,我们看看一些常用的算数函数吧
-
ABS函数
ABS是计算绝对值的函数,简单来说,绝对值的计算方法就是:0和正数的绝对值就是其本身,负数的绝对值就是去掉符号的结果
SELECT m,ABS(m) AS abs_col FROM 表名;
-
求余MOD函数
MOD(被除数,除数),例如7/3的余数是1,就是MOD(7 , 1)
SELECT m,n,MOD(m,n) AS mod_col FROM 表名;
-
ROUND--四舍五入
ROUND(对象数值,保留小数的位数)例如7.8888,保留一位,就是ROUND(7.8888 , 1)
SELECT m,n,ROUND(m,n) AS round_col FROM 表名;
-
字符串函数
-
CONCAT 拼接
在实际业务中,我们会经常碰到abc + de = abcde这样希望字符串拼接的情况,通过两条竖线"||"就可以实现
SELECT str1,str2,CONCAT(str1,str2) AS str_col FROM 表名;
-
LENGTH 字符串的长度
LENGTH(字符串)
SELECT str1,LENGTH(str1) AS len_str FROM 表名;
-
LOWER 小写转换
LOWER(字符串)
SELECT str1,LOWER(str1) AS low_str FROM 表名;
-
SUBSTRING 字符串的截取
SUBSTRING(对象字符串 FROM 截取的起始位置 FOR 截取的字符数)
SELECT str1,SUBSTRING(str1 FROM 3 FOR 2) AS sub_str FROM 表名;
-
UPPER 大写转换
UPPER(字符串)
SELECT str1,UPPER(str1) AS up_str FROM 表名;
-
将空字符串转换为固定字符
SELECT COALESCE ( NULL, '你好' )
-
日期函数
-
CURRENT_DATE 当前日期,2018-12-21
SELECT CURRENT_DATE;
-
CURRENT_TIME 当前时间,22:52:57
SELECT CURRENT_TIME;
-
CURRENT_TIMESTAMP 当前日期和时间,2018-12-21 22:53:09
SELECT CURRENT_TIMESTAMP;
-
EXTPACT 截取日期元素
EXTPACT(日期元素 FROM 日期)
SELECT CURRENT_TIMESTAMP
,
extract( YEAR FROM CURRENT_TIMESTAMP ) AS YEAR,
extract( MONTH FROM CURRENT_TIMESTAMP ) AS MONTH,
extract( DAY FROM CURRENT_TIMESTAMP ) AS DAY,
extract( HOUR FROM CURRENT_TIMESTAMP ) AS HOUR,
extract( MINUTE FROM CURRENT_TIMESTAMP ) AS MINUTE,
extract( SECOND FROM CURRENT_TIMESTAMP ) AS SECOND;
执行结果
CURRENT_TIMESTAMP | YEAR | MONTH | DAY | HOUR | MINUTE | SECOND |
---|---|---|---|---|---|---|
2018-12-21 23:00:06 | 2018 | 12 | 21 | 23 | 0 | 6 |
-
Date()函数 ,返回当前时间里的日期部分
select DATE(CURRENT_TIMESTAMP);
-
常用日期和时间处理函数
函数 | 说明 |
---|---|
AddDate() | 增加一个日期(天,周等) |
AddTime() | 增加一个时间(时,分等) |
CurDate() | 返回当前日期 |
CurTime() | 返回当前时间 |
Date() | 返回当前日期时间的日期部分 |
DateDiff() | 计算两个日期只差 |
Date_Add() | 高度灵活的日期运算函数 |
-
转换函数
转换这个词的含义非常广泛,在SQL中有两层意思,一是数据类型转换,简称为类型转换,另一层意思是值得转换
-
CAST函数
CAST(转换前的值 AS 想要转换的数据类型)
可以转换的类型是有限制的。这个类型可以是以下值其中的一个:
- 二进制,同带binary前缀的效果 : BINARY
- 字符型,可带参数 : CHAR()
- 日期 : DATE
- 时间: TIME
- 日期时间型 : DATETIME
- 浮点数 : DECIMAL
- 整数 : SIGNED
- 无符号整数 : UNSIGNED
SELECT CAST('0001' AS SIGNED)
SELECT CAST('2018-1-4' AS date)
-
COALESCE -- 将NULL转换为其他值
COALESCE(数据1,数据2....)
SELECT COALESCE(NULL,1) AS col_1 ,
COALESCE(NULL,NULL,'测试') AS col_2 ;
谓词
-
什么是谓词
虽然之前没有提及过谓词这个概念,但其实前面已经使用过了,例如=,<.>等比较运算符,其正式名称就是比较谓词,将会给大家介绍一下几个谓词
- LIKE
- BETWEEN
- IS NULL , IS NOT NULL
- IN
- EXISTS
-
LIKE谓词
LIKE这个关键字其实在实际应用中经常会用到,这就是模糊查询
SELECT
*
FROM
shop
WHERE
name LIKE '%刀%'
这里这个sql语句的意思就是说,只要name这个字段里面含有刀字的都会查出来
-
BETWEEN谓词--范围查询
使用BETWEEN可以进行范围查询,比如说我们想查出年龄在18到20的人
SELECT
*
FROM
shop
WHERE
age BETWEEN 18 and 20
-
IS NULL 和 IS NOT NULL判断是否为空
这里前面都说过了,简单的写个sql语句
SELECT
*
FROM
shop
WHERE
name IS NOT NULL
-
IN谓词--OR的简便用法
我们之前用or查询过查过商品是50元或者100元的商品
SELECT
*
FROM
shop
WHERE
jiage = 50
OR jiage = 10
如果随着天剑的越来越多,一直追加OR吗,当然是不可能的,这里就要使用到IN谓词了
SELECT
*
FROM
shop
WHERE
jiage IN ( 50, 10 )
这里还有 NOT IN,意思和IN关键字相反
这里需要对NOT IN进行说明下,NOT IN里面是不能有NULL的,有空的话查询的结果就是空,一条结果都不会出来
-
EXIST谓词
EXIST位置的作用是判读是否存在满足条件的记录,语法理解起来比较困难 这里为了理解起来比较方便,有创建了一张商店商品表
商店(tenpo_id) | 商店名称(tenpomei) | 商品编号(shopinid) | 数量(suryo) |
---|---|---|---|
000A | 北京 | 0001 | 50 |
000A | 北京 | 0002 | 50 |
000A | 北京 | 0003 | 50 |
000A | 北京 | 0004 | 50 |
这里要查的条件就是查出北京店(000A)在售商品的销售单价
SELECT shopin_mei,hanbai_tanka FROM Shopin AS S
WHERE EXISTS (SELECT * FROM Tenshopin AS TS WHERE tenpo_id = '000A' AND S.shopid_id = TS.shopinid)
执行结果
shopin_mei | hanbai_tanka |
---|---|
T恤衫 | 500 |
打孔器 | 320 |
运动T恤 | 2800 |
菜刀 | 2800 |
之前我们学的谓词,基本上都是列LIKE字符串或者列BETWEEN 值1AND值2,这样指定参数,而EXIST的左侧并没有任何的参数,很奇妙吧,这是因为EXIST是只有1个参数的谓词,EXIST只需要在右边书写一个参数,该参数通常就是一个子查询,就如我们上面所写的那样
CASE表达式
-
CASE表达式的语法
其实CASE表达式和java中的switch还是很像的,语法分为简单CASE表达式和搜索CASE表达式两种,下来就来看看表达式的语法吧
CASE
WHEN <判断表达式> THEN <表达式>
WHEN <判断表达式> THEN <表达式>
.
.
ELSE NULL
END
CASE表达式会对WHEN中的条件进行判断,如果条件成立就输出THEN里的内容,如果没有符合条件就输出的是ELSE里的内容,最终走向END,END是不能省略的
搜索CASE表达式
SELECT
wwww,
CASE
WHEN jiage = 200 THEN CONCAT( 'A:', jiage )
WHEN jiage = 10 THEN CONCAT( 'B:', jiage )
ELSE NULL
END AS casejiage
FROM
shop
简单CASE表达式
SELECT
wwww,
CASE jiage
WHEN 200 THEN CONCAT( 'A:', jiage )
WHEN 10 THEN CONCAT( 'B:', jiage )
ELSE NULL
END AS casejiage
FROM
shop
最后的ELSE NULL代表了上述条件都不成立时返回NULL,其实这个是可以省略的,默认的就是NULL
- 这里我们对之前的GROUP BY进行下改造 之前我们通过GROUP BY分组查某个分类商品的总价,其实通过CASE表达式也是可以完成的,我们一起来看看吧
SELECT
SUM(CASE WHEN shopin_bunrui = '衣服' THEN hantai_tanka END) AS sum_tanka_ihuku,
SUM(CASE WHEN shopin_bunrui = '厨房用具' THEN hantai_tanka END) AS sum_tanka_kitchen
FROM Shopin
执行结果
sum_tanka_ihuku | sum_tanka_kitchen |
---|---|
5000 | 600 |
这样的结果会以"行"的形式输出,而无法以列的形式进行排列
第七章:结合运算
表的加减法
-
表的加法--UNION
学习之前我们先添加一张表,和之前的Shopin表的结构一样,只是表面变成了Shipn2,在表中添加几条数据
INSERT INTO `shopin2`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`, `tprokubi`) VALUES ('0001', 'T恤衫', '衣服', 1000, 500, '2018-12-24');
INSERT INTO ``shopin2`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`, `tprokubi`) VALUES ('0002', '打孔器', '办公用品', 500, 320, '2018-12-12');
INSERT INTO `shopin2`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`) VALUES ('0003', '运动T恤', '衣服', 4000, 2800);
INSERT INTO `shopin2`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`) VALUES ('0009', '手套', '衣服', 800, 500);
INSERT INTO ``shopin2`(`shopin_id`, `shopin_mei`, `shopin_bunrui`, `hanbai_tanka`, `shiire_tanka`, `tprokubi`) VALUES ('0010', '水壶', '厨房用具', 2000, 1700, '2018-12-12');
这张表的有三条数据是和之前的表是重复的,有两条是这张表独有的,使用UNION对表就行加法计算
SELECT shopin_id,shopin_mei
FROM Shopin
UNION
SELECT shopin_id,shopin_mei
FROM Shopin2
执行结果我就不展示了,做了实验了小伙伴应该会发现,只查出了10调数据,是没有重复数据的,因为0001~0003的数据两个表都存在,这就是我们在学校学过的并集运算
-
包含重复行的集合运算--ALL选项
SELECT shopin_id,shopin_mei
FROM Shopin
UNION ALL
SELECT shopin_id,shopin_mei
FROM Shopin2
在集合中使用ALL选项,可以保留重复行
-
集合运算的注意事项
- 作为运算对象的记录列数必须相同,像下面的例子,一个是查两列,一个是查三列,会发生错误,无法进行计算
SELECT shopin_id,shopin_mei
FROM Shopin
UNION
SELECT shopin_id,shopin_mei,hanbai_tanka
FROM Shopin2
- 作为运算对象的记录中列的类型必须一致,从左侧开始,相同位置上的列必须是同一数据类型,例如,下面的SQL语句中,第二列的数据类型不一致,一个是数值类型,一个是日期类型 ,所以就会发生错误
SELECT shopin_id,hanbai_tanka
FROM Shopin
UNION
SELECT shopin_id,torokubi
FROM Shopin2
- 可以使用任何SELECT语句,但ORDER BY子句只能在最后使用一次
通过UNION进行并集运算时可以使用任何形式的SELECT语句,之前学过的WHERE,GROUP BY,HAVING等子句都可以使用,但是ORDER BY只能在最后使用一次
SELECT shopin_id,shopin_mei
FROM Shopin
UNION
SELECT shopin_id,shopin_mei
FROM Shopin2
ORDER BY shopin_id;
-
选取表中公共部分--INTERSECT
下面介绍就是我们数学运算中的交集,就是两张表都共同存在的数据,和UNION语法完全一样
mysql里是错的
SELECT shopin_id,shopin_mei
FROM Shopin
INTERSECT
SELECT shopin_id,shopin_mei
FROM Shopin2
我在做实验的时候,其实mysql并没有这个关键字,这种语法在mysql里是错的,但是,也可以通过一些别的方法实现去取的交集
SELECT
shopin_id,
shopin_mei
FROM Shopin
UNION
SELECT
shopin_id,
shopin_mei
FROM Shopin2
GROUP BY
shopin_id,
shopin_mei
HAVING
COUNT( shopin_id ) =2
联结(以列为单位进行联结)
-
什么是联结
通俗来说,就是进行这些集合运算时,会导致记录行数的增减,使用UNION会增加记录行数,但是这些运算并不会导致列数改变,作为集合运算对象表的前提就是列数要一致,因此运算结果不会导致列的增加
本次学习的联结(JOIN)运算,简单来说就是将其他表中的列添加过来,进行添加列的运算,该操作通常用于无法从一张表中获取期望数据(列)的情况,实际应用中,期望得到的数据往往会分散在不同的表中,使用联结就可以从不同的表中选取数据了
-
内联结--INNER JOIN
- 商品表(Shopin)
商品编号 | 商品名称 | 商品分类 | 销售单价 | 进货单价 | 登记日期 |
---|---|---|---|---|---|
0001 | T恤衫 | 衣服 | 1000 | 500 | 2018-12-24 |
0002 | 打孔器 | 办公用品 | 500 | 320 | 2018-12-12 |
0003 | 运动T恤 | 衣服 | 4000 | 2800 | |
0004 | 菜刀 | 厨房用具 | 3000 | 2800 | 2018-12-24 |
0005 | 高压锅 | 厨房用具 | 6800 | 5000 | 2018-06-24 |
0006 | 叉子 | 厨房用具 | 500 | 2018-12-24 | |
0007 | 擦菜板 | 厨房用具 | 880 | 790 | 2018-04-01 |
0008 | 圆珠笔 | 办公用品 | 100 | 2018-10-01 |
- 商店商品表(TenpoShoin)
商店(tenpo_id) | 商店名称(tenpomei) | 商品编号(shopinid) | 数量(suryo) |
---|---|---|---|
000A | 北京商店杂货铺 | 0001 | 50 |
000A | 北京商店杂货铺 | 0002 | 50 |
000A | 北京商店杂货铺 | 0003 | 50 |
000A | 北京商店杂货铺 | 0004 | 50 |
000B | 南京商店杂货铺 | 0004 | 50 |
000C | 上海商店杂货铺 | 0004 | 50 |
从上面两张表里,我们发现都存在有相同的列--商品编号,
所谓联结运算,就是以商品编号为桥梁,将满足条件的汇集到同一结果之中,比如,我们想从Shopin表中取出商品名称(shopin_mei)和销售单价(hanbai_tanka),与TenpoShopin表中的内容进行结合
执行结果,只写了一条,剩下的都省略了
tenpo_id | tenpomei | shopinid | shopin_mei | hanbai_tanka |
---|---|---|---|---|
000A | 北京商店杂货铺 | 0001 | T恤衫 | 500 |
SELECT TS.tenpo_id,TS.tenpomei,TS.shopinid,S.shopin_mei,S.hanbai_tanka FROM TenpoShoin AS TS
INNER JOIN Shopin AS S
ON TS.shopinid = S.shopinid
- 1.内联结要点1--FROM子句 第一点要注意的是,之前的FROM子句中只有一张表,而这次我们同时使用了TenpoShoin和Shopin两张表
FROM TenpoShoin AS TS INNER JOIN Shopin AS S
使用关键字INNER JOIN就可以使两张表联结在一起了,
- 2.内联结要点2--ON子句 第二点要注意的是ON后面所记载的联结条件
ON TS.shopinid = S.shopinid
我们可以在ON之后指定两张表联结所使用的列(联结键).本例子中使用的是商品的编号,也就是说,ON是专门用来指定联结条件的,它能起到与WHERE相同的作用,需要指定多个键时,同样可以使用AND OR,ON子句在进行内联结时是必不可少的(没有ON会发生错误),并且ON必须书写在FROM和WHERE之间
- 3.内联结要点3--SELECT子句 第三个要注意的是,在SELECT子句中指定的列
SELECT TS.tenpo_id,TS.tenpomei,TS.shopinid,S.shopin_mei,S.hanbai_tanka
在SELECT中,像TS.tenpo_id和S.hanbai_tanka这样<表的别名>.<列名>的形式指定列,这和使用一张表时的情况不同,由于多表联结,所以要使用别名,以免发生混淆,,所以使用联结时SELECT子句的列需按照<表的别名>.<列名>的格式来书写SELECT子句中的全部的列
-
内联结和WHERE子句混合使用
SELECT TS.tenpo_id,TS.tenpomei,TS.shopinid,S.shopin_mei,S.hanbai_tanka FROM TenpoShoin AS TS
INNER JOIN Shopin AS S
ON TS.shopinid = S.shopinid
WHERE TS.tenpo_id = '000A'
这个是查询北京杂货店的在售商品
外联结--OUTER JOIN
内联结之外比较重要的就是外联结了,我们再来回顾下前面的例子,我们将Shopin表和TenpoShopin表进行内联结,从两张表中取出各个商店销售的商品信息,其中,实现"读取两张表信息"的就是联结功能
外联结也是通过ON子句使用联结键将两张表进行联结,同时从两张表中选取相应的列,基本的使用方法并没有发生改变,只是结果却有所不同,还是让我们看看外联结的sql语句吧
SELECT TS.tenpo_id,TS.tenpomei,TS.shopinid,S.shopin_mei,S.hanbai_tanka FROM TenpoShoin AS TS
RIGHT OUTER JOIN Shopin AS S
ON TS.shopinid = S.shopinid
执行结果就不展示了,有兴趣的小伙伴可以去实验下,执行结果是多出了几条内联结时不存在的数据
- 外联结要点①---选取出单张表中全部的信息
与内联结相比,不同点显而易见,那就是结果的行数不一样,那多出来的记录到底是为什么呢
这正是外联结的关键点,只要数据存在于某一张表中就能够读取出来,外联结的名称的由来也跟NULL有关,所谓外部,也就是包含元表中不存在的信息,相反,只包含表内的联结也成为内联结
- 外联结要②---每张表都是主表吗
外联结还有一点非常重要,那就是那张表作为主表,最终的结果中会包含主表内所有的数据,指定表的关键字是LEFT和RIGHT.如其名称所示,使用LEFT时FROM子句中左侧的表就是主表,使用RIGHT时右侧的表就是主表
大家可能会犹豫到底应该使用LEFT还是使用RIGHT呢,其实他们的功能没有任何区别,使用哪一个都可以,通常,使用LEFT的情况会多一些,但并没有非使用不可的理由,使用RIGHT也没有问题
交叉联结--CROSS JOIN
交叉联结其实在实际应用中并不常见,语法上和内联结一样,但是结果有点麻烦,看下SQL语句吧
SELECT TS.tenpo_id,TS.tenpomei,TS.shopinid,S.shopin_mei,S.hanbai_tanka FROM TenpoShoin AS TS
CROSS JOIN Shopin AS S
执行了的小伙伴会惊奇的返现结果是很多的,其实执行结果就是两张表的行数的乘积,
交叉联结就不介绍了,感兴趣的可以去百度下,在实际业务中没有应用到原因有两个,一是其结果没有实用价值,二是其结果行数太多,需要花费大量的运算时间和高性能设备的支持
如果你喜欢,可以关注我的公众号