感谢datawhale开展的每月组队学习,本章教材ref:SQL Task03
视图
常见操作
** 创建视图**
CREATE VIEW <视图名称>(<列名1>,<列名2>,...) AS <SELECT语句>
-- 例子
CREATE VIEW productsum (product_type, cnt_product)
AS
SELECT product_type, COUNT(*)
FROM product
GROUP BY product_type ;
我们可以直接从数据表创建视图,也可以从视图创建视图(但,不要这样,性能会很慢)
视图名必须是unique的,不可以和别的视图/表重名
** 修改视图 **
ALTER VIEW <视图名称> AS <SELECT语句>
-- 例子
ALTER VIEW productSum
AS
SELECT product_type, sale_price
FROM Product
WHERE regist_date > '2009-09-11';
- 视图的列名会随着SELECT改变诶,不用自己去写
** 更新视图 **
原本的product表的这个地方也会随之改变。这有点像shallow copy浅拷贝的意思?视图像是一个reference。
虽然这样可以修改成功,但是不建议使用这种方式。
我们在创建视图时也尽量不要通过视图修改表
是的,shallow copy、deep copy虽然好理解,但是有的时候在实际应用中是很容易没注意就把原数据改了。还是不要用更新视图内容比较好
删除视图
子查询
-
查询出销售单价高于平均销售单价的商品
-- 销售单价高于平均销售单价的商品 SELECT * FROM product WHERE sale_price > (SELECT AVG(sale_price) FROM product);
-
查询出注册日期最晚的那个商品
-- 查询出注册日期最晚的那个商品 SELECT * FROM product WHERE regist_date = (SELECT MAX(regist_date) FROM product);
-
你能猜到这段代码的运行结果是什么吗?运行一下看看与你想象的结果是否一致
SELECT product_id, product_name, sale_price, (SELECT AVG(sale_price) FROM product) AS avg_price FROM product;
从product表中获取三个列信息:product_id, product_name, sale_price, 算出表中所有产品的平均售价放在最后一列
-
你能理解这个例子在做什么操作么
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);
用JOIN可以改写成
SELECT a.product_type, product_name, sale_price FROM product AS a LEFT JOIN ( SELECT b.product_type, AVG(sale_price) as average_price FROM product AS b GROUP BY b.product_type ) as c ON a.product_type = c.product_type WHERE sale_price > average_price;
教材给出的query结构截图可能有点问题…因为在UPDATE VIEW更新视图的时候,原表就已经被改动掉了,办公用品类的有两个:一个打孔器500元,圆珠笔被改成了5000元了已经。但是这里给出的output截图,比自己产品类平均值还高的商品还是打孔器(500元)。
emmm… 也有可能是写教材的大佬做了什么不commit/rollback操作…
各种各样的函数
算数函数
字符串函数
日期函数
转换函数
谓语
CASE 表达式
练习题
-
创建出满足下述三个条件的视图(视图名称为 ViewPractice5_1)。使用 product(商品)表作为参照表,假设表中包含初始状态的 8 行数据。
条件 1:销售单价大于等于 1000 日元。
条件 2:登记日期是 2009 年 9 月 20 日。
条件 3:包含商品名称、销售单价和登记日期三列。对该视图执行 SELECT 语句的结果如下所示。
SELECT * FROM ViewPractice5_1;
执行结果
product_name | sale_price | regist_date --------------+------------+------------ T恤衫 | 1000 | 2009-09-20 菜刀 | 3000 | 2009-09-20
-- 3.1 CREATE VIEW ViewPractice5_1 (product_name, sale_price, regist_date) AS SELECT product_name, sale_price, regist_date FROM product WHERE regist_date = '2009-09-20' AND sale_price >= 1000;
小tips:
可以在创建视图之前先执行一下后面的SELECT语句,看一下是否正确。
我一开始粗心大于等于没写等于,执行结果少一行,再drop view,再重新创建… 也算是做个删除视图的练习吧 😅
-
向习题一中创建的视图 ViewPractice5_1 中插入如下数据,会得到什么样的结果呢?
INSERT INTO ViewPractice5_1 VALUES (' 刀子 ', 300, '2009-11-02');
我的猜想:大概是会在原表中也增加一行?那没有value的那些其他列的值会为NULL?❌
我的猜想是错误的,因为:这个视图是原表的一个窗口,而原表product
中 我们设置product_id
,product_type
应当是NOT NULL
的,如果我们通过这个视图以这样的方式insert数据,我们无法给要求不能为空的product_id
和product_type
赋值。因此MySQL不允许我们这样做。
从给出的报错信息来看,如果我们给product_id
和product_type
提前设置一个默认值,比如product_id自动increment或许是可以实现的。
如果要创建一个updatable的视图的话,创建的那个SELECT子句里不能包含:- 聚合函数
- DISTINCT
- GROUP BY 子句
- HAVING 子句
- UNION、UNION ALL 子句
- LEFT JOIN、OUTER JOIN
- SELECT 或是 WHERE 子句 中有 subquery 子查询
- 从另一个不可updatable的视图创建
- reference only to literal values?
- multiple references to any column of the base table
可以使用
SELECT table_name, is_updatable FROM information_schema.VIEWS WHERE TABLE_SCHEMA = 'classicmodels';
来查看视图是不是updatable
-
请根据如下结果编写 SELECT 语句,其中 sale_price_all 列为全部商品的平均销售单价。
product_id | product_name | product_type | sale_price | sale_price_all ------------+-------------+--------------+------------+--------------------- 0001 | T恤衫 | 衣服 | 1000 | 2097.5000000000000000 0002 | 打孔器 | 办公用品 | 500 | 2097.5000000000000000 0003 | 运动T恤 | 衣服 | 4000 | 2097.5000000000000000 0004 | 菜刀 | 厨房用具 | 3000 | 2097.5000000000000000 0005 | 高压锅 | 厨房用具 | 6800 | 2097.5000000000000000 0006 | 叉子 | 厨房用具 | 500 | 2097.5000000000000000 0007 | 擦菜板 | 厨房用具 | 880 | 2097.5000000000000000 0008 | 圆珠笔 | 办公用品 | 100 | 2097.5000000000000000
CREATE VIEW AvgPriceByType AS SELECT product_id, product_name, a.product_type, a.sale_price, b.avg_sale_price FROM product a, ( SELECT product_type, AVG(sale_price) as avg_sale_price FROM product GROUP BY product_type ) b WHERE a.product_type = b.product_type;
思路:
-
请根据习题一中的条件编写一条 SQL 语句,创建一幅包含如下数据的视图(名称为AvgPriceByType)
product_id | product_name | product_type | sale_price | avg_sale_price ------------+-------------+--------------+------------+--------------------- 0001 | T恤衫 | 衣服 | 1000 |2500.0000000000000000 0002 | 打孔器 | 办公用品 | 500 | 300.0000000000000000 0003 | 运动T恤 | 衣服 | 4000 |2500.0000000000000000 0004 | 菜刀 | 厨房用具 | 3000 |2795.0000000000000000 0005 | 高压锅 | 厨房用具 | 6800 |2795.0000000000000000 0006 | 叉子 | 厨房用具 | 500 |2795.0000000000000000 0007 | 擦菜板 | 厨房用具 | 880 |2795.0000000000000000 0008 | 圆珠笔 | 办公用品 | 100 | 300.0000000000000000
SELECT product_id, product_name, a.product_type, a.sale_price, b.avg_sale_price FROM product a, (SELECT product_type, AVG(sale_price) as avg_sale_price FROM product GROUP BY product_type) b WHERE a.product_type = b.product_type;
思路
-
运算或者函数中含有 NULL 时,结果全都会变为NULL ?(判断题)
感觉… emmm是的吧,最起码大部分是的
可以慢慢总结一下
函数中含有NULL
结果全都变为NULL
的有:
- CONCAT(str1, str2, NULL) --> NULL -
对本章中使用的 product(商品)表执行如下 2 条 SELECT 语句,能够得到什么样的结果呢?
SELECT product_name, purchase_price FROM product WHERE purchase_price NOT IN (500, 2800, 5000);
从表
product
中获取产品名称和购买价格, 取出的产品应满足:购买价格不是 500元,不是2800元,不是5000元(统统只要一块钱,不是
SELECT product_name, purchase_price FROM product WHERE purchase_price NOT IN (500, 2800, 5000, NULL);
空
因为WHERE purchase_price NOT IN (500, 2800, 5000, NULL)
转换成OR
来写的话就是WHERE purchase_price <> 500 OR purchase_price <> 2800 OR purchase_price <> 5000 OR purchase_price <> NULL
那我们上期学过了,取
NULL
或取非NULL
如果用=
或<>
结果都会是空 -
按照销售单价( sale_price)对练习 3.6 中的 product(商品)表中的商品进行如下分类。
- 低档商品:销售单价在1000日元以下(T恤衫、办公用品、叉子、擦菜板、 圆珠笔)
- 中档商品:销售单价在1001日元以上3000日元以下(菜刀)
- 高档商品:销售单价在3001日元以上(运动T恤、高压锅)
请编写出统计上述商品种类中所包含的商品数量的 SELECT 语句,结果如下所示。
执行结果
low_price | mid_price | high_price ----------+-----------+------------ 5 | 1 | 2
-- solution 1 SELECT COUNT(CASE WHEN sale_price<=1000 THEN product_id ELSE NULL END) as low_price, COUNT(CASE WHEN 1000<sale_price AND sale_price<=3000 THEN product_id ELSE NULL END) as mid_price, COUNT(CASE WHEN sale_price>3000 THEN product_id ELSE NULL END) as high_price FROM product; -- solution 2 SELECT SUM(CASE WHEN sale_price<=1000 THEN 1 ELSE 0 END) as low_price, SUM(CASE WHEN 1000<sale_price AND sale_price<=3000 THEN 1 ELSE 0 END) as mid_price, SUM(CASE WHEN sale_price>3000 THEN 1 ELSE 0 END) as high_price FROM product;
方法一:
我们通过CASE
表达式获得满足条件的所有product_id
, 然后使用COUNT()
函数去统计总共有多少个product_id
,使用AS
赋予别名
⚠️ ELSE NULL,对于COUNT()
函数,如果有NULL
COUNT(*)
包含NULL的数据行数COUNT(<列名>)
不包含NULL的数据函数
方法二
第二个方法是用到了SUM()
函数。一样,先通过CASE
表达式获得满足条件的项?并赋予其值为1,不满足的为0。这个时候其实跟第一个方法意思一样的,但是第一个方法可能如果表里有重复商品的话就更好一点,第二个方法就没办法去除重复的部分。最后利用加法SUM()
就可以计算出满足条件的商品数量
- 满足条件为1
- 不满足条件为0
- 求和