文章目录

  • 前言
  • 1. 视图
    • 1.1 什么是视图
    • 1.2 视图与表有什么区别
    • 1.3 为什么会存在视图
    • 1.4 如何创建视图
    • 1.5 如何修改视图结构
    • 1.6 如何更新视图内容
  • 2. 子查询
    • 2.1 什么是子查询
    • 2.2 子查询和视图的关系
    • 2.3 嵌套子查询
    • 2.4 标量子查询
    • 2.5 标量子查询有什么用
    • 2.6 关联子查询
  • 总结

前言

复杂的MySQL查询使用多个参数来搜索数据,并且可能包含多个表之间的多个联接以及相当多的嵌套子查询(嵌套在另一查询中 的查询)的组合。复杂查询还经常涉及大量使用AND和OR子句。

复杂查询通常用于从多个表中检索复杂数据。高级查询还可以用于报告,联接多个表,嵌套查询和事务锁定。


1. 视图

先来看一个查询语句:

SELECT stu_name FROM view_students_info;

单从表面上看起来这个语句是和正常的从数据表中查询数据是完全相同的,但其实我们操作的是一个视图。所以从SQL的角度来说操作视图与操作表看起来是完全相同的。

Q:那么为什么还会有视图的存在呢?视图到底是什么?视图与表有什么不同呢?

1.1 什么是视图

视图是一个虚拟的表,不同于直接操作数据表,视图是依据SELECT语句来创建的,所以操作视图时会根据视图的SELECT语句生成一张虚拟表,然后在这张虚拟表上做SQL操作。

1.2 视图与表有什么区别

有一句话非常凝练的概括了视图与表的区别——“是否保存了实际的数据”。所以视图并不是数据库真实存储的数据表。它可以看作是一个窗口,通过这个窗口我们可以看到数据库表中真实存在的数据。所以我们要区别视图和数据表的本质,即视图是基于真实表的一张虚拟的表,其数据来源均建立在真实表的基础上。

1.3 为什么会存在视图

主要有以下几点原因:

  1. 通过定义视图可以将频繁使用的SELECT语句保存以提高效率;
  2. 通过定义视图可以使用户看到的数据更加清晰;
  3. 通过定义视图可以不对外公开数据表全部字段,增强数据的保密性;
  4. 通过定义视图可以降低数据的冗余。

1.4 如何创建视图

创建视图的基本语法如下:

CREATE VIEW <视图名称>(<列名1>,<列名2>,...) AS <SELECT语句>

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

注意: 视图名称是在数据库中是唯一的,不能与其他视图和表重名。
视图不仅可以基于真实表,也可以在视图基础上继续创建视图。


虽然在视图上继续创建视图的语法没有错误,但是还是应该尽量避免这种操作。这是因为对多数DBMS 来说,多重视图会降低SQL 的性能。

注意事项:
需要注意的是在一般的DBMS中定义视图时不能使用ORDER BY 语句。下面这样定义视图时错误的。

CREATE VIEW product_sum(product_type, cnt_product)
AS
SELECT product_type, COUNT(*)FROM product
GROUP BY product_type
ORDER BY product_type;

Q:为什么不能使用ORDER BY子句?
A:因为视图和表是一样的,数据行都是没有顺序的。

基于单表查询的视图
在product表的基础上创建一个视图,代码如下:

CREATE VIEW productsum(product_type, cnt_product)
AS
SELECT product_type, COUNT(*)FROM product
GROUP BY product_type;

创建的视图如下图所示:

基于多表的视图
为了学习多表视图,再创建一张表,相关代码如下:

-- 创建一张shop_product 表
create table shop_product
(shop_id char(4) NOT NULL,shop_name varchar(200) NOT NULL,product_id char(4) NOT NULL,quantity INTEGER NOT NULL,primary key(shop_id, product_id));INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000A', '东京', '0001', 30);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000A', '东京', '0002', 50);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000A', '东京', '0003', 15);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000B', '名古屋', '0002', 30);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000B', '名古屋', '0003', 120);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000B', '名古屋', '0004', 20);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000B', '名古屋', '0006', 10);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000B', '名古屋', '0007', 40);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000C', '大阪', '0003', 20);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000C', '大阪', '0004', 50);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000C', '大阪', '0006', 90);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000C', '大阪', '0007', 70);
INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) values ('000D', '福冈', '0001', 100);

