sql学习笔记3

149 阅读5分钟

「这是我参与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标量子查询

只返回一个值的查询。

场景:

  1. 查询出销售单价高于平均销售单价的商品
  2. 查询出注册日期最晚的那个商品

标量子查询不仅仅局限于 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;