摘要
视图#1
子查询#2
标量子查询#3
关联子查询#4
如何使用SQL解决业务问题#5
各种函数#6
视图#1
什么是视图?用一句话概述的话,就是“从SQL的角度来看视图就是一张表”。但是使用视图时并不会将数据保存到存储设备之中,而且也不会将数据保存到其他任何地方。实际上视图保存的是SELECT语句,我们从视图中读取数据时,视图会在内部执行该SELECT语句并创建出一张临时表。
视图有什么用?视图的优点大体有两点:
第一:由于视图无需保存数据,因此可以节省存储设备的容量。。
第二:可以将频繁使用的SELECT语句保存成视图,这样就不用每次都重新书写了。创建好视图之后,只需在SELECT语句中进行调用,就可以方便地得到想要的结果了。特别是在进行汇总以及复杂的查询条件导致SELECT语句非常庞大时,使用视图可以大大提高效率。而且,视图中的数据会随着原表的变化自动更新。视图归根到底就是SELECT语句,所谓“参照视图”也就是“执行SELECT语句”的意思,因此可以保证数据的最新状态。这也是将数据保存在表中所不具备的优势。
本文内的大部分代码将以下面表的数据进行演练,分别是student、score。
student表
score表
如何创建视图?
通过数据库管理工具Navicat输入查询语句:
create view 视图名称(<视图列名1>,<视图列名2>,...)
as
<select 查询语句>;
练习:创建 按性别汇总 视图。
create view 按性别汇总(性别,人数)
as
select 性别,count(*)
from student
group by 性别;
创建完成后,在Navicat对应的数据库下刷新视图,就能显“按性别汇总的视图。
有哪些注意事项?
注意事项1:避免在视图下再创建视图,对多数DBMS来说,多重视图会降低SQL的性能。
注意事项2:不能往视图里插入数据,不然会报错。
子查询#2
什么是子查询?子查询以视图为基础。子查询的特点概括起来就是“一张一次性视图”,子查询将用来定义视图的SELECT语句直接用于FROM子句当中。
在FROM子句中直接写定义视图的SQL查询语句,运行顺序是先运行括号里面的再运行括号外面的。
练习:用子查询实现查询student表中各个性别的人数。
select 性别,人数
from(
select 性别,count(*) as 人数
from student
group by 性别
) as 按性别汇总;
如何使用子查询?
常见的子查询搭配有:...in(子查询)、...any(子查询)、...all(子查询)
练习:找出每个课程里 成绩最低的 学号。
当用select语句直接查找每个课程里 成绩最低的学号,这里select语句识别的 学号 会随机出现。
正确的方法是分解成两个步骤(分析思路):
-- 第1步:查找出每门课程的最低成绩有哪些值
select 课程号,min(成绩)
from score
group by 课程号
-- 第2步:在成绩表里查找这些值
select 学号,成绩
from score
where 成绩 in(80,60,80);
-- 合并后的SQL,括号里面的语句为子查询
select 学号,成绩
from score
where 成绩 in(
select min(成绩)
from score
group by 课程号
);
在使用any时需要与比较运算符一起使用
...any(子查询)与some(子查询)相同,一般表达式为:
select <列名1>
from <表名1>
where <列名1> > any(子查询);
练习:哪些学生的成绩比课程0002的全部成绩里的任意一个高呢?
(分析思路)
第1步:课程0002的全部成绩,比如是(10,30)——any括号里面的语句
select 成绩
from score
where 课程号 = '0002';
第2步:某个学生的成绩大于任意一个第1步里的成绩,就符合条件——任意一个高any(子查询)
select 学号,成绩
from score
where 成绩 > any(子查询);
完整代码:
select 学号,成绩
from score
where 成绩 > any(
select 成绩
from score
where 课程号='0002'
);
...all(子查询)
练习:哪些学生的成绩比课程0002的全部成绩里的都高呢?
select 学号,成绩
from score
where 成绩 > all(
select 成绩
from score
where 课程号 ='0002'):
注意事项:
1、 注意书写规则,a> 3*all(b) 为错误书写,a/3 > all(b) 为正确书写;
2、 select...from(子查询(子查询))层次嵌套,由于子查询的层数原则上没有限制,因此可以像“子查询的FROM子句中还可以继续使用子查询,该子查询的FROM子句中还可以再使用子查询……”这样无限嵌套下去。但是,随着子查询嵌套层数的增加,SQL语句会变得越来越难读懂,性能也会越来越差。因此,应该尽量避免使用多层嵌套的子查询。
3、子查询后连接as,可以添加子查询名称,用法为“as 子查询名称”,尽管as 子查询名称可以省略,但尽量养成良好的习惯,方便维护。
标量子查询#3
标量子查询有一个特殊的限制,就是必须而且只能返回1行1列的结果,也就是返回表中某一行的某一列的值。由于返回的是单一的值,因此标量子查询的返回值可以用在=或者<>这样需要单一值的比较运算符之中。这也正是标量子查询的优势所在。
标量子查询的书写位置并不仅仅局限于WHERE子句中,通常任何可以使用单一值的位置都可以使用。也就是说,能够使用常数或者列名的地 方,无论是SELECT子句、GROUP BY子句、HAVING子句,还是ORDER BY子句,几乎所有的地方都可以使用。
标量子查询通常与in、any、all、between共同实现查询条件。
练习:找出大于平均成绩学生的学号和成绩。
(以下代码会报错,原因是在where 子句中不能使用汇总函数,只能是单一的值)
select 学号,成绩
from score
where 成绩 > avg(成绩);
由于标量子查询中返回单一的值,所以可以使用运算符(分析思路):
-- 第1步:通过标量子查询,运行结果avg(成绩)=81.125
select avg(成绩)
from score
);
-- 第2步:查找大于81.125的成绩和学号
select 学号,成绩
from score
where 成绩 >81.125
-- 合并后的SQL,大于平均成绩学生的学号和成绩
select 学号,成绩
from score
where 成绩 > (
select avg(成绩)
from score
);
练习:找出差生(成绩<=60)优等生(成绩 >=80)的学号和成绩
select 学号,成绩
from score
where 成绩 between
(select avg(成绩)
from score
where 成绩 <=60)and
(selectr avg(成绩)
from score
where 成绩 >=80);
注意事项:
在一行select子句中不能使用多行数据的子句,也就是不能返回多行数据,不然会报错。原因是该子查询绝对不能返回多行结果,如果子查询返回了多行结果,那么它就不再是标量子查询,而仅仅是一个普通的子查询了,因此不能被用在=或者<>等需要单一输入值的运算符当中,也不能用在SELECT等子句当中。
关联子查询#4
关联子查询会在细分的组内进行比较时使用。关联子查询和GROUP BY子句一样,也可以对表中的数据进行切分。关联子查询的结合条件如果未出现在子查询之中就会发生错误。
在使用关联子查询时,需要在表所对应的列名之前加上表的别名,以“< 表名 >.< 列名 >”的形式记述。在对表中某一部分记录的集合进行比较时,就可以使用关联子查询。
练习:查找出每个课程中 大于对应课程平均成绩的学生。
select 学号,课程号,成绩
from score as s1
where 成绩 > (select avg(成绩)
-- 关联名称s2仅在子查询里有效
from score as s2
-- 关联条件
where s1.课程号 = s2.课程号
group by 课程号
);
使用关联子查询时,该子查询的结果必须是单一的。起到关键作用的就是在子查询中添加的WHERE子句的条件。该条件的意思就是,在每个课程中对应的成绩和平均成绩进行比较。
为什么外部的表别名s1可以在子查询里识别出来呢?
是由于关联名称的作用域在起作用,关联名称就是像s1、s2这样作为表别名的名称,作用域(scope)就是生存范围(有效范围)。也就是说,关联名称存在一个有效范围的限制。
子查询内部设定的关联名称,只能在该子查询内部使用。换句话说,就是“内部可以看到外部,而外部看不到内部”。
我们想得到一个查询结果,但机器并不能简单识别,因此我们需要创作一个连接他们的桥梁,这个桥梁就是关联子查询,这有点类似数学中虚数的概念,因此如果要掌握这种思维,便需要加强练习
《SQLZOO——SELECT within SELECT Tutorial》练习题7:
在每一個州中找出最大面積的國家,列出洲份 continent, 國家名字 name 及面積 area。 (有些國家的記錄中,AREA是NULL,沒有填入資料的。)
SELECT continent, name, area
FROM world x
WHERE area >= ALL
(SELECT area FROM world y
WHERE y.continent=x.continent
AND area>0)
《SQLZOO——SELECT within SELECT Tutorial》练习题8:
列出洲份名稱,和每個洲份中國家名字按子母順序是排首位的國家名。(即每洲只有列一國)
select continent,name
from world x
where x.name=
(select y.name
from world y
-- 通过条件限定,与group by类似,通过where y.continent=x.continent,达到把不同continent切分的效果
where y.continent=x.continent
order by name
limit 1);
《SQLZOO——SELECT within SELECT Tutorial》练习题9:
找出洲份,當中全部國家都有少於或等於 25000000 人口. 在這些洲份中,列出國家名字name,continent 洲份和population人口。
SELECT name, continent, population
FROM world x
WHERE 25000000>=ALL
(SELECT population
FROM world y
WHERE y.continent=x.continent
AND population>0);
如何使用SQL解决业务问题#5
1、翻译成大白话;
2、写出分析思路;
3、写出对应的sql子句。
练习:哪些学生的成绩比课程0002的全部成绩里的任意一个高呢?
第1步:课程0002的全部成绩,比如是(10,30)
select 查询结果【成绩】
from 从哪张表中查找数据【成绩表】
where 查询条件【课程号是0002】
group by 分组【没有】
having 对分组结果指定条件【没有】
order by 对查询结果排序【没有】
limit 从查询结果中取出指定行【没有】;
注意运行顺序:
select 成绩
from score
where 课程号 = '0002';
查找出课程0002的全部成绩
第2步:某个学生的成绩大于任意一个第1步里的成绩,就符合条件
比任意一个高,需要用到any(子查询)
select 学号,成绩
from score
where 成绩 > all (子查询);
两个步骤合并到一起的SQL是:
select 学号,成绩
from score
where 成绩 > ALL
(select 成绩
from score
where 课程号 ='0002');
《SQLZOO——SELECT within SELECT Tutorial》练习题6:
哪些國家的GDP比Europe歐洲的全部國家都要高呢? [只需列出 name 。] (有些國家的記錄中,GDP是NULL,沒有填入資料的。)
select name
from world
where gdp > all
-- 由于下面的子查询并不只返回单一值,因此需要在比较运算符后加上all
(select gdp
from world
where continent ='Europe'
and gdp >0);
当某些数据为NULL时,由于空值无法比较,因此需要加上 列名>0 这个条件。
《SQLZOO——SELECT within SELECT Tutorial》练习题10:
有些國家的人口是同洲份的所有其他國的3倍或以上。列出 國家名字name 和 洲份 continent。
select name,continent
from world x
-- 根据sql书写规则,算术运算符应该在比较运算符前面
where x.population/3 >= all
(select population
from world y
-- 建立子查询,虚构表y,其中表y的continent等于表x,等同于把continent切分,同一个continent的数据进行比较,表y的name不能等于表x,原因是表y的name需要与表x的name逐行比较,要设置name不一样才能进行相互比较
where x.continent=y.continent
and y.name<>x.name
and y.population>0)
各种函数#6
函数大致可以分为以下几种。
算术函数(用来进行数值计算的函数)
+ (加法)
- (减法)
* (乘法)
/ (除法)
字符串函数(用来进行字符串操作的函数)
日期函数(用来进行日期操作的函数)
转换函数(用来转换数据类型和值的函数)
“转换”这个词的含义非常广泛,在SQL中主要有两层意思:一是数据类型的转换,简称为类型转换,在英语中称为cast ;另一层意思是值的转换。
之所以需要进行类型转换,是因为可能会插入与表中数据类型不匹配的数据,或者在进行运算时由于数据类型不一致发生了错误,又或者是进行自动类型转换会造成处理速度低下。这些时候都需要事前进行数据类型转换。
聚合函数(用来进行数据聚合的函数)