在product表和shop_product表的基础上创建视图。

create view view_shop_product(product_type, sale_price, shop_name)
as
select product_type, sale_price, shop_namefrom product,shop_product
where product.product_id = shop_product.product_id;

创建的视图如下:

在这个视图的基础上进行查询:

select sale_price, shop_namefrom view_shop_product
where product_type = '衣服';

1.5 如何修改视图结构

修改视图结构的基本语法如下:

ALTER VIEW<视图名> AS <SELECT语句>

当然视图名在数据库中需要是唯一的,不能与其他视图和表重名。
当然也可以通过将当前视图删除然后重新创建的方式达到修改的效果。(对于数据库底层是不是也是这样的呢,可以自己探索一下。)

修改视图

修改上方的productsum视图为

ALTER VIEW productsumASSELECT product_type, sale_priceFROM product
WHERE regist_date > '2009-09-11';

此时productsum视图内容如下图所示

1.6 如何更新视图内容

因为视图是一个虚拟表,所以对视图的操作就是对底层基础表的操作,所以在修改时只有满足底层基本表的定义才能成功修改。
对于一个视图来说,如果包含以下结构的任意一种都是不可以被更新的:

  • 聚合函数SUM()、MIN()、MAX()、COUNT()等
  • DISTINCT关键字
  • GROUP BY 子句
  • HAVING 子句
  • UNION 或 UNION ALL 运算符
  • FROM 子句中包含多个表

视图归根结低还是从表派生出来的,因此,如果原表可以更新,那么视图中的数据也可以更新。反之亦然,如果视图发生了改变,而原表没有进行相应更新的话,就无法保证数据的一致性了。

更新视图

因为我们刚刚修改的productsum视图不包括以上的限制条件,我们来尝试更新一下视图。

UPDATE productsumSET sale_price = '5000'
WHERE product_type = '办公用品';

未执行update语句前的productsum视图:

执行update语句之后,再来查看productsum视图,可以发现数据以及更新了:

注意: 如若mysql执行update语句会报错,报错如下:

Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. To disable safe mode, toggle the option in Preferences

这是因为MySQL运行在 safe-updates 模式下,该模式会导致非主键条件下无法执行 update 或者 delete 命令。

可以通过以下SQL进行状态查询:

show variables like 'SQL_SAFE_UPDATES';


这里显示sql_safe_updates 模式为打开状态。

解决办法:

  • 执行下面的SQL,关闭 safe-updates 模式:
set sql_safe_updates = 0;
-- 或者
set sql_safe_updates = false;
  • 执行下面的SQL 语句,打开 safe-updates 模式:
set sql_safe_updates = 1;
-- 或者
set sql_safe_updates = true;

2. 子查询

代码如下(示例):

SELECT stu_name
FROM (SELECT stu_name, COUNT(*) AS stu_cntFROM students_infoGROUP BY stu_age) AS studentsum;

这个语句看起来很好理解,其中使用括号括起来的SQL语句首先执行,执行成功后再执行外面的SQL语句。上一节提到的视图也是根据SELECT语句创建视图然后在这个基础上在进行查询。
Q:什么是子查询呢?子查询和视图又有什么关系呢?

2.1 什么是子查询

子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从 MySQL4.1开始引入,在SELECT 子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。

2.2 子查询和视图的关系

子查询就是将用来定义视图的 SELECT语句直接用于 FROM子句当中。其中AS studentsum可以看作是子查询的名称,而且由于子查询是一次性的,所以子查询不会像视图那样保存在存储介质中,而是在 SELECT 语句执行之后就消失了。

2.3 嵌套子查询

与在视图上再定义视图类似,子查询也没有具体的限制,例如:

SELECT product_type, cnt_product
FROM (SELECT *FROM (SELECT product_type,COUNT(*) AS cnt_productFROM productGROUP BY product_type) AS productsumWHERE cnt_product = 4) AS productsum2;

