Task04:集合运算

教程地址

https://github.com/datawhalechina/wonderful-sql
https://gitee.com/datawhalechina/wonderful-sql

1. 表的加减法

表的加减法就是基于集合运算,想想交集、并集和差集

一般来说是,union、intersect、except

1.1 union

1.1.1 优先展示

union就是并集,将相同的列连接起来,默认是去重的

我之前在笔试的时候见过一个类型的,是说要优先展示符合某一个条件的数据

例如我们选择优先展示售价小于 800 的商品

mysql> SELECT product_name,sale_price
    ->   FROM product
    ->  WHERE sale_price<800
    ->   
    ->  UNION
    ->  
    -> SELECT product_name,sale_price
    ->   FROM product
    ->  WHERE sale_price>=800
    -> ;
+--------------+------------+
| product_name | sale_price |
+--------------+------------+
| 打孔器       |        500 |
| 叉子         |        500 |
| T恤          |       1000 |
| 运动T恤      |       4000 |
| 菜刀         |       3000 |
| 高压锅       |       6800 |
| 擦菜板       |        880 |
| 圆珠笔       |       5000 |
+--------------+------------+
8 rows in set (0.00 sec)

当然也可以合并来自不同表的相同列

1.1.2 合并结果

使用 union,其实也类似于 or

即合并两个筛选条件所得到的结果

1.1.3 保留重复

上文说过默认是去重的,如果我们用 union all,那就是不去重的

mysql> SELECT product_id, product_name
    ->   FROM product
    ->  UNION ALL
    -> SELECT product_id, product_name
    ->   FROM product2;
+------------+--------------+
| product_id | product_name |
+------------+--------------+
| 0001       | T恤          |
| 0002       | 打孔器       |
| 0003       | 运动T恤      |
| 0004       | 菜刀         |
| 0005       | 高压锅       |
| 0006       | 叉子         |
| 0007       | 擦菜板       |
| 0008       | 圆珠笔       |
| 0001       | T恤          |
| 0002       | 打孔器       |
| 0003       | 运动T恤      |
| 0009       | 手套         |
| 0010       | 水壶         |
+------------+--------------+
13 rows in set (0.00 sec)

mysql> 

1.1.4 类型转换

我们在合并数据时,同一列的数据类型一定是相同的

但是它们来自不同的数据源,所以可能存在隐式转换

需要注意的是 hive中进行join关联时,关联列要避免使用隐式数据类型转换,否则容易导致数据倾斜

1.2 intersect

intersect 其实就是求交集,但是 mysql 目前是不支持的

我们需要用内连接来实现这个操作

mysql> SELECT p1.product_id, p1.product_name
    ->   FROM product AS p1 
    ->     INNER JOIN product2 AS p2
    ->       ON p1.product_id=p2.product_id
    -> ;
+------------+--------------+
| product_id | product_name |
+------------+--------------+
| 0001       | T恤          |
| 0002       | 打孔器       |
| 0003       | 运动T恤      |
+------------+--------------+
3 rows in set (0.00 sec)

以 product_id 为关键字,取相同的部分

1.3 except

这个差集,mysql 目前也是不支持的

我们也需要用其他方式实现

例如用 not in,选出不在子查询当中的记录

也就是,外查询减去子查询

mysql> SELECT * 
    ->   FROM product
    ->   WHERE product_id NOT IN (
    ->     SELECT product_id FROM product2)
    -> ;
+------------+--------------+--------------+------------+----------------+-------------+
| product_id | product_name | product_type | sale_price | purchase_price | regist_date |
+------------+--------------+--------------+------------+----------------+-------------+
| 0004       | 菜刀         | 厨房用具     |       3000 |           2800 | 2009-09-20  |
| 0005       | 高压锅       | 厨房用具     |       6800 |           5000 | 2009-01-15  |
| 0006       | 叉子         | 厨房用具     |        500 |           NULL | 2009-09-20  |
| 0007       | 擦菜板       | 厨房用具     |        880 |            790 | 2008-04-28  |
| 0008       | 圆珠笔       | 办公用品     |       5000 |           NULL | 2009-11-11  |
+------------+--------------+--------------+------------+----------------+-------------+
5 rows in set (0.04 sec)

