美文网首页
《SQL基础教程》学习笔记Ch5

《SQL基础教程》学习笔记Ch5

作者: los_pollos | 来源:发表于2018-03-28 11:56 被阅读0次

    5复杂查询

    5-1视图

    视图和表

    当我们创建表时,会通过INSERT语句将数据保存到数据库中,而数据库中的数据会被保存到计算机的存储设备中;但是使用视图不会将数据保存在数据库中,实际上视图只保存SELECT语句,从视图中读取数据时,视图会在内部执行该SELECT语句并创建出一张临时表。

    视图的优点:
    1.无需保存数据,节省存储设备的容量
    2.可以将频繁使用的SELECT语句保存为视图

    创建视图的方法
    --创建ProductSum视图
    CREATE VIEW ProductSum (product_type, cnt_product) --视图的列名
    AS
    SELECT product_type, COUNT(*)
      FROM Product
     GROUP BY product_type;
    --使用视图
    SELECT product_type, cnt_product
      FROM ProductSum;
    
    5-1.png

    在FROM子句中使用视图的查询通常有两个步骤:
    1.首先执行定义视图的SELECT子句;
    2.根据得到的结果,再执行在FROM子句中使用视图的SELECT子句

    定义视图时,可以使用WHERE,GROUP BY,HAVING等语句
    此外应该避免下列的多重视图(以视图的基础创建视图):

    CREATE VIEW ProductSumJim (product_type, cnt_product)
    AS
    SELECT product_type, cnt_product
      FROM ProductSum
     WHERE product_type = '办公用品';
    
    视图的限制

    1.定义视图时不能使用ORDER BY子句
    这是因为视图和表一样,数据行是没有顺序的
    2.只在某些条件下可以进行更新
    更新语句:INSERT,DELETE,UPDATE

    比较有代表性的条件:
    SELECT子句未使用DISTINCT;
    FROM子句中只有一张表;
    未使用GROUP BY子句;
    未使用HAVING子句。

    视图和表需要同时进行更新,因而通过汇总(GROUP BY)得到的视图无法进行更新。

    --能够进行更新的视图
    CREATE VIEW ProductJim (product_id, product_name, product_type, sale_price, purchase_price, regist_date)
    AS
    SELECT *
      FROM Product
     WHERE product_type = '办公用品';
    --向视图中添加数据行
    INSERT INTO ProductJim VALUES ('0009', '印章', '办公用品', 95, 10, '2009-11-30');
    

    另外,需要注意的是,在PostgreSQL运行上面的插入语句会报错,由于PostgreSQL初始设定视图为只读,我们通过下列语句来允许PostgreSQL对视图进行更新:

    CREATE OR REPLACE RULE insert_rule
    AS ON INSERT 
    TO  ProductJim DO INSTEAD
    INSERT INTO Product VALUES(
        new.product_id,
        new.product_name,
        new.product_type,
        new.sale_price,
        new.purchase_price,
        new.regist_date);
    

    执行的结果:视图及原表中数据均已被插入

    删除视图
    DROP VIEW ProductSum;
    

    在PostgreSQL中此句会出错,由于关联视图存在的原因,修改为:

    DROP VIEW ProductSum CASCADE;
    

    5-2子查询

    首先来复习一下前面是怎么使用视图来查询的:

    --创建ProductSum视图
    CREATE VIEW ProductSum (product_type, cnt_product) --视图的列名
    AS
    SELECT product_type, COUNT(*)
      FROM Product
     GROUP BY product_type;
    --使用视图
    SELECT product_type, cnt_product
      FROM ProductSum;
    

    所谓子查询:

    -- 在FROM子句中直接书写定义视图的SELECT语句
    SELECT product_type, cnt_product
      FROM (SELECT product_type, COUNT(*) AS cnt_product
             FROM Product
             GROUP BY product_type) AS ProductSum;
    

    这两种方式得到的结果完全一致。
    执行顺序是先执行括号内的SELECT语句,再执行括号外的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;
    
    5-2.png
    子查询的名称

    原则上,子查询必须使用AS设定名称

    标量子查询

    标量子查询指的是必须且只能返回1行1列的子查询
    假设我们想要“查询出销售单价高于平均销售单价的商品”

    --错误的语句
    --在WHERE子句中不能使用聚合函数
    SELECT product_id, product_name, sale_price
      FROM Product
     WHERE sale_price > AVG(sale_price); --错误
    

    要解决这个问题,我们可以使用标量子查询语句

    --计算平均销售单价的标量子查询
    SELECT AVG(sale_price)
      FROM Product;
    --选取出销售单价高于全部商品的平均单价的商品
    SELECT product_id, product_name, sale_price
      FROM Product
     WHERE sale_price > (SELECT AVG(sale_price)
                           FROM Product);
    
    5-3.png
    标量子查询的书写位置

    能够使用常数或者列名的地方,无论是SELECT子句、GROUP BY子句、HAVING子句还是ORDER BY子句,几乎所有的地方都可以使用

    --在SELECT子句中使用标量子查询
    SELECT product_id,
           product_name,
           sale_price,
            (SELECT AVG(sale_price)
                   FROM Product)  AS avg_price
    FROM Product;
    --在HAVING子句中使用标量子查询
    SELECT product_type, AVG(sale_price)
      FROM Product
     GROUP BY product_type
    HAVING AVG(sale_price) > (SELECT AVG(sale_price)
                                FROM Product);
    
    使用标量子查询时的注意事项

    标量子查询只能够返回单一值,如果返回多行值,它就不再是标量子查询,而仅仅是一个普通的子查询

    --由于不是标量子查询,因此不能在SELECT子句中使用
    SELECT product_id, 
           product_name,
           sale_price,
           (SELECT AVG(sale_price)
             FROM Product
             GROUP BY product_type) AS avg_price
      FROM Product;
    

    该段会报错,这是由于子查询的结果是三个而不是一个,不再是标量子查询,不可以写在SELECT子句中

    5-3关联子查询

    普通的子查询和关联子查询的区别

    假设这次我们想要“查询出某个商品类中高于此类商品平均销售单价的商品”

    --按照商品种类计算平均价格
    SELECT AVG(sale_price)
      FROM Product
     GROUP BY product_type;
    --发生错误的子查询
    SELECT product_id, product_name, sale_price
      FROM Product
     WHERE sale_price > (SELECT AVG(sale_price)
                           FROM Product
                        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);
    
    5-4.png

    在这里起到关键作用的是在子查询中添加的WHERE子句的条件,它的意思是在同一商品种类中,对商品的销售单价和平均单价进行比较。
    由于作为比较对象的都是同一张Product表,因此要使用P1和P2两个别名。
    需要注意的是:
    1.关联子查询也是用来对集合进行切分的
    2.结合条件一定要写在子查询中

    相关文章

      网友评论

          本文标题:《SQL基础教程》学习笔记Ch5

          本文链接:https://www.haomeiwen.com/subject/wgpecftx.html