注:本文的SQL语言适用的是Oracle数据库与mySQL可能存在略微不同
前言知识
一、关键词执行顺序
很多小伙伴在学习SQL语言后最苦恼的一个问题就是SQL语句中各关键词执行顺序的问题
针对这个问题,在练习开始前,我们先来讲一讲
SQL书写顺序
SELECT <字段名> FROM <表名> JOIN <表名> ON <连接条件> WHERE <筛选条件> GROUP BY <字段名> HAVING <筛选条件> UNION ORDER BY <字段名> LIMIT <限制行数>;
执行顺序
1、FROM:选择from后面跟的表,产生虚拟表1
2、ON:ON是JOIN的连接条件,符合连接条件的行会被记录在虚拟表2中
3、JOIN:如果指定了LEFT JOIN,那么保留表中未匹配的行就会作为外部行添加到虚拟表2中,产生虚拟表3。如果有多个JOIN链接,会重复执行步骤1~3,直到处理完所有表
4、WHERE:对虚拟表3进行WHERE条件过滤,符合条件的记录会被插入到虚拟表4中
5、GROUP BY:根据GROUP BY子句中的列,对虚拟表2中的记录进行分组操作,产生虚拟表5
6、HAVING:对虚拟表5进行HAVING过滤,符合条件的记录会被插入到虚拟表6中
7、SELECT(包括聚集函数):SELECT到一步才执行,选择指定的列,插入到虚拟表7中
8、UNION:UNION连接的两个SELECT查询语句,会重复执行步骤1~7,产生两个虚拟表7,UNION会将这些记录合并到虚拟表8中
9、ORDER BY:将虚拟表8中的记录进行排序,虚拟表9
10、LIMIT:取出指定行的记录,返回结果集
二、笛卡尔积同名属性处理(Oracle)
select pub.student.name sname,pub.course.name cname from pub.student,pub.course
关键点:
1、Oracle中更名不用as,直接用空格即可
2、oracle中存在同名属性的关系进行笛卡尔积必须要对同名列更名。一般书写中可以默认会修改为关系名.属性名来代替原属性名
三、SQL中关系的属性调用
select e.id,e.person_name from employee e join manages m on e.id=m.id where exists( select 1 from employee where m.manager_id=id and e.city=city e.street=street )
关键点:
1、对于SQL中自然连接或特殊连接后得到的新关系,对其属性的调用可以直接利用关系名.属性名来调用新关系中的属性(要在关系的嵌套内,才可以利用关系名去调用属性名)
2、脱离关系代数中对于属性的修改、更名、删除的思维方式:
a.关系代数中笛卡尔积、连接后有固定的方式对新关系的属性进行处理。
b.SQL中新关系不用管新关系中属性的名称,一律可以用关系名来调用
3、关系的笛卡尔积可以用exists+嵌套关系来实现
4、natrual join后同名属性会删除;利用笛卡尔积+筛选两个属性名都会保留;利用join on来实现两个同名属性也都会保留
四、SQL中子查询作用域问题
这个也是初次接触SQL语言的新手常犯的错误。因为我们知道像having等语句内部本身有一个作用域,但是在语句内部中加入select子查询后,整个语句的作用域会发生变化。
select * from works group by company_name having ( select id,name from works where salary>avg(salary) )
本题就是一个典型的错误:
1、having 内的avg聚集函数,本身的作用域是分组后的各组内,但是加入select子查询后,avg聚集函数的作用域变为子查询所提供的works完整作用域
2、having后面的返回值是bool类型的,所以必须有表达式
练习题
注:
1、为保证学习效率,同一题中相同图表会重复放置,方便查阅
2、本文代码将自然连接用笛卡尔积+筛选实现,这是实际工作的好习惯
3.9
请看图 3.19 中的关系数据库,其中的主键用下划线标出,用 SQL 给出下列每个查询的表达式。
a. 查找在 "第一银行公司 "工作的每位员工的编号、姓名和居住城市
select id,person_name,city from employee, works where employee.id=works.id and company_name='First Bank Corporation'
b. 找出在 "第一银行公司 "工作且收入超过 $10000 的每位员工的编号、姓名和居住城市
select id,person_name,city from employee, works where employee.id=works.id and company_name='First Bank Corporation' and salary > 10000
c. 找出每位不在 "第一银行公司 "工作的员工的编号(两个写法)
( select id from employee ) minus ( select id from employee,works where employee.id=works.id and company_name='First Bank Corporation' )
select id from employee where id not in( select id from works where company_name='First Band Corporation' )
d. 找出所有收入超过 "小型银行公司 "每个员工的员工 ID
select id from work where salary > ( select max(salary) from works where company_name='Small Bank Corporation' )
e. 假设一个公司可以在好几个城市有分部。找出位于“Small Bank Corporation”所在城市的所有公司(company_name是主键,所以一家公司只能在一个城市)
Select company_name From Company Where city = ( select city From company Where company_name = 'Small Bank Corporation' )
f.找出雇员最多的公司名称(或公司名称、在最多员工数相同的情况下)的公司名称
select company_name from works group by company_name having count(id)>all( select count(id) from works group by company_name )
g. 找出员工平均工资高于 “First Bank Corporation”平均工资的每家公司的名称
select company_name from works group by company_name having avg(salary)>( select avg(salary) from works where company_name='First Bank Corporation' )
3.10
考虑图 3.19 中的关系数据库。用 SQL 语句表达表达式:
a.修改数据库,使 ID 为 "12345 "的员工现在住在 "Newtown"镇
update employee set city='Newtown' where id='12345'
b. 给 "第一银行公司 "的每位经理加薪 10%,除非其工资超过 100000 美元。在这种情况下,只加薪 3%(这两个update的顺序不可以更换)(增加时先加大的,减少时先减少的;防止有的记录被操作两次)
update works as T set T.salary=t.salary*1.03 where works.id in ( select id from manages ) and salary>100000 and company_name='First Bank Corporation' update works as T set T.salary=t.salary*1.1 where works.id in ( select id from manages ) and salary<100000 and company_name='First Bank Corporation'
3.16
请看图 3.19 中的雇员数据库,其中的主键用下划线标出,为以下每个查询给出一个 SQL 表达式
a.查找与工作公司所在城市相同的每位员工的 ID 和姓名
select id,person_name from employee,works,company where employee.id=works.id and works.company_name=company.company_name and employee.city=company.city
b.查找与其经理住在同一城市同一条街上的每位员工的 ID 和姓名
select e.id,e.person_name from employee e join manages m on e.id=m.id where exists( select 1 from employee where m.manager_id=id and e.city=city and e.street=street )
c.找出每位收入高于其所在公司所有员工平均工资的员工的身份信息和姓名
select id,name from works w,employee e where w.id=e.id and salary >( select avg(salary) from works s where w.company_name=s.company_name )
d. 找出工资总额最小的公司
select company_name from works group by company_name having ( sum(salary)<all( select sum(salary) from works group by company_name ) )
总结
如果能帮助到大家,大家可以点点赞、收收藏呀~