前面的章节介绍了数据更新操作,今天介绍MySQL的复杂查询。复杂查询主要包括视图、子查询和关联子查询
目录
1.视图
先介绍一下视图:
- 从SQL的角度来看,视图和表是相同的,两者的区别在于表中存储的是实际的数据,而视图中保存的是SELECT语句(视图本身并不存储数据)。
- 使用视图可以轻松完成跨多表查询数据等复杂操作。
- 视图中不能包含ORDER BY和对视图的更新操作(INSERT、DELETE、UPDATE)
1.1创建视图的方法
CREATE VIEW 视图名称(<视图列名1><视图列名2><视图列名3>......)
AS
<SELECT语句>
例如:创建一个视图
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;
我们这里解释一下,我们首先通过创建了一个视图Productsum,然后通过SELECT语句获取视图中的数据,这样我们就不用了每次需要数据时都SELECT创建新的数据表,需要注意的是,原表product的数据产生变化后,视图数据也会随着变化,非常方便。
之所以能够实现上述功能,是因为视图就是保存好的SELECT语句定义视图时可以使用任何SELECT语句,既可以使用WHERE,GROUP BY,HAVING,也可以通过SELECT*来指定全部列。
1.2使用视图的查询
在FROM子句中使用视图的查询,通常有如下两个步骤:
- 首先执行定义视图的SELECT语句。(创建视图)
- 根据得到的结果,再执行在FROM子句中使用视图的SELECT语句。(从视图中取数据)
也就是说视图的查询通常需要执行2条以上的SELECT语句。这里没有用两条,而用了两条以上,是因为还可能出现以视图为基础创建视图的多重视图。例如:我们可以以productsun视图创建productsumjim视图。
--基于productsum创建新的视图
CREATE VIEW productsumjim(product_type,cnt_product)
AS
SELECT product_type,cnt_product
FROM productsum
WHERE product_type="办公用品";
--查看视图
SELECT product_type,cnt_product
FROM productsumjim;
结果如下:
虽然语法上没有错误,但是我们还是尽量少使用在视图上创建视图,这是因为对于多数DBMS来说,多重视图会降低SQL的性能。因此还是建议大家使用单一视图。
1.3视图的限制 ——定义时不能使用ORDER BY
虽然我们前面说过定义视图时我们能使用任何SELECT语句,但是有一点例外就是ORDER BY,因此下面的语法是错误的。
CREATE VIEW productsum(product_type,cnt_product)
AS
SELECT product_type,COUNT(*)
FROM product
GROUP BY product_type
OEDER BY product_type --这里是错误的,不能使用ORDER BY语句。
1.4视图的限制——对视图的更新
对视图的更新有着很严格的限制:
- SELECT子句中未使用DISTINCT
- FROM子句中只有一张表
- 未使用GROUP BY子句
- 未使用HAVING子句
接下来,我们就对视图进行更新。
--创建一个视图
CREATE 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')
我们可以看见不仅在原数据表product中添加了数据,视图中也添加了数据。
1.5删除视图
DROP VIEW 视图名称(<视图列名1>,<视图列名2>,......)
DROP VIEW productsum;
删除成功。
2.子查询
我们先来说一下子查询和视图:子查询是将用来定义视图的SELECT语句直接用于FROM语句中。
我们使用子查询来实现一个视图
--在from子句中直接书写定义视图的SELECT语句
SELECT product_type,cnt_product
FROM(SELECT product_type,COUNT(*) AS cnt_product
FROM product
GROUP BY product_type) AS productsum;
可以看出结果一模一样。但是有一个不同点:子查询是一张一次性视图,在数据库中并不会创建productsum,下次使用时需要重新定义。
这里我们使用了一个两层的子查询,原则上来说,子查询的层数没有明确的限制。
--一个三层的子查询
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;
成功运行:
2.1 子查询的名称
原则上,子查询必须设定名称,并使用AS关键字进行名称的设定,该关键字有时候也可以省略。
2.2 标量子查询
标量就是单一的意思,即是指必须而且只能返回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);
2.3 子查询的书写位置
标量子查询的书写位置并不仅仅局限于WHERE子句中,通常任何可以使用单一值位置都可以使用,也就是说,能够使用常数或者列名的地方,无论是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);
这里我们是查询某一种类的产品平均销售价格大于所有产品的平均销售价格。
2.4标量子查询时的注意事项
子查询绝对不能返回多行结果,一旦出现多行,就不再是标量子查询,不能用= > <等需要单一值运算符中,也不能出现在SELECT等句子当中。
3.关联子查询
介绍一下关联子查询:
- 关联子查询会在细分的组内进行比较时使用。
- 关联子查询和GROUP BY子句一样,也可以对表中的数据进行切分。
- 关联子查询的结合条件如果未出现在子查询中就会发生错误。
例如:我们查询各种商品种类中高于该种类的平均销售单价的商品。
--发生错误的语句
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);
结果如下:
这里起到的关键作用就是在子查询中添加的WHERE子句条件。该条件的意思就是在同一商品种类中对各商品的销售单价和平均单价进行比较。
参考资料
图书:《SQL基础教程》