1.4 对称差

这个就神奇一点,指的是两个集合各自减去交集的部分

同样,mysql 并不支持直接的语法,我们需要用其他办法实现

直觉一点的说,我们将两个集合各自减去交集,再合并就行

但是这样需要先求交集,在这里我们可以直接减去另一个集合

相当于减去了交集,实现的效果基本是一致的

mysql> 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)
    -> ;
+------------+--------------+--------------+------------+----------------+-------------+
| product_id | product_name | product_type | sale_price | purchase_price | regist_date |
+------------+--------------+--------------+------------+----------------+-------------+
| 0004       | 菜刀         | 厨房用具     |       3000 |           2800 | 2009-09-20  |
| 0005       | 高压锅       | 厨房用具     |       6800 |           5000 | 2009-01-15  |
| 0006       | 叉子         | 厨房用具     |        500 |           NULL | 2009-09-20  |
| 0007       | 擦菜板       | 厨房用具     |        880 |            790 | 2008-04-28  |
| 0008       | 圆珠笔       | 办公用品     |       5000 |           NULL | 2009-11-11  |
| 0009       | 手套         | 衣服         |        800 |            500 | NULL        |
| 0010       | 水壶         | 厨房用具     |       2000 |           1700 | 2009-09-20  |
+------------+--------------+--------------+------------+----------------+-------------+
7 rows in set (0.01 sec)

2. 连结

2.1 内连结

挺神奇的,我看其他地方不都是,连接,我也习惯称之为连接

内连接就是根据关键字,取交集部分,合并两个表的列

2.1.1 普通连接

mysql> SELECT SP.shop_id
    ->        ,SP.shop_name
    ->        ,SP.product_id
    ->        ,P.product_name
    ->        ,P.product_type
    ->        ,P.sale_price
    ->        ,SP.quantity
    ->   FROM shopproduct AS SP
    ->   INNER JOIN product AS P
    ->     ON SP.product_id = P.product_id
    -> ;
+---------+-----------+------------+--------------+--------------+------------+----------+
| shop_id | shop_name | product_id | product_name | product_type | sale_price | quantity |
+---------+-----------+------------+--------------+--------------+------------+----------+
| 000A    | 东京      | 0001       | T恤          | 衣服         |       1000 |       30 |
| 000A    | 东京      | 0002       | 打孔器       | 办公用品     |        500 |       50 |
| 000A    | 东京      | 0003       | 运动T恤      | 衣服         |       4000 |       15 |
| 000B    | 名古屋    | 0002       | 打孔器       | 办公用品     |        500 |       30 |
| 000B    | 名古屋    | 0003       | 运动T恤      | 衣服         |       4000 |      120 |
| 000B    | 名古屋    | 0004       | 菜刀         | 厨房用具     |       3000 |       20 |
| 000B    | 名古屋    | 0006       | 叉子         | 厨房用具     |        500 |       10 |
| 000B    | 名古屋    | 0007       | 擦菜板       | 厨房用具     |        880 |       40 |
| 000C    | 大阪      | 0003       | 运动T恤      | 衣服         |       4000 |       20 |
| 000C    | 大阪      | 0004       | 菜刀         | 厨房用具     |       3000 |       50 |
| 000C    | 大阪      | 0006       | 叉子         | 厨房用具     |        500 |       90 |
| 000C    | 大阪      | 0007       | 擦菜板       | 厨房用具     |        880 |       70 |
| 000D    | 福冈      | 0001       | T恤          | 衣服         |       1000 |      100 |
+---------+-----------+------------+--------------+--------------+------------+----------+
13 rows in set (0.01 sec)

我们需要注意的是,连接多张表的时候,最好在选列的时候加上表名的前缀

还有一个问题就是,最好给表起别名,因为原名一般都很长

然后就是这个和关联子查询的一些区别的,应该是比子查询效率高的

因为关联子查询更像是,外查询每遇到一个记录,就去内查询的结果里面找

2.1.2 结合 where

