Mysql 基础(十三) 复杂查询

复杂查询

生活的道路一旦选定,就要勇敢地走到底,决不回头。——左拉

视图

  • 从 SQL 角度来看,视图和表是相同的,两者的区别是表中保存的是实际的数据,而视图中保存的是 SELECT 语句(视图本身并不存储数据)

  • 使用视图,可以轻松完成跨多表查询数据等复杂操作

  • 可以将常用的 SELECT 语句作为视图来使用

  • 创建视图需要使用 CREATE VIEW 语句

  • 视图包含”不能使用 ORDER BY”和”可对其有限制的更新”两项限制

  • 删除视图需要使用 DROP VIEW 语句

视图和表

什么是视图

从 SQL 来看视图就是一张表

  • 视图和表的区别

是否保存了真实的数据

使用表的时候我们就是从存储设备(硬盘)中读取数据,进行各种计算之后再将结果返回给用户这样一个过程

但是使用视图的时候并不会将数据保存到设备中,而且也不会将数据保存到其他任何地方,所以视图保存的是 SELECT 语句,我们从视图中读取数据时,视图会在内部执行该 SELECT 语句并创建一张临时表

视图的优点

  • 由于视图无需保存数据。因此可以节省存储设备的容量。(因为它存储的是 SQL 语句)

  • 可以将频繁使用的 SELECT 语句保存成视图,这样就不用每次都重新书写了创建好视图后只需在 SELECT 语句中进行调用就可以方便得到想要的结果。特别是在进行汇总以及复杂的查询条件导致 SELECT 语句非常庞大,使用视图可以大大效率

  • 视图中的数据会随着原表的变化自动更新.视图归根到底就是 SELECT 语句,所谓参照视图也就是执行 SELECT 语句的意思,因此可以保证数据的最新状态.

应当将经常使用的 SELECT 语句做成视图

创建视图的方法

  • 创建视图需要使用 CREATE VIEW 语句,其语法如下:

CREATE VIEW (<视图列名1>,<视图列名2>,...) AS 

SELECT 语句需要书写在 AS 关键字之后 SELECT 语句中列的排列顺序和视图中列的排列顺序相同.SELECT 语句中的第 1 列就是视图中的第一列,SELECT 语句中的第 2 列就是视图中的第 2 列。以此类推:视图的列名在视图名称之后的列表中定义

  • 实例化

CREATE VIEW ProductSum (product_type,cnt_product) AS SELECT product_type,COUNT(*) FROM Product GROUP BY product_type;

这里千万不能省略 AS 关键字

这样一个视图就创立完了

  • 使用视图

SELECT product_type,cnt_product FROM ProductSum;
  • 结果
product_type cnt_product
衣服 2
办公用品 2
厨房用具 4

这样下次在查询的时候需要在工作中频繁汇总的时候,就不用每次使用 GROUP BY 和 COUNT 函数 写 SELECT 语句来从 Product 表中取得数据了.创建出视图之后就可以通过非常简单的 SELECT 语句随时取得汇总的结果,这样表中的数据更新后视图也会随之更新

之所以这样是因为视图就是保存好的 SELECT 语句定义视图的时候可以使用处 ORDER BY 以外的任何 SELECT 语句比如 GROUP BY WHERE,HAVING 等等

视图嵌套

我们可以在视图的规模上在创建视图,如下面 ProductSum 就是视图


CREATE VIEW ProductSumJim (product_type, cnt_product)
AS
SELECT product_type, cnt_product
FROM ProductSum
WHERE product_type = '办公用品';
  • 执行结果
product_type cnt_product
办公用品 2

这里特别强调的是一般不推荐这么做,或者避免在视图上创建视图,因为多重视图会降低 SQL 性能.

视图的限制

  • 定义视图时候不能使用 ORDER BY 子句

CREATE VIEW ProductSum (product_type,cnt_product) AS
SELECT product_type,COUNT(*) FROM Product GROUP BY product_type
ORDER BY product_type

因为视图和表一样,数据行都是没有顺序的。所以在定义视图时请不要使用 ORDER BY 子句

  • 对视图进行更新(一般情况禁止更新)

