子查询
我们先来一个例子
SELECT
stu_name
FROM
( SELECT stu_name, count( * ) AS stu_cnt FROM stuinfos_info GROUP BY stu_age ) AS studentSum;
这个语句看起来很好理解,其中使用括号括起来的sql语句首先执行,执行成功后再执行外面的sql语句。但是我们上一节提到的视图也是根据SELECT语句创建视图然后在这个基础上再进行查询。那么什么是子查询呢?子查询和视图又有什么关系呢?
什么是子查询
子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从 MySQL 4.1 开始引入,在 SELECT 子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。
子查询与视图之间的关系
子查询就是将用来定义视图的 SELECT 语句直接用于 FROM 子句当中。其中AS studentSum可以看作是子查询的名称,而且由于子查询是一次性的,所以子查询不会像视图那样保存在存储介质中, 而是在 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;
其中最内层的子查询我们将其命名为productSum,这条语句根据product_type分组并查询个数,第二层查询中将个数为4的商品查询出来,最外层查询product_type和cnt_product两列。 虽然嵌套子查询可以查询出结果,但是随着子查询嵌套的层数的叠加,SQL语句不仅会难以理解而且执行效率也会很差,所以要尽量避免这样的使用。
标量子查询
标量就是单一的意思,那么标量子查询也就是单一的子查询,那什么叫做单一的子查询呢?
所谓单一就是要求我们执行的SQL语句只能返回一个值,也就是要返回表中具体的某一行的某一列。例如我们有下面这样一张表
product_id | product_name | sale_price
------------+-------------+----------
0003 | 运动T恤 | 4000
0004 | 菜刀 | 3000
0005 | 高压锅 | 6800
那么我们执行一次标量子查询后是要返回类似于,“0004”,“菜刀”这样的结果。
标量子查询有什么作用
让我们看如何通过标量子查询语句查询出销售单价高于平均销售单价的商品。
SELECT product_id, product_name, sale_price
FROM 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_price
FROM
product;
关联子查询
- 什么是关联子查询
关联子查询既然包含关联两个字那么一定意味着查询与子查询之间存在着联系。这种联系是如何建立起来的呢?
我们先看一个例子:
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);
通过上面的例子我们大概可以猜到吗,关联子查询就是通过一些标志将内外两层的查询连接起来起到过滤数据的目的,接下来我们就一起看一下关联子查询的具体内容吧。
- 关联子查询与子查询的联系
查询出销售单价高于平均销售单价的商品,这个例子的SQL语句如下
select product_id,product_name,sale_price
from product
where sale_price > select(avg(sale_price)) from product;
选取出各商品种类中高于该商品种类的平均销售单价的商品
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);
可以看出上面这两个语句的区别吗?
在第二条SQL语句也就是关联子查询中我们将外面的product表标记为p1,将内部的product设置为p2,而且通过WHERE语句连接了两个查询。
但是如果刚接触的话一定会比较疑惑关联查询的执行过程,这里有一个博客讲的比较清楚。在这里我们简要的概括为:
1.首先执行不带WHERE的主查询
2.根据主查询讯结果匹配product_type,获取子查询结果
3.将子查询结果再与主查询结合执行完整的SQL语句
在子查询中像标量子查询,嵌套子查询或者关联子查询可以看作是子查询的一种操作方式即可。
练习题
1、
创建出满足下述三个条件的视图(视图名称为 ViewPractice5_1)。使用 product(商品)表作为参照表,假设表中包含初始状态的 8 行数据。
- 条件 1:销售单价大于等于 1000 日元。
- 条件 2:登记日期是 2009 年 9 月 20 日。
- 条件 3:包含商品名称、销售单价和登记日期三列。
对该视图执行 SELECT 语句的结果如下所示。
create view viewpractice5_1 as
select product_name,sale_price,regist_date
from product
where sale_price >=1000 and regist_date = '2009-09-20';
select * from viewpractice5_1;
2、
向习题一中创建的视图 ViewPractice5_1 中插入如下数据,会得到什么样的结果呢?
INSERT INTO ViewPractice5_1 VALUES (' 刀子 ', 300, '2009-11-02');
视图不能进行添加记录
3、
请根据如下结果编写 SELECT 语句,其中 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,sale_price,avg(sale_price) sale_price_all
from product ;
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
CREATE VIEW AvgPriceByTyp ( 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 p1 WHERE p1.product_type = p2.product_type GROUP BY product_type ) AS avg_sale_price
FROM
product AS p2;