mysql> SELECT *
    ->   FROM (-- 第一步查询的结果
    ->         SELECT SP.shop_id
    ->                ,SP.shop_name
    ->                ,SP.product_id
    ->                ,P.product_name
    ->                ,P.product_type
    ->                ,P.sale_price
    ->                ,SP.quantity
    ->           FROM shopproduct AS SP
    ->             INNER JOIN product AS P
    ->               ON SP.product_id = P.product_id) AS STEP1
    ->   WHERE shop_name = '东京'
    ->    AND product_type = '衣服' ;
+---------+-----------+------------+--------------+--------------+------------+----------+
| shop_id | shop_name | product_id | product_name | product_type | sale_price | quantity |
+---------+-----------+------------+--------------+--------------+------------+----------+
| 000A    | 东京      | 0001       | T恤          | 衣服         |       1000 |       30 |
| 000A    | 东京      | 0003       | 运动T恤      | 衣服         |       4000 |       15 |
+---------+-----------+------------+--------------+--------------+------------+----------+
2 rows in set (0.00 sec)

这个感觉也就是先连接表,然后对结果进行了一个筛选

当然我们也可以选择,先对表进行筛选,然后再连接,例如:

mysql> SELECT SP.shop_id
    ->        ,SP.shop_name
    ->        ,SP.product_id
    ->        ,P.product_name
    ->        ,P.product_type
    ->        ,P.sale_price
    ->        ,SP.quantity
    -> 
    ->     FROM (-- 子查询 1:从 ShopProduct 表筛选出东京商店的信息
    ->         SELECT *
    ->         FROM shopproduct
    ->         WHERE shop_name = '东京' ) AS SP
    -> 
    ->     INNER JOIN -- 子查询 2:从 Product 表筛选出衣服类商品的信息
    -> 
    ->       (SELECT *
    ->       FROM product
    ->       WHERE product_type = '衣服') AS P
    -> 
    ->     ON SP.product_id = P.product_id;
+---------+-----------+------------+--------------+--------------+------------+----------+
| shop_id | shop_name | product_id | product_name | product_type | sale_price | quantity |
+---------+-----------+------------+--------------+--------------+------------+----------+
| 000A    | 东京      | 0001       | T恤          | 衣服         |       1000 |       30 |
| 000A    | 东京      | 0003       | 运动T恤      | 衣服         |       4000 |       15 |
+---------+-----------+------------+--------------+--------------+------------+----------+
2 rows in set (0.00 sec)

在这中间可能会有报错:

Display all 880 possibilities? (y or n)

这是因为混入了 tab,应该全部改为空格

2.1.3 结合 group by

感觉跟 where 差不多,先连接,再筛选

mysql> 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
    -> ;
+---------+-----------+-----------+
| shop_id | shop_name | max_price |
+---------+-----------+-----------+
| 000A    | 东京      |      4000 |
| 000B    | 名古屋    |      4000 |
| 000C    | 大阪      |      4000 |
| 000D    | 福冈      |      1000 |
+---------+-----------+-----------+
4 rows in set (0.00 sec)

然后是那个思考题,意思是,现在只有每个商店里面的最高售价

但是不知道这个最高售价是那一个商品

我感觉可能要逐个求每个商店的最高售价及其商品,然后再用并集连接

2.1.4 自然连接

这个也就是,NATURAL JOIN,不用指定 on,自然指定关键字

2.2 外连结

外连接可能类似于求并集,但是又不一样

左连接是以左边的为基准,右边的合并入左边,右连接则相反

2.2.1 左连接

简简单单,根据 product_id 并入左表

mysql> SELECT SP.shop_id
    ->        ,SP.shop_name
    ->        ,SP.product_id
    ->        ,P.product_name
    ->        ,P.sale_price
    ->   FROM product AS P
    ->   LEFT OUTER JOIN shopproduct AS SP
    ->     ON SP.product_id = P.product_id;
