「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」
4.复杂查询
4.1视图
视图与表的区别:是否保存实际数据。视图可理解为一个窗口,用来看到数据库表中真实数据。
比如一个select查询出来的结果就是视图。
视图基于真值表,也可以基于视图。但多重视图会降低sql性能。
一般dbms视图定义不能使用order by,因为视图和表一样,数据行没有顺序。
mysql视图定义可使用order by。
4.1.1视图创建
CREATE VIEW <视图名称>(<列名1>,<列名2>,...) AS <SELECT语句>
4.1.2视图更新
- 如果原表可以更新,那么 视图中的数据也可以更新。反之亦然,如果视图发生了改变,而原表没有进行相应更新的话,就无法保证数据的一致性了。
- 对于一个视图来说,如果包含以下结构的任意一种都是不可以被更新的:
-
- 聚合函数 SUM()、MIN()、MAX()、COUNT() 等。
- DISTINCT 关键字。
-
- GROUP BY 子句。
- HAVING 子句。
-
- UNION 或 UNION ALL 运算符。
- FROM 子句中包含多个表。
- 视图更新,原表也会被更新。但更新的只是视图里可以被看到的数据
4.1.3删除视图
DROP VIEW <视图名1> [ , <视图名2> …]
-- 创建视图
CREATE VIEW <视图名称>(<列名1>,<列名2>,...) AS <SELECT语句>
-- 基于单表视图
CREATE VIEW productsum (product_type, cnt_product)
AS
SELECT product_type, COUNT(*)
FROM product
GROUP BY product_type ;
-- 基于多表视图
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;
-- 在上面的视图上查询
SELECT sale_price, shop_name
FROM view_shop_product
WHERE product_type = '衣服';
-- 修改视图结构,视图名需要是数据库唯一。
-- 也可以通过将当前视图删除然后重新创建的方式达到修改的效果。
ALTER VIEW <视图名> AS <SELECT语句>
ALTER VIEW productSum
AS
SELECT product_type, sale_price
FROM Product
WHERE regist_date > '2009-09-11';
-- 视图更新
UPDATE productsum
SET sale_price = '5000'
WHERE product_type = '办公用品';
-- 删除视图:需要权限
DROP VIEW <视图名1> [ , <视图名2> …]
DROP VIEW productSum;
4.2子查询
嵌套查询,select的from来自另一个select(子查询)的结果。
子查询是一次性的,不会存储的介质中。
但视图会保存下来。
多层嵌套,导致难以理解以及效率下降,尽量避免。
SELECT stu_name
FROM (
SELECT stu_name, COUNT(*) AS stu_cnt
FROM students_info
GROUP BY stu_age) AS studentSum;
4.2.1标量子查询
只返回一个值的查询。
场景:
- 查询出销售单价高于平均销售单价的商品
- 查询出注册日期最晚的那个商品
标量子查询不仅仅局限于 WHERE 子句中,通常任何可以使用单一值的位置都可以使用。也就是说, 能够使用常数或者列名的地方,无论是 SELECT 子句、GROUP BY 子句、HAVING 子句,还是 ORDER BY 子句,几乎所有的地方都可以使用。
SELECT product_id, product_name, sale_price
FROM product
WHERE sale_price > (SELECT AVG(sale_price) FROM product);
SELECT product_id,
product_name,
sale_price,
(SELECT AVG(sale_price)
FROM product) AS avg_price
FROM product;
4.2.2关联子查询
通过一些标志将内外两层的查询连接起来起到过滤数据的目的
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);
--
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);
4.3练习题
4.3.1
创建出满足下述三个条件的视图(视图名称为 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(prodouct_name, sale_price, regist_date) as
select product_name, sale_price, regist_date
from product
where sale_price >= 1000 and regist_date = '2009-09-20';
select * from ViewPractice5_1;
4.3.2
向习题一中创建的视图 ViewPractice5_1 中插入如下数据,会得到什么样的结果呢?
INSERT INTO ViewPractice5_1 VALUES (' 刀子 ', 300, '2009-11-02');
执行报错,提示:Field of view 'shop.viewpractice5_1' underlying table doesn't have a default value.
显式的:INSERTINTOViewPractice5_1(prodouct_name, sale_price, regist_date) VALUES (' 刀子 ', 300, '2009-11-02');
依然如此。
product表的列定义:
如果在product表设置视图对应字段拥有默认值,
查询资料,向视图插入数据,首先要有插入资格,其次拥有以下条件之一:
- 该字段允许空值。
- 该字段设有默认值。
- 该字段是标识字段,可根据标识种子和标识增量自动填充数据。
- 该字段的数据类型为timestamp或uniqueidentifier。
4.3.3
请根据如下结果编写 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
-- 注意子查询里的括号
select product_id, product_name, product_type, sale_price,
(select AVG(sale_price) from product) as sale_price_all
from product;
4.3.4
请根据习题一中的条件编写一条 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
提示:其中的关键是 avg_sale_price 列。与习题三不同,这里需要计算出的 是各商品种类的平均销售单价。这与使用关联子查询所得到的结果相同。 也就是说,该列可以使用关联子查询进行创建。问题就是应该在什么地方使用这个关联子查询。
解答:先完成查询句,再套上创建视图的语法。
关于各商品种类的平均销售单价,参考前面的例子。
关键在于对子查询两个表的别名的定义。最外层是表p1,内层是表p2。
CREATE view AvgPriceByType (product_id, product_name, product_type, sale_price,avg_sale_price)
as
select product_id, product_name, product_type, sale_price,
(SELECT AVG(sale_price)
FROM product AS p2
WHERE p1.product_type =p2.product_type) as avg_sale_price
from product as p1;
select * from AvgPriceByType;