视图
视图是什么
视图是一个虚拟的表,视图是依据SELECT语句来创建的,所以操作视图时会根据创建视图的SELECT语句生成一张虚拟表,然后在这张虚拟表上做SQL操作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rfsg9YGW-1640072423323)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/60b35bc3-7acb-4e53-a896-b45d321aa244/Untitled.png)]
视图存在的意义
视图的操作
创建视图
语法
CREATE VIEW <视图名称>(<列名1>,<列名2>,...) AS <SELECT语句>
注意事项
1.SELECT 语句中列的排列顺序和视图中列的排列顺序相同
2.视图不仅可以基于真实表,也可以在视图的基础上继续创建视图。(不建议)
3.一般的DBMS中定义视图时不能使用ORDER BY语句。(mysql可以)
4.视图表名要是唯一的。
基于单表的视图
CREATE VIEW productsum (product_type, cnt_product)
AS
SELECT product_type, COUNT(*)
FROM product
GROUP BY product_type ;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HmGQUqO7-1640072423327)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ef67eb7c-4198-4249-8cd4-29b5b3999683/Untitled.png)]
基于多表的视图
CREATE VIEW view_shop_product(product_type, sale_price, shop_name)
AS
SELECT product_type, sale_price, shop_name
FROM product,
shop_product
WHERE product.product_id = shop_product.product_id;
修改视图结构
语法:
ALTER VIEW <视图名> AS <SELECT语句>
例子:
ALTER VIEW productSum
AS
SELECT product_type, sale_price
FROM Product
WHERE regist_date > '2009-09-11';
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oPm1rAMR-1640072423329)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fd070f3d-ae9c-4d49-8891-aa9c16291b73/Untitled.png)]
更新视图内容
对于一个视图来说,如果包含以下结构的任意一种都是不可以被更新的:
例子:
UPDATE productsum
SET sale_price = '5000'
WHERE product_type = '办公用品';
更新视图数据,相关原表的数据也会更新,不过只能修改透过窗口能看到的内容。(因此不建议通过视图来修改表)
删除视图
语法:
DROP VIEW <视图名1> [ , <视图名2> …]
例子:
DROP VIEW productSum;
子查询
什么是子查询
SELECT stu_name
FROM (
SELECT stu_name, COUNT(*) AS stu_cnt
FROM students_info
GROUP BY stu_age) AS studentSum;
子查询指一个查询语句嵌套在另一个查询语句内部的查询,在 SELECT 子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。
子查询和视图的关系
子查询是一次性的,而视图是存储在介质中的。
嵌套子查询
一个子查询嵌套一个子查询,尽量避免使用。
--例子:
SELECT product_type, cnt_product
FROM (SELECT *
FROM (SELECT product_type,
COUNT(*) AS cnt_product
FROM product
GROUP BY product_type) AS productsum
WHERE cnt_product = 4) AS productsum2;
标量子查询
返回表中具体的某一行的某一列数据
--例子:如下表格
product_id | product_name | sale_price
------------+-------------+----------
0003 | 运动T恤 | 4000
0004 | 菜刀 | 3000
0005 | 高压锅 | 6800
--执行一次标量子查询后是要返回类似于,“0004”,“菜刀”这样的结果。
由于标量子查询的特性,导致标量子查询不仅仅局限于 WHERE 子句中,通常任何可以使用单一值的位置都可以使用。
--例子:
SELECT product_id,
product_name,
sale_price,
(SELECT AVG(sale_price)
FROM product) AS avg_price
FROM product;
关联子查询
关联子查询就是通过一些标志将内外两层的查询连接起来起到过滤数据的目的。
--例子:选取出各商品种类中高于该商品种类的平均销售单价的商品
SELECT product_type, product_name, sale_price
FROM product ASp1
WHERE sale_price > (SELECT AVG(sale_price)
FROM product ASp2
WHERE p1.product_type =p2.product_type
GROUP BY product_type);
关联子查询执行过程:
函数
所谓函数,类似一个黑盒子,你给它一个输入值,它便按照预设的程序定义给出返回值,输入值称为参数。
算术函数
ABS-绝对值
ABS函数用于计算也该数字的绝对值,表示一个数到原点的距离。(NULL的绝对值也是NULL)
MOD-求余数
语法:MOD( 被除数,除数 )
ROUND-四舍五入
语法:ROUND( 对象数值,保留小数的位数 )
例子:
SELECT m,
ABS(m)ASabs_col ,
n, p,
MOD(n, p) AS mod_col,
ROUND(m,1)ASround_colS
FROM samplemath;
字符串函数
CONCAT-拼接
语法:CONCAT(str1, str2, str3)
MySQL中使用 CONCAT 函数进行拼接。
LENGTH-字符串长度
语法:LENGTH( 字符串 )
LOWER-小写转换
LOWER 函数只能针对英文字母使用,它会将参数中的字符串全都转换为小写。
REPLACE-字符串的替换
语法:REPLACE( 对象字符串,替换前的字符串,替换后的字符串 )
SUBSTRING-字符串的截取
语法:SUBSTRING (对象字符串 FROM 截取的起始位置 FOR 截取的字符数)
使用 SUBSTRING 函数 可以截取出字符串中的一部分字符串。截取的起始位置从字符串最左侧开始计算,索引值起始为1。
SUBSTRING_INDEX – 字符串按索引截取
语法:SUBSTRING_INDEX (原始字符串, 分隔符,n)
该函数用来获取原始字符串按照分隔符分割后,第 n 个分隔符之前(或之后)的子字符串
日期函数
CURRENT-获取当前日期
SELECT CURRENT_DATE;
+--------------+
| CURRENT_DATE |
+--------------+
| 2020-08-08 |
+--------------+
1 row in set (0.00 sec)
CURRENT_TIME-当前时间
SELECT CURRENT_TIME;
+--------------+
| CURRENT_TIME |
+--------------+
| 17:26:09 |
+--------------+
1 row in set (0.00 sec)
CURRNT_TIMESTAMP-当前日期和时间
SELECT CURRENT_TIMESTAMP;
+---------------------+
| CURRENT_TIMESTAMP |
+---------------------+
| 2020-08-08 17:27:07 |
+---------------------+
1 row in set (0.00 sec)
EXTRACT-截取日期元素
语法:EXTRACT(日期元素 FROM 日期)
使用 EXTRACT 函数可以截取出日期数据中的一部分,例如“年”
SELECT CURRENT_TIMESTAMP as now,
EXTRACT(YEAR FROM CURRENT_TIMESTAMP) AS year,
EXTRACT(MONTH FROM CURRENT_TIMESTAMP) AS month,
EXTRACT(DAY FROM CURRENT_TIMESTAMP) AS day,
EXTRACT(HOUR FROM CURRENT_TIMESTAMP) AS hour,
EXTRACT(MINUTE FROM CURRENT_TIMESTAMP) AS MINute,
EXTRACT(SECOND FROM CURRENT_TIMESTAMP) AS second;
+---------------------+------+-------+------+------+--------+--------+
| now | year | month | day | hour | MINute | second |
+---------------------+------+-------+------+------+--------+--------+
| 2020-08-08 17:34:38 | 2020 | 8 | 8 | 17 | 34 | 38 |
+---------------------+------+-------+------+------+--------+--------+
1 row in set (0.00 sec)
转换函数
“转换”这个词的含义非常广泛,在 SQL 中主要有两层意思:一是数据类型的转换,简称为类型转换,在英语中称为**cast
**;另一层意思是值的转换。
CAST-类型转换
语法:CAST(转换前的值 AS 想要转换的数据类型)
-- 将字符串类型转换为数值类型
SELECT CAST('0001' AS SIGNED INTEGER) AS int_col;
+---------+
| int_col |
+---------+
| 1 |
+---------+
1 row in set (0.00 sec)
-- 将字符串类型转换为日期类型
SELECT CAST('2009-12-14' AS DATE) AS date_col;
+------------+
| date_col |
+------------+
| 2009-12-14 |
+------------+
1 row in set (0.00 sec)
COALESCE – 将NULL转换为其他值
语法:COALESCE(数据1,数据2,数据3……)
COALESCE 是 SQL 特有的函数。该函数会返回可变参数 A 中左侧开始第1个不是NULL的值。
SELECT COALESCE(NULL, 11) AS col_1,
COALESCE(NULL, 'hello world', NULL) AS col_2,
COALESCE(NULL, NULL, '2020-11-01') AS col_3;
+-------+-------------+------------+
| col_1 | col_2 | col_3 |
+-------+-------------+------------+
| 11 | hello world | 2020-11-01 |
+-------+-------------+------------+
1 row in set (0.00 sec)
谓词
谓词就是返回值为真值的函数。包括**TRUE / FALSE / UNKNOWN
**。
谓词主要有以下几个:
LIKE-用于字符串的模糊查询
**_
**下划线匹配任意 1 个字符
%
匹配任意个任意字符
--例子:查询ddd开头的数据
SELECT *
FROM samplelike
WHERE strcol LIKE 'ddd%';
--例子:查询中间有ddd的数据
SELECT *
FROM samplelike
WHERE strcol LIKE '%ddd%';
--查询结尾有ddd的数据
SELECT *
FROM samplelike
WHERE strcol LIKE '%ddd';
BETWEEN– 用于范围查询
使用 BETWEEN 可以进行范围查询,包含临界值。
-- 选取销售单价为100~ 1000元的商品
SELECT product_name, sale_price
FROM product
WHERE sale_price BETWEEN 100 AND 1000;
IS NULL、IS NOT NULL– 用于判断是否为NULL
为了选取出某些值为 NULL 的列的数据,不能使用 =,而只能使用特定的谓词IS NULL。
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)
NOT IN、IN– OR的简便用法
IN 谓词(NOT IN 谓词)具有其他谓词所没有的用法,那就是可以使用子查询作为其参数。
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)
SELECT product_name, sale_price
FROM product
WHERE product_id IN (SELECT product_id
FROM shopproduct
WHERE shop_id = '000C');
需要注意的是,在使用IN 和 NOT IN 时是无法选取出NULL数据的。
EXISTS
谓词的作用就是 “判断是否存在满足某种条件的记录”。
EXIST 只需要在右侧书写 1 个参数,该参数通常都会是一个关联子查询。
--商店编号(shop_id)为 '000C',商品(product)表和商店商品(shopproduct)表中商品编号(product_id)相同
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);
CASE表达式
概念
CASE 表达式是在区分情况时使用的,这种情况的区分在编程中通常称为(条件)分支
--语法:
CASE WHEN <求值表达式> THEN <表达式>
WHEN <求值表达式> THEN <表达式>
WHEN <求值表达式> THEN <表达式>
.
.
.
ELSE <表达式>
END
--依次判断 when 表达式是否为真值,是则执行 THEN 后的语句,如果所有的 when 表达式均为假,则执行 ELSE 后的语句。
--无论多么庞大的 CASE 表达式,最后也只会返回一个值。
应用场景:根据不同分支得到不同列值
CASE 表达式最后的“END”是不能省略的
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)
应用场景2:实现列方向上的聚合
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)
-- 对按照商品种类计算出的销售单价合计值进行行列转换
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:实现行转列
假设有如下图表的结构
计划得到如下的图表结构
聚合函数 + CASE WHEN 表达式即可实现该转换
-- 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)
练习题
3.1
创建出满足下述三个条件的视图(视图名称为 ViewPractice5_1)。使用 product(商品)表作为参照表,假设表中包含初始状态的 8 行数据。
CREATE VIEW ViewPractice5_1 AS
SELECT product_name,sale_price,regist_date
FROM product
WHERE sale_price>=1000 and regist_date='2009-09-20';
对该视图执行 SELECT 语句的结果如下所示。
SELECT * FROM ViewPractice5_1;
执行结果
product_name | sale_price | regist_date
--------------+------------+------------
T恤衫 | 1000 | 2009-09-20
菜刀 | 3000 | 2009-09-20
3.2
向习题一中创建的视图 ViewPractice5_1 中插入如下数据,会得到什么样的结果呢?
INSERT INTO ViewPractice5_1 VALUES (' 刀子 ', 300, '2009-11-02');
插⼊时将会报错。
3.3
请根据如下结果编写 SELECT 语句,其中 sale_price_all 列为全部商品的平均销售单价。
SELECT product_id,product_name,product_type,sale_price,
(SELECT AVG(sale_price) FROM product) as sale_price_all
FROM product;
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
3.4
请根据习题一中的条件编写一条 SQL 语句,创建一幅包含如下数据的视图(名称为AvgPriceByType)。
-- 创建视图的语句
CREATE VIEW AvgPriceByType AS
SELECT product_id,
product_name,
product_type,
sale_price,
(SELECT AVG(sale_price)
FROM product p2
WHERE p1.product_type = p2.product_type
GROUP BY p1.product_type) AS avg_sale_price
FROM product p1;
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
3.5
运算或者函数中含有 NULL 时,结果全都会变为NULL ?(判断题)
正确
3.6
对本章中使用的 product(商品)表执行如下 2 条 SELECT 语句,能够得到什么样的结果呢?
获取进口价不为500、2800、5000和NULL的数据
SELECT product_name, purchase_price
FROM product
WHERE purchase_price NOT IN (500, 2800, 5000);
NOT IN的参数不能包含NULL,否则,查询结果为空
SELECT product_name, purchase_price
FROM product
WHERE purchase_price NOT IN (500, 2800, 5000, NULL);
3.7
按照销售单价( sale_price)对练习 6.1 中的 product(商品)表中的商品进行如下分类。
请编写出统计上述商品种类中所包含的商品数量的 SELECT 语句,结果如下所示。
SELECT
SUM(CASE WHEN sale_price<1000 THEN 1 ELSE NULL END) as low_price,
SUM(CASE WHEN sale_price>1001 and sale_price<3000 THEN 1 ELSE NULL END) as mid_price,
SUM(CASE WHEN sale_price>3001 THEN 1 ELSE NULL END) as high_price
FROM product;
执行结果
low_price | mid_price | high_price
----------+-----------+------------
5 | 1 | 2