+---------+-----------+------------+--------------+------------+
| shop_id | shop_name | product_id | product_name | sale_price |
+---------+-----------+------------+--------------+------------+
| 000D    | 福冈      | 0001       | T恤          |       1000 |
| 000A    | 东京      | 0001       | T恤          |       1000 |
| 000B    | 名古屋    | 0002       | 打孔器       |        500 |
| 000A    | 东京      | 0002       | 打孔器       |        500 |
| 000C    | 大阪      | 0003       | 运动T恤      |       4000 |
| 000B    | 名古屋    | 0003       | 运动T恤      |       4000 |
| 000A    | 东京      | 0003       | 运动T恤      |       4000 |
| 000C    | 大阪      | 0004       | 菜刀         |       3000 |
| 000B    | 名古屋    | 0004       | 菜刀         |       3000 |
| NULL    | NULL      | NULL       | 高压锅       |       6800 |
| 000C    | 大阪      | 0006       | 叉子         |        500 |
| 000B    | 名古屋    | 0006       | 叉子         |        500 |
| 000C    | 大阪      | 0007       | 擦菜板       |        880 |
| 000B    | 名古屋    | 0007       | 擦菜板       |        880 |
| NULL    | NULL      | NULL       | 圆珠笔       |       5000 |
+---------+-----------+------------+--------------+------------+
15 rows in set (0.00 sec)
mysql> select * from product;
+------------+--------------+--------------+------------+----------------+-------------+
| product_id | product_name | product_type | sale_price | purchase_price | regist_date |
+------------+--------------+--------------+------------+----------------+-------------+
| 0001       | T恤          | 衣服         |       1000 |            500 | 2009-09-20  |
| 0002       | 打孔器       | 办公用品     |        500 |            320 | 2009-09-11  |
| 0003       | 运动T恤      | 衣服         |       4000 |           2800 | NULL        |
| 0004       | 菜刀         | 厨房用具     |       3000 |           2800 | 2009-09-20  |
| 0005       | 高压锅       | 厨房用具     |       6800 |           5000 | 2009-01-15  |
| 0006       | 叉子         | 厨房用具     |        500 |           NULL | 2009-09-20  |
| 0007       | 擦菜板       | 厨房用具     |        880 |            790 | 2008-04-28  |
| 0008       | 圆珠笔       | 办公用品     |       5000 |           NULL | 2009-11-11  |
+------------+--------------+--------------+------------+----------------+-------------+
8 rows in set (0.00 sec)

mysql> select * from shopproduct;
+---------+-----------+------------+----------+
| shop_id | shop_name | product_id | quantity |
+---------+-----------+------------+----------+
| 000A    | 东京      | 0001       |       30 |
| 000A    | 东京      | 0002       |       50 |
| 000A    | 东京      | 0003       |       15 |
| 000B    | 名古屋    | 0002       |       30 |
| 000B    | 名古屋    | 0003       |      120 |
| 000B    | 名古屋    | 0004       |       20 |
| 000B    | 名古屋    | 0006       |       10 |
| 000B    | 名古屋    | 0007       |       40 |
| 000C    | 大阪      | 0003       |       20 |
| 000C    | 大阪      | 0004       |       50 |
| 000C    | 大阪      | 0006       |       90 |
| 000C    | 大阪      | 0007       |       70 |
| 000D    | 福冈      | 0001       |      100 |
+---------+-----------+------------+----------+
13 rows in set (0.00 sec)

我们可以看到,左表的内容的得到保留的,例如高压锅和圆珠笔

这两个东西,是只在左边当中的,但是它们的关键字,product_id,没有保留

2.2.2 结合 where

这个没啥好说的,就先连表,再筛选

2.2.3 全外连接

outer join,左右表都保留,但是 mysql 目前不支持

2.3 多表连接

我们已经有表了

mysql> select * from inventoryproduct;
+--------------+------------+--------------------+
| inventory_id | product_id | inventory_quantity |
+--------------+------------+--------------------+
| P001         | 0001       |                  0 |
| P001         | 0002       |                120 |
| P001         | 0003       |                200 |
| P001         | 0004       |                  3 |
| P001         | 0005       |                  0 |
| P001         | 0006       |                 99 |
| P001         | 0007       |                999 |
| P001         | 0008       |                200 |
| P002         | 0001       |                 10 |
| P002         | 0002       |                 25 |
| P002         | 0003       |                 34 |
| P002         | 0004       |                 19 |
| P002         | 0005       |                 99 |
| P002         | 0006       |                  0 |
| P002         | 0007       |                  0 |
| P002         | 0008       |                 18 |
+--------------+------------+--------------------+
16 rows in set (0.00 sec)