其中最内层的子查询我们将其命名为productsum,这条语句根据product_type分组并查询个数,第二层查询中将个数为4的商品查询出来,最外层查询product_type和cnt_product两列。

虽然嵌套子句可以查询出结果,但是随着子查询嵌套层数的叠加,SQL语句不仅会难以理解而且执行效率也会很差,所以要尽量避免这样的使用。

2.4 标量子查询

标量就是单一的意思,那么标量子查询也就是单一的子查询。
Q:那什么叫做单一的子查询呢?
A:所谓单一就是要求我们执行的SQL语句只能返回一个值,也就是要返回表中具体的某一行的某一列。

例如,我们有下面这有一张表:

product_id product_name sale_price
0003 运动T恤 4000
0004 菜刀 3000
0005 高压锅 6800

那么我们执行一次标量子查询后是要返回类似于,“0004”,“菜刀”这样的结果。

2.5 标量子查询有什么用

那知道了标量子查询可以返回一个值了,那么它有什么作用呢?
先来看几个具体的需求:

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

Q:看如何通过标量子查询语句查询出销售单价高于平均销售单价的商品?

代码如下:

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

运行结果如图:

上面的这条语句首先后半部分查询出product表中的平均售价,前面的sql语句在根据WHERE条件挑出合适的商品。

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

还可以这样使用标量子查询:

SELECT product_id,product_name,sale_price,(SELECT AVG(sale_price)FROM product) AS avg_priceFROM product;

运行结果如下图:

2.6 关联子查询

什么是关联子查询

关联子查询既然包含关联两个字那么一定意味着查询与子查询之间存在这联系。这种联系是如何建立起来的呢?

先来看一个例子:

SELECT product_type, product_name, sale_priceFROM product AS p1
WHERE sale_price > (SELECT AVG(sale_price)FROM product AS p2WHERE p1.product_type = p2.product_typeGROUP BY product_type);

运行结果:

通过上面的例子,我们知道关联子查询就是通过一些标志将内外两层的查询连接起来起到过滤数据的目的,接下来我们就一起看一下关联子查询的具体内容吧。

关联子查询与子查询的联系

查询出销售单价高于平均销售单价的商品的SQL语句如下:

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

运行结果如图:

再来看一下这个需求:选取出各商品种类中高于该商品种类的平均销售单价的商品的SQL语句如下:

SELECT product_type, product_name, sale_priceFROM product AS P1
WHERE sale_price > (SELECT AVG(sale_price)FROM product AS p2
WHERE p1.product_type = p2.product_typeGROUP BY product_type);

运行结果如图:

上面这两个语句的区别:

在第二条SQL语句也就是关联子查询我们将外面的product表标记为p1,将内部的product设置为p2,而且通过WHERE 语句连接了两个查询。

但是如果刚接触的话一定会比较疑惑关联查询的执行过程,可以通过这篇文章了解。在这里可以简要的概况为:

  1. 首先执行不带WHERE的主查询;
  2. 根据主查询的结果匹配product_type,获取子查询的结果;
  3. 将子查询结果再与主查询结合执行完整的SQL语句。

在子查询中像标量子查询,嵌套子查询或者关联子查询可以看作是子查询的一种操作方式即可。


总结

视图和子查询是数据库操作中较为基础的内容,对于一些复杂的查询需要使用子查询加一些条件语句组合才能得到正确的结果。但是无论如何对于一个SQL语句来说都不应该设计的层数非常深且特别复杂,不仅可读性差而且执行效率也难以保证,所以尽量有简洁的语句来完成需要的功能。

