SQL复杂查询
这边主要说到几种SQL的复杂查询
统计
我们对一些网站都会做一些统计数据,针对这些数据我们需要些一些统计sql 我们在对应代码中去请求数据库
这里可以使用SQL里面的视图 来方便查询
视图究竟是什么呢?如果用一句话概述的话,就是“从 SQL 的角度 来看视图就是一张表”。
那么视图和表到底有什么不同呢?区别只有一个,那就是“是否保存 了实际的数据
通常,我们在创建表时,会通过 INSERT 语句将数据保存到数据库 之中,而数据库中的数据实际上会被保存到计算机的存储设备(通常是硬盘)中。因此,我们通过 SELECT 语句查询数据时,实际上就是从存储设备(硬盘)中读取数据,进行各种计算之后,再将结果返回给用户这样一个过程。但是使用视图时并不会将数据保存到存储设备之中,而且也不会将数据保存到其他任何地方。我们从视图中读取数据时,视图会在内部执行该 SELECT 语句并创建出 一张临时表
表中存储的是实际数据,而视图中保存的是从表中取出数据所使用的SELECT语句。
视图无需保存数据,因此可以节省存储设备的容量, 还有一个优点就是可以将频繁使用的sql保存,利于复用
这里就不对视图的基本操作赘述 但是我们使用视图还是需要注意几点
-
视图是可以派生的 我们称为多重视图
但是我们不推荐使用 这样不利于理解 并且多重视图会降低SQL性能 应该避免在视图的基础上创建视图。
-
定义视图时不能使用 ORDER BY子句
1 2
CREATE VIEW ProductSum (product_type, cnt_product) AS SELECT product_type, COUNT(*) FROM Product GROUP BY product_type ORDER BY product_type;
为什么不能使用 ORDER BY 子句呢?这是因为视图和表一样,数据行都是没有顺序的
-
尽量不要对视图进行更新
尽管他和表很像,也不是不可以对它进行更新 当它满足特定条件可以对视图进行更新 但是我们没有必要去操作视图, 一般我们直接对数据表进行数据修改或添加即可
SQL子查询
- 子查询对于SQL的执行顺序
1
2
3
4
5
SELECT product_type, cnt_product FROM ( SELECT product_type, COUNT(*) AS cnt_product FROM Product GROUP BY product_type ) AS ProductSum;
子查询作为内层查询会首先执行。
例如这个查询 先执行FROM后面的() 可以想象成先查询一张表的数据出来 再对查询出来的表进行查询
由于子查询的层数没有上限,我们可以无限子查询,这让我想起面试题
写一个你见过最长的SQL 抛开性能的话 我们可以写很长
-
标量子查询
Q: 什么是标量? 标量就是单一的意思,在数据库之外的领域也经常
而标量子查询则有一个特殊的限制,那就是必须而且只能返回 1 行 1列的结果,也就是返回表中某一行的某一列的值
说一个例子就明白了
比较常见一个问题: 查询出销售单价高于平均销售单价的商品
1
2
3
4
5
6
7
8
9
10
11
SELECT product_id, product_name, sale_price FROM Product WHERE sale_price > AVG(sale_price);
可是这样是不被允许的 WHERE子句后面是不可以带聚合函数的 那我们怎么去编写一个符合条件的SQL呢
这个时候我们用到标量子查询
1. SELECT AVG(sale_price) FROM Product; 这是一条查商品平均值的SQL
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 等子句当中
1
2
3
4
5
6
7
8
9
SELECT product_id, product_name, sale_price,(SELECT AVG(sale_price) FROM Product GROUP BY product_type) AS avg_price FROM Product;
例如这样
avg
----------------------
2500.0000000000000000
300.0000000000000000
2795.0000000000000000
这个子查询会返回多条数据 那碰到这种怎么办呢? 我们可以使用关联子查询
关联子查询
普通的子查询和关联子查询的区别
按此前所学,使用子查询就能选取出销售单价(sale_price)高于全部商品平均销售单价的商品。这次我们稍稍改变一下条件,选取出各商品种类中高于该商品种类的平均销售单价的商品。
- 按照商品种类与平均销售单价进行比较
按照商品种类计算平均价格
1
SELECT AVG(sale_price) FROM Product GROUP BY product_type;
如果我们使用前面(标量子查询)的方法,直接把上述SELECT 语句使用到 WHERE 子句当中的话,就会发生错误。
– 发生错误的子查询
1
2
3
SELECT product_id, product_name, sale_price FROM Product WHERE sale_price > (SELECT AVG(sale_price) FROM Product GROUP BY producttype);
出错原因上面已经说明过了
– 我们看一下修改后的SQL
1
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 子句的条件
该条件的意思就是,在同一商品种类中对各商品的销售单价和平均单价进行比较。
这次由于作为比较对象的都是同一张 Product 表,因此为了进行区别,分别使用了 P1 和 P2 两个别名。在使用关联子查询时,需要在表所对应的列名之前加上表的别名,以“< 表名 >.< 列名 >”的形式记述。在对表中某一部分记录的集合进行比较时,就可以使用关联子查询。因此,使用关联子查询时,通常会使用“限定(绑定)”或者“限制”这样的语言,例如本次示例就是限定“商品种类”对平均单价进行比较。
在细分的组内进行比较时,需要使用关联子查询 写关联子查询时 结合条件一定要写在子查询中