2.3.1 多表内连接

查一下各个商店的各件商品的库存

mysql> SELECT SP.shop_id
    ->        ,SP.shop_name
    ->        ,SP.product_id
    ->        ,P.product_name
    ->        ,P.sale_price
    ->        ,IP.inventory_quantity
    ->   FROM shopproduct AS SP
    ->     INNER JOIN product AS P
    ->       ON SP.product_id = P.product_id
    ->     INNER JOIN inventoryproduct AS IP
    ->       ON SP.product_id = IP.product_id
    ->  WHERE IP.inventory_id = 'P001';
+---------+-----------+------------+--------------+------------+--------------------+
| shop_id | shop_name | product_id | product_name | sale_price | inventory_quantity |
+---------+-----------+------------+--------------+------------+--------------------+
| 000A    | 东京      | 0001       | T恤          |       1000 |                  0 |
| 000A    | 东京      | 0002       | 打孔器       |        500 |                120 |
| 000A    | 东京      | 0003       | 运动T恤      |       4000 |                200 |
| 000B    | 名古屋    | 0002       | 打孔器       |        500 |                120 |
| 000B    | 名古屋    | 0003       | 运动T恤      |       4000 |                200 |
| 000B    | 名古屋    | 0004       | 菜刀         |       3000 |                  3 |
| 000B    | 名古屋    | 0006       | 叉子         |        500 |                 99 |
| 000B    | 名古屋    | 0007       | 擦菜板       |        880 |                999 |
| 000C    | 大阪      | 0003       | 运动T恤      |       4000 |                200 |
| 000C    | 大阪      | 0004       | 菜刀         |       3000 |                  3 |
| 000C    | 大阪      | 0006       | 叉子         |        500 |                 99 |
| 000C    | 大阪      | 0007       | 擦菜板       |        880 |                999 |
| 000D    | 福冈      | 0001       | T恤          |       1000 |                  0 |
+---------+-----------+------------+--------------+------------+--------------------+
13 rows in set (0.00 sec)

同样是先连表,再筛选

2.3.1 多表外连接

换一换关键词,其他没啥区别

2.4 非等值连接

这大概意思是,要选出来比自己售价高的商品,然后计数,作为该商品的排名

mysql> SELECT  product_id
    ->         ,product_name
    ->         ,sale_price
    ->         ,COUNT(p2_id) AS my_rank
    ->         -- ,P2_id
    ->     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 my_rank
    -> ;
+------------+--------------+------------+---------+
| product_id | product_name | sale_price | my_rank |
+------------+--------------+------------+---------+
| 0005       | 高压锅       |       6800 |       1 |
| 0008       | 圆珠笔       |       5000 |       2 |
| 0003       | 运动T恤      |       4000 |       3 |
| 0004       | 菜刀         |       3000 |       4 |
| 0001       | T恤          |       1000 |       5 |
| 0007       | 擦菜板       |        880 |       6 |
| 0002       | 打孔器       |        500 |       8 |
| 0006       | 叉子         |        500 |       8 |
+------------+--------------+------------+---------+
8 rows in set (0.00 sec)

反正连接条件依赖于 on 指定的判断,调整为大于即可

然后例如说圆珠笔,它有两个比它售价高的商品

那么在中间的某个表上,它就与这两个商品的记录做连接

例如:

mysql> SELECT  product_id
    ->         ,product_name
    ->         ,sale_price
    ->         -- ,COUNT(p2_id) AS my_rank
    ->         ,P2_id
    ->     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 my_rank
    -> ;
