4.1 表的加减法
4.1.1 什么是集合运算
4.1.2 表的加法—UNION
4.1.2.1 UNION
UNION相当于是求并集的运算,可以对两张表,也可以对同一张表~~
UNION会除去重复的记录!!!
#两张表
SELECT product_id, product_name
FROM product
UNION
SELECT product_id, product_name
FROM product2;
同一张表~~
练习题:假设连锁店想要增加毛利率超过 50%或者售价低于 800 的货物的存货量, 请使用 UNION 对分别满足上述两个条件的商品的查询结果求并集.
SELECT product_id,product_name,product_type
,sale_price,purchase_price
FROM product
WHERE sale_price<800
UNION
SELECT product_id,product_name,product_type
,sale_price,purchase_price
FROM product
WHERE sale_price>1.5*purchase_price;
不使用UNION
SELECT product_id,product_name,product_type
,sale_price,purchase_price
FROM product
WHERE sale_price < 800
OR sale_price > 1.5 * purchase_price;
4.1.2.2 UNION 与 OR 谓词
确实, 对于同一个表的两个不同的筛选结果集, 使用 UNION 对两个结果集取并集, 和把两个子查询的筛选条件用 OR 谓词连接, 会得到相同的结果, 但倘若要将两个不同的表中的结果合并在一起, 就不得不使用 UNION 了,而且, 即便是对于同一张表, 有时也会出于查询效率方面的因素来使用 UNION。
练习题:分别使用 UNION 或者 OR 谓词,找出毛利率不足 30%或毛利率未知的商品.
-- 使用 OR 谓词
SELECT *
FROM product
WHERE sale_price / purchase_price < 1.3
OR sale_price / purchase_price IS NULL;
-- 使用 UNION
SELECT *
FROM product
WHERE sale_price / purchase_price < 1.3
UNION
SELECT *
FROM product
WHERE sale_price / purchase_price IS NULL;
4.1.2.3 包含重复行的集合运算 UNION ALL
union all可以保留重复行!!!用法同上union
4.1.2.4 bag 模型与 set模型
是否允许元素重复导致了 set 和 bag 的并交差等运算都存在一些区别. 以 bag 的交为例, 由于 bag 允许元素重复出现, 对于两个 bag, 他们的并运算会按照: 1.该元素是否至少在一个 bag 里出现过, 2.该元素在两个 bag 中的最大出现次数 这两个方面来进行计算. 因此对于 A = {1,1,1,2,3,5,7}, B = {1,1,2,2,4,6,8} 两个 bag, 它们的并就等于 {1,1,1,2,2,3,4,5,6,7,8}
4.1.2.5 隐式类型转换
通常来说, 我们会把类型完全一致, 并且代表相同属性的列使用 UNION 合并到一起显示, 但有时候, 即使数据类型不完全相同, 也会通过隐式类型转换来将两个类型不同的列放在一列里显示,时间日期类型和字符串,数值以及缺失值均能兼容。
SYSDATE()函数可以返回当前日期时间
4.1.3 MySQL 8.0 不支持交运算INTERSECT
集合的交也就是两个集合的公共部分
4.1.3.1 bag的交运算
对于两个 bag, 他们的交运算会按照: 1.该元素是否同时属于两个 bag, 2.该元素在两个 bag 中的最小出现次数这两个方面来进行计算. 因此对于 A = {1,1,1,2,3,5,7}, B = {1,1,2,2,4,6,8} 两个 bag, 它们的交运算结果就等于 {1,1,2}
4.1.4 差集,补集与表的减法
4.1.4.1 MySQL 8.0 不支持EXCEPT运算
可用 not in 来实现except的功能
4.1.4.2 EXCEPT 与 NOT 谓词
练习题: 使用NOT谓词进行集合的减法运算, 求出product表中, 售价高于2000,但利润低于30%的商品, 结果应该如下表所示.
SELECT *
FROM product
WHERE sale_price > 2000
AND product_id NOT IN (SELECT product_id
FROM product
WHERE sale_price >= 1.3*purchase_price)
4.1.4.3 EXCEPT ALL 与 bag 的差
对于两个 bag, 他们的差运算会按照:
1.该元素是否属于作为被减数的 bag,
2.该元素在两个 bag 中的出现次数
这两个方面来进行计算. 只有属于被减数的bag的元素才参与EXCEP ALL运算, 并且差bag中的次数,等于该元素在两个bag的出现次数之差(差为零或负数则不出现). 因此对于 A = {1,1,1,2,3,5,7}, B = {1,1,2,2,4,6,8} 两个 bag, 它们的差就等于 {1,3,5,7}.
4.1.4.4 IN TERSECT 与 AND 谓词
对于同一个表的两个查询结果而言, 他们的交INTERSECT实际上可以等价地将两个查询的检索条件用AND谓词连接来实现
4.1.5 对称差
两个集合的对称差等于 A-B并上B-A, 因此实践中可以用这个思路来求对称差
练习题:使用product表和product2表的对称差来查询哪些商品只在其中一张表, 结果类似于:
-- 使用 NOT IN 实现两个表的差集
SELECT *
FROM product
WHERE product_id NOT IN (SELECT product_id FROM product2)
UNION
SELECT *
FROM product2
WHERE product_id NOT IN (SELECT product_id FROM product)
4.2 连结(JOIN)
4.2.1 内连结(INNER JOIN)
-- 内连结
FROM <tb_1> INNER JOIN <tb_2> ON <condition(s)>
4.2.1.1 使用内连结从两个表获取信息
关于内连结,需要注意以下三点:
要点一: 进行连结时需要在 FROM 子句中使用多张表.
要点二:必须使用 ON 子句来指定连结条件.
ON 子句是专门用来指定连结条件的, 我们在上述查询的 ON 之后指定两张表连结所使用的列以及比较条件, 基本上, 它能起到与 WHERE 相同的筛选作用。
要点三: SELECT 子句中的列最好按照 表名.列名 的格式来使用.
4.2.1.2 结合 WHERE 子句使用内连结
如果需要在使用内连结的时候同时使用 WHERE 子句对检索结果进行筛选, 则需要把 WHERE 子句写在 ON 子句的后边
查询的执行顺序:
FROM 子句->WHERE 子句->SELECT 子句
也就是说, 两张表是先按照连结列进行了连结, 得到了一张新表, 然后 WHERE 子句对这张新表的行按照两个条件进行了筛选, 最后, SELECT 子句选出了那些我们需要的列.
练习题:
找出每个商店里的衣服类商品的名称及价格等信息. 希望得到如下结果
-- 参考答案 1--不使用子查询
SELECT SP.shop_id,SP.shop_name,SP.product_id
,P.product_name, P.product_type, P.purchase_price
FROM shopproduct AS SP
INNER JOIN product AS P
ON SP.product_id = P.product_id
WHERE P.product_type = '衣服';
-- 参考答案 2--使用子查询
SELECT SP.shop_id, SP.shop_name, SP.product_id
,P.product_name, P.product_type, P.purchase_price
FROM shopproduct AS SP
INNER JOIN --从 product 表找出衣服类商品的信息
(SELECT product_id, product_name, product_type, purchase_price
FROM product
WHERE product_type = '衣服')AS P
ON SP.product_id = P.product_id;
4.2.1.3 结合GROUP BY 子句使用内连结
练习题:
每个商店中, 售价最高的商品的售价分别是多少?
SELECT SP.shop_id
,SP.shop_name
,MAX(P.sale_price) AS max_price
FROM shopproduct AS SP
INNER JOIN product AS P
ON SP.product_id = P.product_id
GROUP BY SP.shop_id,SP.shop_name
4.2.1.4 自连结(SELF JOIN)
需要注意, 自连结并不是区分于内连结和外连结的第三种连结, 自连结可以是外连结也可以是内连结, 它是不同于内连结外连结的另一个连结的分类方法.
找出每个商品种类当中售价高于该类商品的平均售价的商品.
-- 关联子查询
SELECT product_type, product_name, sale_price
FROM product AS P1
WHERE sale_price > (SELECT AVG(sale_price)
FROM product AS P2
WHERE P1.product_type = P2.product_type
GROUP BY product_type);
首先, 使用 GROUP BY 按商品类别分类计算每类商品的平均价格;接下来, 将上述查询与表 product 按照 product_type (商品种类)进行内连结.最后, 增加 WHERE 子句, 找出那些售价高于该类商品平均价格的商品。
-- 内连结
SELECT P1.product_id
,P1.product_name
,P1.product_type
,P1.sale_price
,P2.avg_price
FROM product AS P1
INNER JOIN
(SELECT product_type,AVG(sale_price) AS avg_price
FROM product
GROUP BY product_type) AS P2
ON P1.product_type = P2.product_type
WHERE P1.sale_price > P2.avg_price;
4.2.1.6 自然连结(NATURAL JOIN)
自然连结并不是区别于内连结和外连结的第三种连结, 它其实是内连结的一种特例–当两个表进行自然连结时, 会按照两个表中都包含的列名来进行等值内连结, 此时无需使用 ON 来指定连接条件.
SELECT * FROM shopproduct NATURAL JOIN product
上述查询得到的结果, 会把两个表的公共列放在第一列(或者很多公共列), 然后按照两个表的顺序和表中列的顺序, 将两个表中的其他列都罗列出来。
4.2.2 外连结(OUTER JOIN)
左连结会保存左表中无法按照 ON 子句匹配到的行, 此时对应右表的行均为缺失值; 右连结则会保存右表中无法按照 ON 子句匹配到的行, 此时对应左表的行均为缺失值; 而全外连结则会同时保存两个表中无法按照 ON子句匹配到的行, 相应的另一张表中的行用缺失值填充.
-- 左连结
FROM <tb_1> LEFT OUTER JOIN <tb_2> ON <condition(s)>
-- 右连结
FROM <tb_1> RIGHT OUTER JOIN <tb_2> ON <condition(s)>
-- 全外连结
FROM <tb_1> FULL OUTER JOIN <tb_2> ON <condition(s)>
4.2.2.2 使用左连结从两个表获取信息
●外连结要点 1: 选取出单张表中全部的信息
●外连结要点 2:使用 LEFT、RIGHT 来指定主表.
一定要注意那些为null的值!!!
4.2.2.4 全外连结
MySQL 8.0目前还不支持全外连结,不过我们可以通过对左连结和右连结的结果进行UNION来实现全外连结。
也可以对多表进行连结
也可以实现非等值连结
4.2.5 交叉连结— CROSS JOIN(笛卡尔积)
交叉连结又叫笛卡尔积, 后者是一个数学术语. 两个集合做笛卡尔积, 就是使用集合 A 中的每一个元素与集合 B 中的每一个元素组成一个有序的组合。
-- 1.使用关键字 CROSS JOIN 显式地进行交叉连结
SELECT SP.shop_id
,SP.shop_name
,SP.product_id
,P.product_name
,P.sale_price
FROM shopproduct AS SP
CROSS JOIN product AS P;
--2.使用逗号分隔两个表,并省略 ON 子句
SELECT SP.shop_id
,SP.shop_name
,SP.product_id
,P.product_name
,P.sale_price
FROM shopproduct AS SP , product AS P;
实际上, 正如书中所说, 上述写法中, 将 CROSS JOIN 改为逗号后, 正是内连结的旧式写法, 但在 ANSI 和 ISO 的 SQL-92 标准中, 已经将使用 INNER JION …ON… 的写法规定为标准写法, 因此极力推荐大家在平时写 SQL 查询时, 使用规范写法.
参考
http://datawhale.club/t/topic/473