DataWhale组队 SQL—Day4集合运算

本文深入讲解SQL中的表运算,包括表的加减法、不同类型的连接操作及其应用场景,并通过实例展示了如何利用SQL解决实际问题。

1 表的加减法

1.1 知识点梳理

在这里插入图片描述

1.2 练习题

1.2.1 找出 product 和 product2 中售价高于 500 的商品的基本信息。

--UNION
SELECT *
  FROM product
WHERE sale_price > 500
UNION
SELECT *
  FROM product2
WHERE  sale_price > 500;
  

1.2.2 商店决定对product表中利润低于50%和售价低于1000的商品提价, 请使用UNION ALL 语句将分别满足上述两个条件的结果取并集。

--UNION ALL
SELECT *
  FROM product
WHERE sale_price<1000
UNION ALL
SELECT *
  FROM product
WHERE sale_price<1.5 * purchase_price;

1.2.3 使用 SYSDATE()函数可以返回当前日期时间, 是一个日期时间类型的数据, 试测试该数据类型和数值,字符串等类型的兼容性.
例如, 以下代码可以正确执行, 说明时间日期类型和字符串,数值以及缺失值均能兼容。
在这里插入图片描述
运行结果:
在这里插入图片描述
1.2.4 使用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);

1.2.5 使用AND谓词查找product表中利润率高于50%,并且售价低于1500的商品。

SELECT *
  FROM product
WHERE sale_price<1500
AND sale_price>1.5*purchase_price;

1.2.6 使用product表和product2表的对称差来查询哪些商品只在其中一张表。

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);

1.2.7 借助上述对称差的实现方式, 求product和product2的交。

SELECT *
FROM product
--product和product2 的并集
WHERE product_id IN (SELECT product_id FROM product
UNION    SELECT product_id FROM product2)
--减去A-B和B-A的并集
AND product_id NOT IN( SELECT product_id FROM product
WHERE product_id NOT IN (SELECT product_id FROM  product2)
UNION  SELECT product_id FROM product2
WHERE product_id NOT IN (SELECT product_id FROM  product));

2 连结

2.1 知识点梳理

在这里插入图片描述

2.2 内连结 练习题

2.2.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 product_type="衣服"
--先选出衣服,再连结
SELECT  SP.shop_id, SP.shop_name, SP.product_id
       ,P.product_name, P.product_type, P.purchase_price
  FROM shopproduct AS SP 
  --从 product 表找出衣服类商品的信息
INNER JOIN 
  (SELECT product_id, product_name, product_type, purchase_price
     FROM product	
    WHERE product_type = '衣服')AS P 
   ON SP.product_id = P.product_id;

2.2.2 分别使用连结两个子查询和不使用子查询的方式, 找出东京商店里, 售价低于 2000 的商品信息,希望得到如下结果。
在这里插入图片描述

--使用子查询
SELECT *
FROM 
(SELECT *
FROM shopproduct
WHERE shop_name = '东京') AS SP
INNER JOIN
(SELECT *
FROM product
WHERE sale_price < 2000) AS P
ON SP.product_id = P.product_id;

--不用子查询
SELECT P.*,SP.*
FROM shopproduct AS SP
INNER JOIN product AS p
ON SP.product_id = P.product_id
WHERE shop_id ='000A'
AND sale_price<2000;

2.2.3 每个商店中, 售价最高的商品的售价分别是多少?

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;

**思考题:**上述查询得到了每个商品售价最高的商品, 但并不知道售价最高的商品是哪一个.如何获取每个商店里售价最高的商品的名称和售价?

2.2.4 每类商品中售价最高的商品都在哪些商店有售?

我再想想

2.2.5 分别使用内连结和关联子查询每一类商品中售价最高的商品。

--内连结
SELECT p1.product_id, p1.product_name, p1.product_type, p1.sale_price,p2.max_sale_price
FROM product AS p1
INNER JOIN
(SELECT product_type, MAX(sale_price) AS max_sale_price
FROM product
GROUP BY product_type) AS p2
ON p1.product_type=p2.product_type
AND p1.sale_price=p2.max_sale_price;
--关联子查询
SELECT product_type,product_name,sale_price
  FROM product AS p1
WHERE sale_price = (SELECT MAX(sale_price)
                      FROM product AS p2
                   WHERE p1.product_type = p2.product_type
                   GROUP BY product_type);

2.2.6 试写出与以下自然连结等价的内连结。
在这里插入图片描述

SELECT  *
  FROM shopproduct AS SP 
 INNER JOIN product AS P 
    ON SP.product_id = P.product_id;

2.2.7 使用内连结求 product 表和 product2 表的交集。

SELECT P1.*
  FROM product AS P1
 INNER JOIN product2 AS P2
    ON P1.product_id = P2.product_id

2.3 外连结 练习题

2.3.1 基本语法

-- 左连结     
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)>

2.3.2 练习题 使用外连结从shopproduct表和product表中找出那些在某个商店库存少于50的商品及对应的商店.希望得到如下结果.在这里插入图片描述注意高压锅和圆珠笔两种商品在所有商店都无货, 所以也应该包括在内。