+------------+--------------+------------+-------+
| product_id | product_name | sale_price | P2_id |
+------------+--------------+------------+-------+
| 0001       | T恤          |       1000 | 0008  |
| 0001       | T恤          |       1000 | 0005  |
| 0001       | T恤          |       1000 | 0004  |
| 0001       | T恤          |       1000 | 0003  |
| 0001       | T恤          |       1000 | 0001  |
| 0002       | 打孔器       |        500 | 0008  |
| 0002       | 打孔器       |        500 | 0007  |
| 0002       | 打孔器       |        500 | 0006  |
| 0002       | 打孔器       |        500 | 0005  |
| 0002       | 打孔器       |        500 | 0004  |
| 0002       | 打孔器       |        500 | 0003  |
| 0002       | 打孔器       |        500 | 0002  |
| 0002       | 打孔器       |        500 | 0001  |
| 0003       | 运动T恤      |       4000 | 0008  |
| 0003       | 运动T恤      |       4000 | 0005  |
| 0003       | 运动T恤      |       4000 | 0003  |
| 0004       | 菜刀         |       3000 | 0008  |
| 0004       | 菜刀         |       3000 | 0005  |
| 0004       | 菜刀         |       3000 | 0004  |
| 0004       | 菜刀         |       3000 | 0003  |
| 0005       | 高压锅       |       6800 | 0005  |
| 0006       | 叉子         |        500 | 0008  |
| 0006       | 叉子         |        500 | 0007  |
| 0006       | 叉子         |        500 | 0006  |
| 0006       | 叉子         |        500 | 0005  |
| 0006       | 叉子         |        500 | 0004  |
| 0006       | 叉子         |        500 | 0003  |
| 0006       | 叉子         |        500 | 0002  |
| 0006       | 叉子         |        500 | 0001  |
| 0007       | 擦菜板       |        880 | 0008  |
| 0007       | 擦菜板       |        880 | 0007  |
| 0007       | 擦菜板       |        880 | 0005  |
| 0007       | 擦菜板       |        880 | 0004  |
| 0007       | 擦菜板       |        880 | 0003  |
| 0007       | 擦菜板       |        880 | 0001  |
| 0008       | 圆珠笔       |       5000 | 0008  |
| 0008       | 圆珠笔       |       5000 | 0005  |
+------------+--------------+------------+-------+
37 rows in set (0.00 sec)

我们可以看到比圆珠笔高的是 8 号和 5 号商品

其分别与这两条记录做了连接

2.5 交叉连接

cross join,听起来挺像outer join,但是不一样

这个更多,匹配不匹配都做一遍连接

mysql> 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;
+---------+-----------+------------+--------------+------------+
| shop_id | shop_name | product_id | product_name | sale_price |
+---------+-----------+------------+--------------+------------+
| 000A    | 东京      | 0001       | 圆珠笔       |       5000 |
......
+---------+-----------+------------+--------------+------------+
104 rows in set (0.00 sec)

也可以选择隐式地连接

SELECT SP.shop_id
       ,SP.shop_name
       ,SP.product_id
       ,P.product_name
       ,P.sale_price
  FROM ShopProduct AS SP , Product AS P;

交叉连接又叫笛卡尔积,就是那个一一配对的

内连接和外连接,其实也就是对交叉连接的限制和过滤

2.6 语法问题

我们在上面提到过,交叉连接有隐式地方式

如果采用了这种方式,那代码可能会变得不便阅读

而且配合上 where 之后,更难判断结果是内连接还是外连接

A.练习题

A.1

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

mysql> SELECT *
    ->   FROM product
    ->  WHERE sale_price>500
    ->   
    ->  UNION
    ->  
    -> SELECT *
    ->   FROM product2
    ->  WHERE sale_price>500
    -> ;