SQL学习笔记——task3:MySQL复杂查询相关推荐

  1. 【数据库和SQL学习笔记】6.SELECT查询4:嵌套查询、对查询结果进行操作

    数据库系统软件:SQL Server 2019 Express 操作系统:Windows 10 文章目录 嵌套查询 IN 与 NOT IN EXISTS 与 NOT EXISTS 比较运算符 SOME ...

  2. 经典SQL学习笔记 (四)-子查询

    子查询 当一个查询是另一个查询的条件时,称之为子查询.子查询可以使用几个简单命令构造功能强大的复合命令.子查询最常用于SELECT-SQL命令的WHERE子句中.子查询是一个 SELECT 语句,它嵌 ...

  3. 三、MySQL子查询学习笔记(标量子查询、列子查询、行子查询、表子查询 详解)

    三.MySQL子查询学习笔记 7:子查询 含义: 一条查询语句中又嵌套了另一条完整的select语句,其中被嵌套的select语句,称为子查询或内查询:在外面的查询语句,称为主查询或外查询 分类: 一 ...

  4. 简简单单 My SQL 学习笔记(2)——分组和简单数据的查询

    初始数据 创建表(要记得先选择好我们的数据库 use+数据库名) create table student( studentno int(4) primary key not null auto_in ...

  5. JavaWeb-综合案例(用户信息)-学习笔记01【列表查询】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb-综合案例(用户信息)-学习笔记01[列表查询] JavaWeb-综合案例(用户信息)-学习笔记02[登录功能] JavaWeb-综合案 ...

  6. 探针一号的SQL学习笔记

    SQL学习笔记 文章目录 SQL学习笔记 1.问题 2.SQL基本语句 3.SQL基本数据查询 4.SQL复杂点的数据查询 5.DML操作 6.数据表操作 1.问题 什么是数据库? 是一个仓库,可以按 ...

  7. 架构mysql_MySQL学习笔记之MySQL架构

    MySQL 最重要.最与众不同的特性是它的存储引擎架构,这种架构的设计将查询处理及其他系统任务和数据的存储/提取相分离.这种处理和存储分离的设计可以在使用时根据性能.特性,以及其他需求来选择数据存储的 ...

  8. 判断题:oracle自带的sql语言环境是pl/sql,Oracle之PL/SQL学习笔记之数据类型(三)

    Oracle之PL/SQL学习笔记之数据类型(三) 所有的编程语言中变量是使用最频繁的.PL/SQL作为一个面向过程的数据库编程语言同样少不了变量,利用变量可以把PL/SQL块需要的参数传递进来,做到 ...

  9. IBatis.Net学习笔记六--再谈查询

    在IBatis.Net学习笔记五--常用的查询方式 中我提到了一些IBatis.Net中的查询,特别是配置文件的写法. 后来通过大家的讨论,特别是Anders Cui 的提醒,又发现了其他的多表查询的 ...

最新文章

  1. 如何删除mac通用二进制文件
  2. Windows 8 应用开发 - 本地数据存储
  3. AngularJS学习笔记
  4. Linux基础之-网络配置,主机名设置,ssh登陆,scp传输
  5. express路由管理的几种自动化方法分享-js教程-PHP中文网
  6. oracle选择外键列,Oracle外键列上是否需要索引?
  7. Python XML解析
  8. AngularJS: 自定义指令与控制器数据交互
  9. 程序员面试金典——11.3元素查找
  10. java bean规范 is_深入了解JavaBean规范中的属性名和setter/getter方法
  11. C#会员管理系统源码 会员消费管理系统源码
  12. C# WPF仿360安全卫士11
  13. 明白这30条人生道理,你就超过了80%的男人
  14. springboot打包-依赖包到单独文件夹并微缩原有jar包体积
  15. 初学者Arcgis教程大全
  16. 畅享9能改鸿蒙,华为畅享9 root教程_畅享9卡刷root包来获取root权限的方法
  17. 在立创商城上快速制作PCB原理图库
  18. 史上最长的介绍BI(商业智能)的干货
  19. 五种常见的嵌入式操作系统
  20. 【NOIP2015提高组】信息传递

热门文章

  1. 有趣-如何解决递归过多导致的栈溢出
  2. 代码覆盖率测试gcov和lcov
  3. php 声波图,绘制声波图(获取音频回调)
  4. ev3怎么对场地进行测量_测量场地可靠性
  5. CentOS 下安装openOffice,并在Linux上实现office转PDF
  6. 如何用阿波波的重复朗读来联系英语口语
  7. python计算夏令时的具体日期_Python中的夏令时
  8. php获取指定目录下所有文件列表,PHP 获取指定目录下所有文件(包含子目录)...
  9. 管道无损检测python_利用神经网络技术检测半透明材料缺陷的新方法
  10. java getuserinfo_Java URI getRawUserInfo()用法及代码示例