From c4d4c24ba276bc764b28d477cd0c5e50e6066e2e Mon Sep 17 00:00:00 2001 From: mba1398 <26516464+mba1398@users.noreply.github.com> Date: Wed, 7 Jul 2021 15:05:30 +0800 Subject: [PATCH] =?UTF-8?q?Update=20ch03:=20=E5=A4=8D=E6=9D=82=E4=B8=80?= =?UTF-8?q?=E7=82=B9=E7=9A=84=E6=9F=A5=E8=AF=A2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ch03: 复杂一点的查询.md | 680 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 679 insertions(+), 1 deletion(-) diff --git a/ch03: 复杂一点的查询.md b/ch03: 复杂一点的查询.md index 82ba02f..3084b64 100644 --- a/ch03: 复杂一点的查询.md +++ b/ch03: 复杂一点的查询.md @@ -747,4 +747,682 @@ COALESCE(NULL, NULL, '2020-11-01') AS col_3; ## 3.4.1 什么是谓词 -谓词就是 +谓词就是返回值为真值的函数。包括`TRUE / FALSE / UNKNOWN`。 + +谓词主要有以下几个: + +* LIKE +* BETWEEN +* IS NULL、IS NOT NULL +* IN +* EXISTS +## +## 3.4.2 LIKE谓词 -- 用于字符串的部分一致查询 + +当需要进行字符串的部分一致查询时需要使用该谓词。 + +部分一致大体可以分为前方一致、中间一致和后方一致三种类型。 + +首先我们来创建一张表 + +```sql +-- DDL :创建表 +CREATE TABLE samplelike +( strcol VARCHAR(6) NOT NULL, +PRIMARY KEY (strcol) +samplelike); +-- DML :插入数据 +START TRANSACTION; -- 开始事务 +INSERT INTO samplelike (strcol) VALUES ('abcddd'); +INSERT INTO samplelike (strcol) VALUES ('dddabc'); +INSERT INTO samplelike (strcol) VALUES ('abdddc'); +INSERT INTO samplelike (strcol) VALUES ('abcdd'); +INSERT INTO samplelike (strcol) VALUES ('ddabc'); +INSERT INTO samplelike (strcol) VALUES ('abddc'); +COMMIT; -- 提交事务 +SELECT * FROM samplelike; ++--------+ +| strcol | ++--------+ +| abcdd  | +| abcddd | +| abddc  | +| abdddc | +| ddabc  | +| dddabc | ++--------+ +6 rows in set (0.00 sec) +``` +* 前方一致:选取出“dddabc” + +前方一致即作为查询条件的字符串(这里是“ddd”)与查询对象字符串起始部分相同。 + +```sql +SELECT * +FROM samplelike +WHERE strcol LIKE 'ddd%'; ++--------+ +| strcol | ++--------+ +| dddabc | ++--------+ +1 row in set (0.00 sec) +``` +其中的`%`是代表“零个或多个任意字符串”的特殊符号,本例中代表“以ddd开头的所有字符串”。 +* 中间一致:选取出“abcddd”“dddabc”“abdddc” + +中间一致即查询对象字符串中含有作为查询条件的字符串,无论该字符串出现在对象字 + +符串的最后还是中间都没有关系。 + +```sql +SELECT * +FROM samplelike +WHERE strcol LIKE '%ddd%'; ++--------+ +| strcol | ++--------+ +| abcddd | +| abdddc | +| dddabc | ++--------+ +3 rows in set (0.00 sec) +``` +* 后方一致:选取出“abcddd“ + +后方一致即作为查询条件的字符串(这里是“ddd”)与查询对象字符串的末尾部分相同。 + +```sql +SELECT * +FROM samplelike +WHERE strcol LIKE '%ddd'; ++--------+ +| strcol | ++--------+ +| abcddd | ++--------+ +1 row in set (0.00 sec) +``` +综合如上三种类型的查询可以看出,查询条件最宽松,也就是能够取得最多记录的是`中间一致`。这是因为它同时包含前方一致和后方一致的查询结果。 +* `_`下划线匹配任意 1 个字符 + +使用 _(下划线)来代替 %,与 % 不同的是,它代表了“任意 1 个字符”。 + +```sql +SELECT * +FROM samplelike +WHERE strcol LIKE 'abc__'; ++--------+ +| strcol | ++--------+ +| abcdd  | ++--------+ +1 row in set (0.00 sec) +``` +## +## 3.4.3 BETWEEN谓词 -- 用于范围查询 + +使用 BETWEEN 可以进行范围查询。该谓词与其他谓词或者函数的不同之处在于它使用了 3 个参数。 + +```sql +-- 选取销售单价为100~ 1000元的商品 +SELECT product_name, sale_price +FROM product +WHERE sale_price BETWEEN 100 AND 1000; ++--------------+------------+ +| product_name | sale_price | ++--------------+------------+ +| T恤          |       1000 | +| 打孔器       |        500 | +| 叉子         |        500 | +| 擦菜板       |        880 | +| 圆珠笔       |        100 | ++--------------+------------+ +5 rows in set (0.00 sec) +``` +BETWEEN 的特点就是结果中会包含 100 和 1000 这两个临界值,也就是闭区间。如果不想让结果中包含临界值,那就必须使用 < 和 >。 +```sql +SELECT product_name, sale_price +FROM product +WHERE sale_price > 100 +AND sale_price < 1000; ++--------------+------------+ +| product_name | sale_price | ++--------------+------------+ +| 打孔器       |        500 | +| 叉子         |        500 | +| 擦菜板       |        880 | ++--------------+------------+ +3 rows in set (0.00 sec) +``` +## +## 3.4.4 IS NULL、 IS NOT NULL -- 用于判断是否为NULL + +为了选取出某些值为 NULL 的列的数据,不能使用 =,而只能使用特定的谓词IS NULL。 + +```sql +SELECT product_name, purchase_price +FROM product +WHERE purchase_price IS NULL; ++--------------+----------------+ +| product_name | purchase_price | ++--------------+----------------+ +| 叉子         |           NULL | +| 圆珠笔       |           NULL | ++--------------+----------------+ +2 rows in set (0.00 sec) +``` +与此相反,想要选取 NULL 以外的数据时,需要使用IS NOT NULL。 +```sql +SELECT product_name, purchase_price +FROM product +WHERE purchase_price IS NOT NULL; ++--------------+----------------+ +| product_name | purchase_price | ++--------------+----------------+ +| T恤          |            500 | +| 打孔器       |            320 | +| 运动T恤      |           2800 | +| 菜刀         |           2800 | +| 高压锅       |           5000 | +| 擦菜板       |            790 | ++--------------+----------------+ +6 rows in set (0.00 sec) +``` +## +## 3.4.5 IN谓词 -- OR的简便用法 + +多个查询条件取并集时可以选择使用`or`语句。 + +```sql +-- 通过OR指定多个进货单价进行查询 +SELECT product_name, purchase_price +FROM product +WHERE purchase_price = 320 +OR purchase_price = 500 +OR purchase_price = 5000; ++--------------+----------------+ +| product_name | purchase_price | ++--------------+----------------+ +| T恤          |            500 | +| 打孔器       |            320 | +| 高压锅       |           5000 | ++--------------+----------------+ +3 rows in set (0.00 sec) +``` +虽然上述方法没有问题,但还是存在一点不足之处,那就是随着希望选取的对象越来越多, SQL 语句也会越来越长,阅读起来也会越来越困难。这时, 我们就可以使用IN 谓词 +`IN(值1, 值2, 值3, ......)来替换上述 SQL 语句。 + +```sql +SELECT product_name, purchase_price +FROM product +WHERE purchase_price IN (320, 500, 5000); ++--------------+----------------+ +| product_name | purchase_price | ++--------------+----------------+ +| T恤          |            500 | +| 打孔器       |            320 | +| 高压锅       |           5000 | ++--------------+----------------+ +3 rows in set (0.00 sec) +``` +上述语句简洁了很多,可读性大幅提高。 +反之,希望选取出“进货单价不是 320 元、 500 元、 5000 元”的商品时,可以使用否定形式NOT IN来实现。 + +```sql +SELECT product_name, purchase_price +FROM product +WHERE purchase_price NOT IN (320, 500, 5000); ++--------------+----------------+ +| product_name | purchase_price | ++--------------+----------------+ +| 运动T恤      |           2800 | +| 菜刀         |           2800 | +| 擦菜板       |            790 | ++--------------+----------------+ +3 rows in set (0.00 sec) +``` +需要注意的是,在使用IN 和 NOT IN 时是无法选取出NULL数据的。 +实际结果也是如此,上述两组结果中都不包含进货单价为 NULL 的叉子和圆珠笔。 NULL 只能使用 IS NULL 和 IS NOT NULL 来进行判断。 + +## +## 3.4.6 使用子查询作为IN谓词的参数 + +* IN和子查询 + +IN 谓词(NOT IN 谓词)具有其他谓词所没有的用法,那就是可以使用子查询作为其参数。我们已经在 5-2 节中学习过了,子查询就是 SQL内部生成的表,因此也可以说“能够将表作为 IN 的参数”。同理,我们还可以说“能够将视图作为 IN 的参数”。 + +在此,我们创建一张新表`shopproduct`显示出哪些商店销售哪些商品。 + +```sql +-- DDL :创建表 +DROP TABLE IF EXISTS shopproduct; +CREATE TABLE shopproduct +( shop_id CHAR(4) NOT NULL, + shop_name VARCHAR(200) NOT NULL, +product_id CHAR(4) NOT NULL, +  quantity INTEGER NOT NULL, +PRIMARY KEY (shop_id, product_id) -- 指定主键 +); +-- DML :插入数据 +START TRANSACTION; -- 开始事务 +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0001', 30); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0002', 50); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0003', 15); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0002', 30); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0003', 120); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0004', 20); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0006', 10); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0007', 40); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0003', 20); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0004', 50); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0006', 90); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0007', 70); +INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000D', '福冈', '0001', 100); +COMMIT; -- 提交事务 +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) +``` +由于单独使用商店编号(shop_id)或者商品编号(product_id)不能区分表中每一行数据 +,因此指定了 2 列作为主键(primary key)对商店和商品进行组合,用来唯一确定每一行数据。 + +假设我么需要取出大阪在售商品的销售单价,该如何实现呢? + +第一步,取出大阪门店的在售商品 `product_id ; + +第二步,取出大阪门店在售商品的销售单价 `sale_price + +```sql +-- step1:取出大阪门店的在售商品 `product_id` +SELECT product_id +FROM shopproduct +WHERE shop_id = '000C'; ++------------+ +| product_id | ++------------+ +| 0003       | +| 0004       | +| 0006       | +| 0007       | ++------------+ +4 rows in set (0.00 sec) +``` +上述语句取出了大阪门店的在售商品编号,接下来,我么可以使用上述语句作为第二步的查询条件来使用了。 +```sql +-- step2:取出大阪门店在售商品的销售单价 `sale_price` +SELECT product_name, sale_price +FROM product +WHERE product_id IN (SELECT product_id + FROM shopproduct + WHERE shop_id = '000C'); ++--------------+------------+ +| product_name | sale_price | ++--------------+------------+ +| 运动T恤      |       4000 | +| 菜刀         |       3000 | +| 叉子         |        500 | +| 擦菜板       |        880 | ++--------------+------------+ +4 rows in set (0.00 sec) +``` +根据第5章学习的知识,子查询是从最内层开始执行的(由内而外),因此,上述语句的子查询执行之后,sql 展开成下面的语句 +```sql +-- 子查询展开后的结果 +SELECT product_name, sale_price +FROM product +WHERE product_id IN ('0003', '0004', '0006', '0007'); ++--------------+------------+ +| product_name | sale_price | ++--------------+------------+ +| 运动T恤      |       4000 | +| 菜刀         |       3000 | +| 叉子         |        500 | +| 擦菜板       |        880 | ++--------------+------------+ +4 rows in set (0.00 sec) +``` +可以看到,子查询转换之后变为 in 谓词用法,你理解了吗? +或者,你会疑惑既然 in 谓词也能实现,那为什么还要使用子查询呢?这里给出两点原因: + +①:实际生活中,某个门店的在售商品是不断变化的,使用 in 谓词就需要经常更新 sql 语句,降低了效率,提高了维护成本; + +②:实际上,某个门店的在售商品可能有成百上千个,手工维护在售商品编号真是个大工程。 + +使用子查询即可保持 sql 语句不变,极大提高了程序的可维护性,这是系统开发中需要重点考虑的内容。 + +* NOT IN和子查询 + +NOT IN 同样支持子查询作为参数,用法和 in 完全一样。 + +```sql +-- NOT IN 使用子查询作为参数,取出未在大阪门店销售的商品的销售单价 +SELECT product_name, sale_price + FROM product + WHERE product_id NOT IN (SELECT product_id + FROM shopproduct + WHERE shop_id = '000A'); ++--------------+------------+ +| product_name | sale_price | ++--------------+------------+ +| 菜刀         |       3000 | +| 高压锅       |       6800 | +| 叉子         |        500 | +| 擦菜板       |        880 | +| 圆珠笔       |        100 | ++--------------+------------+ +5 rows in set (0.00 sec) +``` +## +## 3.4.7 EXIST 谓词 + +EXIST 谓词的用法理解起来有些难度。 + +① EXIST 的使用方法与之前的都不相同 + +② 语法理解起来比较困难 + +③ 实际上即使不使用 EXIST,基本上也都可以使用 IN(或者 NOT IN)来代替 + +这么说的话,还有学习 EXIST 谓词的必要吗?答案是肯定的,因为一旦能够熟练使用 EXIST 谓词,就能体会到它极大的便利性。 + +不过,你不用过于担心,本课程介绍一些基本用法,日后学习时可以多多留意 EXIST 谓词的用法,以期能够在达到 SQL 中级水平时掌握此用法。 + +* EXIST谓词的使用方法 + +谓词的作用就是 **“判断是否存在满足某种条件的记录”**。 + +如果存在这样的记录就返回真(TRUE),如果不存在就返回假(FALSE)。 + +EXIST(存在)谓词的主语是“记录”。 + +我们继续以 IN和子查询 中的示例,使用 EXIST 选取出大阪门店在售商品的销售单价。 + +```sql +SELECT product_name, sale_price + FROM product AS p + WHERE EXISTS (SELECT * + FROM shopproduct AS sp + WHERE sp.shop_id = '000C' + AND sp.product_id = p.product_id); ++--------------+------------+ +| product_name | sale_price | ++--------------+------------+ +| 运动T恤      |       4000 | +| 菜刀         |       3000 | +| 叉子         |        500 | +| 擦菜板       |        880 | ++--------------+------------+ +4 rows in set (0.00 sec) +``` +* EXIST的参数 + +之前我们学过的谓词,基本上都是像“列 LIKE 字符串”或者“ 列 BETWEEN 值 1 AND 值 2”这样需要指定 2 个以上的参数,而 EXIST 的左侧并没有任何参数。因为 EXIST 是只有 1 个参数的谓词。 所以,EXIST 只需要在右侧书写 1 个参数,该参数通常都会是一个子查询。 + +```sql +(SELECT * + FROM shopproduct AS sp + WHERE sp.shop_id = '000C' + AND sp.product_id = p.product_id)   +``` +上面这样的子查询就是唯一的参数。确切地说,由于通过条件“SP.product_id = P.product_id”将 product 表和 shopproduct表进行了联接,因此作为参数的是关联子查询。 EXIST 通常会使用关联子查询作为参数。 +* 子查询中的SELECT * + +由于 EXIST 只关心记录是否存在,因此返回哪些列都没有关系。 EXIST 只会判断是否存在满足子查询中 WHERE 子句指定的条件“商店编号(shop_id)为 '000C',商品(product)表和商店 + +商品(shopproduct)表中商品编号(product_id)相同”的记录,只有存在这样的记录时才返回真(TRUE)。 + +因此,使用下面的查询语句,查询结果也不会发生变化。 + +```sql +SELECT product_name, sale_price + FROM product AS p + WHERE EXISTS (SELECT 1 -- 这里可以书写适当的常数 + FROM shopproduct AS sp + WHERE sp.shop_id = '000C' + AND sp.product_id = p.product_id); ++--------------+------------+ +| product_name | sale_price | ++--------------+------------+ +| 运动T恤      |       4000 | +| 菜刀         |       3000 | +| 叉子         |        500 | +| 擦菜板       |        880 | ++--------------+------------+ +4 rows in set (0.00 sec) +``` +>大家可以把在 EXIST 的子查询中书写 SELECT * 当作 SQL 的一种习惯。 +* 使用NOT EXIST替换NOT IN + +就像 EXIST 可以用来替换 IN 一样, NOT IN 也可以用NOT EXIST来替换。 + +下面的代码示例取出,不在大阪门店销售的商品的销售单价。 + +```sql +SELECT product_name, sale_price + FROM product AS p + WHERE NOT EXISTS (SELECT * + FROM shopproduct AS sp + WHERE sp.shop_id = '000A' + AND sp.product_id = p.product_id); ++--------------+------------+ +| product_name | sale_price | ++--------------+------------+ +| 菜刀         |       3000 | +| 高压锅       |       6800 | +| 叉子         |        500 | +| 擦菜板       |        880 | +| 圆珠笔       |        100 | ++--------------+------------+ +5 rows in set (0.00 sec) +``` +NOT EXIST 与 EXIST 相反,当“不存在”满足子查询中指定条件的记录时返回真(TRUE)。 +# +# 3.5 CASE 表达式 + +## 3.5.1 什么是 CASE 表达式? + +CASE 表达式是函数的一种。是 SQL 中数一数二的重要功能,有必要好好学习一下。 + +CASE 表达式是在区分情况时使用的,这种情况的区分在编程中通常称为(条件)分支。 + +CASE表达式的语法分为简单CASE表达式和搜索CASE表达式两种。由于搜索CASE表达式包含简单CASE表达式的全部功能。本课程将重点介绍搜索CASE表达式。 + +语法: + +```plain +CASE WHEN <求值表达式> THEN <表达式> + WHEN <求值表达式> THEN <表达式> + WHEN <求值表达式> THEN <表达式> + . + . + . +ELSE <表达式> +END   +``` +上述语句执行时,依次判断 when 表达式是否为真值,是则执行 THEN 后的语句,如果所有的 when 表达式均为假,则执行 ELSE 后的语句。 +无论多么庞大的 CASE 表达式,最后也只会返回一个值。 + +## +## 3.5.2 CASE表达式的使用方法 + +假设现在 要实现如下结果: + +```plain +A :衣服 +B :办公用品 +C :厨房用具   +``` +因为表中的记录并不包含“A : ”或者“B : ”这样的字符串,所以需要在 SQL 中进行添加。并将“A : ”“B : ”“C : ”与记录结合起来。 +* **应用场景1:根据不同分支得到不同列值** +```sql +SELECT  product_name, + CASE WHEN product_type = '衣服' THEN CONCAT('A : ',product_type) + WHEN product_type = '办公用品'  THEN CONCAT('B : ',product_type) + WHEN product_type = '厨房用具'  THEN CONCAT('C : ',product_type) + ELSE NULL + END AS abc_product_type + FROM product; ++--------------+------------------+ +| product_name | abc_product_type | ++--------------+------------------+ +| T恤          | A : 衣服        | +| 打孔器       | B : 办公用品    | +| 运动T恤      | A : 衣服        | +| 菜刀         | C : 厨房用具    | +| 高压锅       | C : 厨房用具    | +| 叉子         | C : 厨房用具    | +| 擦菜板       | C : 厨房用具    | +| 圆珠笔       | B : 办公用品    | ++--------------+------------------+ +8 rows in set (0.00 sec) +``` +ELSE 子句也可以省略不写,这时会被默认为 ELSE NULL。但为了防止有人漏读,还是希望大家能够显示地写出 ELSE 子句。 +此外, CASE 表达式最后的“END”是不能省略的,请大家特别注意不要遗漏。忘记书写 END 会发生语法错误,这也是初学时最容易犯的错误。 + +* **应用场景2:实现列方向上的聚合** + +通常我们使用如下代码实现行的方向上不同种类的聚合(这里是 sum) + +```sql +SELECT product_type, + SUM(sale_price) AS sum_price + FROM product + GROUP BY product_type;   ++--------------+-----------+ +| product_type | sum_price | ++--------------+-----------+ +| 衣服         |      5000 | +| 办公用品     |       600 | +| 厨房用具     |     11180 | ++--------------+-----------+ +3 rows in set (0.00 sec) +``` +假如要在列的方向上展示不同种类额聚合值,该如何写呢? +```plain +sum_price_clothes | sum_price_kitchen | sum_price_office +------------------+-------------------+----------------- + 5000 | 11180 | 600   +``` +聚合函数 + CASE WHEN 表达式即可实现该效果 +```sql +-- 对按照商品种类计算出的销售单价合计值进行行列转换 +SELECT SUM(CASE WHEN product_type = '衣服' THEN sale_price ELSE 0 END) AS sum_price_clothes, + SUM(CASE WHEN product_type = '厨房用具' THEN sale_price ELSE 0 END) AS sum_price_kitchen, + SUM(CASE WHEN product_type = '办公用品' THEN sale_price ELSE 0 END) AS sum_price_office + FROM product; ++-------------------+-------------------+------------------+ +| sum_price_clothes | sum_price_kitchen | sum_price_office | ++-------------------+-------------------+------------------+ +|              5000 |             11180 |              600 | ++-------------------+-------------------+------------------+ +1 row in set (0.00 sec) +``` +* **(扩展内容)应用场景3:实现行转列** + +假设有如下图表的结构 + +![图片](https://github.com/datawhalechina/team-learning-sql/blob/main/img/ch03/ch03.11casewhen1.png) + +计划得到如下的图表结构 + +![图片](https://github.com/datawhalechina/team-learning-sql/blob/main/img/ch03/ch03.12casewhen2.png) + +聚合函数 + CASE WHEN 表达式即可实现该转换 + +```sql +-- CASE WHEN 实现数字列 score 行转列 +SELECT name, + SUM(CASE WHEN subject = '语文' THEN score ELSE null END) as chinese, + SUM(CASE WHEN subject = '数学' THEN score ELSE null END) as math, + SUM(CASE WHEN subject = '外语' THEN score ELSE null END) as english + FROM score + GROUP BY name; ++------+---------+------+---------+ +| name | chinese | math | english | ++------+---------+------+---------+ +| 张三 | 93 | 88 | 91 | +| 李四 | 87 | 90 | 77 | ++------+---------+------+---------+ +2 rows in set (0.00 sec) +``` +上述代码实现了数字列 score 的行转列,也可以实现文本列 subject 的行转列 +```sql +-- CASE WHEN 实现文本列 subject 行转列 +SELECT name, + MAX(CASE WHEN subject = '语文' THEN subject ELSE null END) as chinese, + MAX(CASE WHEN subject = '数学' THEN subject ELSE null END) as math, + MIN(CASE WHEN subject = '外语' THEN subject ELSE null END) as english + FROM score + GROUP BY name; ++------+---------+------+---------+ +| name | chinese | math | english | ++------+---------+------+---------+ +| 张三 | 语文 | 数学 | 外语 | +| 李四 | 语文 | 数学 | 外语 | ++------+---------+------+---------+ +2 rows in set (0.00 sec +``` +总结: +* 当待转换列为数字时,可以使用`SUM AVG MAX MIN`等聚合函数; +* 当待转换列为文本时,可以使用`MAX MIN`等聚合函数 +# +# 练习题-第二部分 + +## 3.5 判断题 + +运算或者函数中含有 NULL 时,结果是否都会变为NULL ? + +## 3.6 + +对本章中使用的 `product`(商品)表执行如下 2 条 `SELECT` 语句,能够得到什么样的结果呢? + +① + +```sql +SELECT product_name, purchase_price + FROM product + WHERE purchase_price NOT IN (500, 2800, 5000); +``` +② +```sql +SELECT product_name, purchase_price + FROM product + WHERE purchase_price NOT IN (500, 2800, 5000, NULL); +``` +## 3.7 + +按照销售单价( `sale_price` )对练习 3.6 中的 `product`(商品)表中的商品进行如下分类。 + +* 低档商品:销售单价在1000日元以下(T恤衫、办公用品、叉子、擦菜板、 圆珠笔) +* 中档商品:销售单价在1001日元以上3000日元以下(菜刀) +* 高档商品:销售单价在3001日元以上(运动T恤、高压锅) + +请编写出统计上述商品种类中所包含的商品数量的 SELECT 语句,结果如下所示。 + +执行结果 + +```sql +low_price | mid_price | high_price +----------+-----------+------------ + 5 | 1 | 2 +``` + + +