+------------+--------------+--------------+------------+----------------+-------------+
| product_id | product_name | product_type | sale_price | purchase_price | regist_date |
+------------+--------------+--------------+------------+----------------+-------------+
| 0001       | T恤          | 衣服         |       1000 |            500 | 2009-09-20  |
| 0003       | 运动T恤      | 衣服         |       4000 |           2800 | NULL        |
| 0004       | 菜刀         | 厨房用具     |       3000 |           2800 | 2009-09-20  |
| 0005       | 高压锅       | 厨房用具     |       6800 |           5000 | 2009-01-15  |
| 0007       | 擦菜板       | 厨房用具     |        880 |            790 | 2008-04-28  |
| 0008       | 圆珠笔       | 办公用品     |       5000 |           NULL | 2009-11-11  |
| 0009       | 手套         | 衣服         |        800 |            500 | NULL        |
| 0010       | 水壶         | 厨房用具     |       2000 |           1700 | 2009-09-20  |
+------------+--------------+--------------+------------+----------------+-------------+
8 rows in set (0.01 sec)

A.2

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

mysql> SELECT *
    ->   FROM product
    ->  WHERE product_id IN (SELECT product_id FROM product2)
    ->   
    ->  UNION
    ->  
    -> SELECT *
    ->   FROM product2
    ->  WHERE product_id IN (SELECT product_id FROM product)
    -> ;
+------------+--------------+--------------+------------+----------------+-------------+
| product_id | product_name | product_type | sale_price | purchase_price | regist_date |
+------------+--------------+--------------+------------+----------------+-------------+
| 0001       | T恤          | 衣服         |       1000 |            500 | 2009-09-20  |
| 0002       | 打孔器       | 办公用品     |        500 |            320 | 2009-09-11  |
| 0003       | 运动T恤      | 衣服         |       4000 |           2800 | NULL        |
+------------+--------------+--------------+------------+----------------+-------------+
3 rows in set (0.00 sec)

A.3

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

mysql> SELECT p1.product_id, p1.product_name, p1.product_type, p1.sale_price, p2.max_sale_price, sp.shop_name
    -> -- SELECT *
    -> 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
    -> -- ;
    -> INNER JOIN 
    ->   shopproduct AS sp
    ->   ON sp.product_id = p1.product_id
    -> -- ;
    -> WHERE p1.sale_price = p2.max_sale_price
    -> ;
+------------+--------------+--------------+------------+----------------+-----------+
| product_id | product_name | product_type | sale_price | max_sale_price | shop_name |
+------------+--------------+--------------+------------+----------------+-----------+
| 0003       | 运动T恤      | 衣服         |       4000 |           4000 | 东京      |
| 0003       | 运动T恤      | 衣服         |       4000 |           4000 | 名古屋    |
| 0003       | 运动T恤      | 衣服         |       4000 |           4000 | 大阪      |
+------------+--------------+--------------+------------+----------------+-----------+
3 rows in set (0.01 sec)

这看起来可能有点奇怪,我们看一看其他情况:

mysql> -- SELECT p1.product_id, p1.product_name, p1.product_type, p1.sale_price, p2.max_sale_price, sp.shop_name
mysql> SELECT *
    -> 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
    -> -- ;
    -> -- INNER JOIN 
    ->   -- shopproduct AS sp
    ->   -- ON sp.product_id = p1.product_id
    -> -- ;
    -> WHERE p1.sale_price = p2.max_sale_price
    -> ;
+------------+--------------+--------------+------------+----------------+-------------+--------------+----------------+
| product_id | product_name | product_type | sale_price | purchase_price | regist_date | product_type | max_sale_price |
+------------+--------------+--------------+------------+----------------+-------------+--------------+----------------+
| 0003       | 运动T恤      | 衣服         |       4000 |           2800 | NULL        | 衣服         |           4000 |
| 0005       | 高压锅       | 厨房用具     |       6800 |           5000 | 2009-01-15  | 厨房用具     |           6800 |
| 0008       | 圆珠笔       | 办公用品     |       5000 |           NULL | 2009-11-11  | 办公用品     |           5000 |
+------------+--------------+--------------+------------+----------------+-------------+--------------+----------------+
3 rows in set (0.00 sec)