SELECT P.product_id
      ,P.product_name
      ,P.sale_price
       ,SP.shop_id
      ,SP.shop_name
      ,SP.quantity 
  FROM product AS P
  LEFT OUTER JOIN-- 先筛选quantity<50的商品
   (SELECT *
      FROM shopproduct
     WHERE quantity < 50 ) AS SP
    ON SP.product_id = P.product_id

2.3.3 练习题:(非等值连结)

**希望对 product 表中的商品按照售价赋予排名。
一个从集合论出发,使用自左连结的思路:, 对每一种商品,找出售价不低于它的所有商品, 然后对售价不低于它的商品使用 COUNT 函数计数. 例如, 对于价格最高的商品。**注 1: COUNT 函数的参数是列名时, 会忽略该列中的缺失值, 参数为 * 时则不忽略缺失值.
注 2: 上述排名方案存在一些问题–如果两个商品的价格相等, 则会导致两个商品的排名错误, 例如, 叉子和打孔器的排名应该都是第六, 但上述查询导致二者排名都是第七. 试修改上述查询使得二者的排名均为第六.
注 3: 实际上, 进行排名有专门的函数, 这是 MySQL 8.0 新增加的窗口函数中的一种(窗口函数将在下一章学习), 但在较低版本的 MySQL 中只能使用上述自左连结的思路.

SELECT  product_id
       ,product_name
       ,sale_price
       ,COUNT(p2_id) AS rank
  FROM (--使用自左连结对每种商品找出价格不低于它的商品
        SELECT P1.product_id
               ,P1.product_name
               ,P1.sale_price
               ,P2.product_id AS P2_id
               ,P2.product_name AS P2_name
               ,P2.sale_price AS P2_price 
          FROM product AS P1 
          LEFT OUTER JOIN product AS P2 
            ON P1.sale_price <= P2.sale_price 
        ) AS X
 GROUP BY product_id, product_name, sale_price
 ORDER BY rank; 

2.3.4.1 练习题:
请按照商品的售价从低到高,对售价进行累计求和[注:这个案例缺少实际意义, 并且由于有两种商品价格相同导致了不必要的复杂度, 但示例数据库的表结构比较简单, 暂时未想出有实际意义的例题]

SELECT	product_id, product_name, sale_price
       ,SUM(P2_price) AS cum_price 
  FROM
        (SELECT  P1.product_id, P1.product_name, P1.sale_price
                ,P2.product_id AS P2_id
                ,P2.product_name AS P2_name
                ,P2.sale_price AS P2_price 
           FROM product AS P1 
           LEFT OUTER JOIN product AS P2 
             ON ((P1.sale_price > P2.sale_price)
             OR (P1.sale_price = P2.sale_price 
            AND P1.product_id<=P2.product_id))
	      ORDER BY P1.sale_price,P1.product_id) AS X
 GROUP BY product_id, product_name, sale_price
 ORDER BY sale_price,cum_price;

注意:考察我们建立自左连结的本意, 是要找出满足:1.比该商品售价更低的, 或者是 2.该种商品自身,以及 3.如果 A 和 B 两种商品售价相等,则建立连结时, 如果 P1.A 和 P2.A,P2.B 建立了连接, 则 P1.B 不再和 P2.A 建立连结。

2.3.4.2练习题:试将上述查询改用关联子查询实现.

SELECT  P1.product_id
       ,P1.product_name
       ,P1.sale_price,
     (SELECT  SUM(sale_price)
        FROM product AS p2 WHERE P1.sale_price > p2.sale_price
        OR (p1.sale_price = sale_price AND p1.product_id >=p2.product_id)
        ORDER BY p1.sale_price,p1.product_id) AS sale_sum
        FROM product AS p1
        ORDER BY sale_sum;
        

2.4 交叉连结—— CROSS JOIN(笛卡尔积)

2.4.1 基本语法

  • 两个集合做笛卡尔积:使集合 A 中的每一个元素与集合 B 中的每一个元素组成一个有序的组合;

  • 数据库表(或者子查询)的并,交和差都是在纵向上对表进行扩张或筛选限制等运算, 这要求表的列数及对应位置的列的数据类型"相容">这些运算并不会增加新的列。 而交叉连接(笛卡尔积)则是在横向上对表进行扩张,
    即增加新的列, 这一点和连结的功能一致;

  • 没有ON子句的限制, 会对左表和右表的每一行进行组合, 经常会导致很多无意义的行出现在检索结果中。

交叉连结语法
-- 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 (笛卡儿积)。进行交叉连结时无法使用内连结和外连结中所使用的ON 子句,这是因为交叉连结是对两张表中的全部记录进行交叉组合,因此结果中的记录数通常是两张表中行数的乘积。

2.4.2 连结与笛卡儿积的关系

  • 笛卡儿积可以视作一种特殊的连结(事实上笛卡儿积的语法也可以写作 CROSS JOIN), 这种连结的 ON 子句是一个恒为真的谓词;
  • 在对笛卡儿积进行适当的限制之后, 也就得到了内连结和外连结。
  • 虽然对笛卡尔积进行适当限制后会得到相同结果,仍不建议使用,主要原因如下:无法马上判断出什么种类的连结;无法短时间内分辨出连结条件和限制条件;语法过时。

3 小结

这块内容太多了,昨天又被导师喊去耽误了一天。本小白熬夜赶工也没能完全掌握,笔记留着有空再整理整理吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值