一句话来说通过汇总得到视图 不允许使用 INSERT UPDATE,DELETE 等等,因为它是汇总的

而不是汇总得到的视图 允许被更新

比如 要是创建汇总


CREATE VIEW ProductSum (product_type,cnt_product) AS SELECT product_type,COUNT(*) FROM Product GROUP BY product_type;

这种情况要是使用 INSERT 之类的会报错,因为它是汇总的

但是如果要是下面这种,没有使用聚合的,则可以修改 INSERT


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');

删除视图

  • 基础语法

DROP VIEW 视图名称(<视图列名1>,<视图列名2>)
  • 实例化

DROP VIEW ProductSum;

子查询

  • 子查询就是一次性视图(SELECT 语句)与视图不同,子查询在 SELECT 语句执行完毕就会消失

  • 由于子查询需要命名,因此需要根据处理内容来指定恰当的名称

  • 标量子查询就是只能返回一行一列的子查询

子查询和视图

  • 子查询的特点概括起来就是一张一次性视图

  • 子查询就是将用来定义视图的 SELECT 语句直接用于 FROM 子句中

  • 简单来说就是查询嵌套,例如下面()里面的就是子查询 AS 不能省


-- 在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 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;

子查询要是嵌套太多了,可读性会变差,性能也会变差,所以尽量避免

标量子查询

标量子查询有一个特殊的限制,那就是必须而且只能返回 1 行 1 列的结果
也就是返回表中某一行的某一列的值 例如’10’或者’东京都’这样的值

标量子查询实际上就是返回单一值的子查询

例如


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

这种写法就是错误的 因为在 WHERE 子句里面不能使用聚合函数

  • 但是利用子查询则可以改变规则

SELECT product_id, product_name, sale_price
FROM Product
WHERE sale_price > (SELECT AVG(sale_price)
FROM Product);

标量子查询的书写位置

  • 标量子查询的书写位置并不仅仅局限于 WHERE 子句中,通常任何可以使用单一值的位置可以使用.也就是说 能够使用常数或者列名的地方,无论 SELECT 子句,GROUP BY 子句,HAVING 子句,还是 ORDER BY 子句 几乎所有的地方都可以使用

比如


SELECT product_id,
product_name,
sale_price,
(SELECT AVG(sale_price)
FROM Product) AS avg_price
FROM Product;

但是标量子查询必须也只能返回一条数据 比如 下面这个就是错误的

因为 GROUP BY xxxx 返回的结果就不止一条


SELECT product_id,
product_name,
sale_price,
(SELECT AVG(sale_price)
FROM Product
GROUP BY product_type) AS avg_price
FROM Product;

关联子查询

  • 关联子查询会在细分的组内进行比较时使用

  • 关联子查询和 GROUP BY 子句一样 也可以对表中的数据进行切分

  • 关联子查询的结合条件如果未出现在子查询内就会发生错误


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);
  • 这里面 P1,P2 都指向的是 Product 表而不是最后的结果

  • 关联的核心就是 P1.product_type = P2.product_type

P2 只能在括号里面使用,括号外面用不了但是 P1 在子查询能用,子查询内部只能子查询使用

关联子查询的执行顺序是不一样的

  • (1) 先执行主查询

SELECT product_type, product_name, sale_price
FROM Product AS P1
  • (2) 从主查询的 product_type 取得第一个值 比如”衣服”,通过 where p1.product_type = p2.product_type 转入子查询:

SELECT AVG(sale_price)

FROM Product AS P2

WHERE P2.product_type = '衣服'

GROUP BY product_type);

这样获取到的结果就是价格的平均值

AVG(sale_price)
2500.0000
  • (3) 把这个数字接着返回主查询

SELECT product_type , product_name, sale_price

FROM Product AS P1

WHERE sale_price > 2500 AND product_type = '衣服'

结果

product_type product_name sale_price
衣服 运动 T 恤 4000

然后在以此类推


文章作者: 雾烟云
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 雾烟云 !
  目录