mysql> select * from shopproduct;
+---------+-----------+------------+----------+
| shop_id | shop_name | product_id | quantity |
+---------+-----------+------------+----------+
| 000A    | 东京      | 0001       |       30 |
| 000A    | 东京      | 0002       |       50 |
| 000A    | 东京      | 0003       |       15 |
| 000B    | 名古屋    | 0002       |       30 |
| 000B    | 名古屋    | 0003       |      120 |
| 000B    | 名古屋    | 0004       |       20 |
| 000B    | 名古屋    | 0006       |       10 |
| 000B    | 名古屋    | 0007       |       40 |
| 000C    | 大阪      | 0003       |       20 |
| 000C    | 大阪      | 0004       |       50 |
| 000C    | 大阪      | 0006       |       90 |
| 000C    | 大阪      | 0007       |       70 |
| 000D    | 福冈      | 0001       |      100 |
+---------+-----------+------------+----------+
13 rows in set (0.00 sec)

噢,另外两类售价最高的商品,没有商店卖,分别是 5 和 8

A.4

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

嗯,内连接的情况,好像上面已经做过了,先建一个子表,然后连起来

mysql> SELECT *
    -> 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
    -> WHERE p1.sale_price = p2.max_sale_price
    -> ;
+------------+--------------+--------------+------------+----------------+-------------+--------------+----------------+
| product_id | product_name | product_type | sale_price | purchase_price | regist_date | product_type | max_sale_price |
+------------+--------------+--------------+------------+----------------+-------------+--------------+----------------+
| 0003       | 运动T恤      | 衣服         |       4000 |           2800 | NULL        | 衣服         |           4000 |
| 0005       | 高压锅       | 厨房用具     |       6800 |           5000 | 2009-01-15  | 厨房用具     |           6800 |
| 0008       | 圆珠笔       | 办公用品     |       5000 |           NULL | 2009-11-11  | 办公用品     |           5000 |
+------------+--------------+--------------+------------+----------------+-------------+--------------+----------------+
3 rows in set (0.00 sec)

关联子查询如下:

mysql> SELECT *
    ->   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)
    -> ;
+------------+--------------+--------------+------------+----------------+-------------+
| product_id | product_name | product_type | sale_price | purchase_price | regist_date |
+------------+--------------+--------------+------------+----------------+-------------+
| 0003       | 运动T恤      | 衣服         |       4000 |           2800 | NULL        |
| 0005       | 高压锅       | 厨房用具     |       6800 |           5000 | 2009-01-15  |
| 0008       | 圆珠笔       | 办公用品     |       5000 |           NULL | 2009-11-11  |
+------------+--------------+--------------+------------+----------------+-------------+
3 rows in set (0.00 sec)

A.5

用关联子查询实现:在 product 表中,取出 product_id, product_name, sale_price, 并按照商品的售价从低到高进行排序、对售价进行累计求和。

mysql> SELECT p1.product_id, p1.product_name, p1.sale_price,
    ->   (
    ->   SELECT SUM(DISTINCT(CASE WHEN p2.sale_price < p1.sale_price THEN p2.sale_price ELSE 0 END)) + p1.sale_price
    ->   FROM product AS p2
    ->   -- WHERE p2.sale_price < p1.sale_price
    ->   ) AS cnt_price
    -> FROM product AS p1
    -> ORDER BY p1.sale_price
    -> ;
+------------+--------------+------------+-----------+
| product_id | product_name | sale_price | cnt_price |
+------------+--------------+------------+-----------+
| 0002       | 打孔器       |        500 |       500 |
| 0006       | 叉子         |        500 |       500 |
| 0007       | 擦菜板       |        880 |      1380 |
| 0001       | T恤          |       1000 |      2380 |
| 0004       | 菜刀         |       3000 |      5380 |
| 0003       | 运动T恤      |       4000 |      9380 |
| 0008       | 圆珠笔       |       5000 |     14380 |
| 0005       | 高压锅       |       6800 |     21180 |
+------------+--------------+------------+-----------+
8 rows in set (0.00 sec)

不太清楚这个累计是啥意思

我这里的方式是,累计小于等于该商品的价位,每个价位只计算一次

啊,我可能大概了解了这个意思,在学习窗口函数时

以上结果应该在第二行是不对的,它的累计值应该是 1000

我感觉在排序之后,应该新增一列序号列

然后再让每一行累计小于等于其序号的售价

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值