多表查询
多表查询,又称表联合查询,即一条sql语句涉及到的表有多张,数据通过特定的连接进行联合显示.
笛卡尔积
在数学中,两个集合X和Y的笛卡尓积(Cartesian product),又称直积,表示为X × Y.
假设集合A={a, b},集合B={0, 1, 2},则两个集合的笛卡尔积为{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。
在数据库中,如果直接查询俩张表,那么其查询结果就会产生笛卡尔积
例如:
select *
from s_emp,s_dept;
连接查询
为了在多表查询中避免笛卡尔积的产生,我们可以使用连接查询来解决这个问题.
连接查询分为:
1.等值连接
2.不等值连接
3.外连接
左外连接
右外连接
全外连接
4.自连接
1.等值连接
利用一张表中某列的值和另一张表中某列的值相等的关系,把俩张表连接起来。
例如:查询员工的名字、部门编号、部门名字
select last_name,dept_id,name
from s_emp,s_dept
where s_emp.dept_id=s_dept.id;
为了表述的更加清楚,可以给每张表起别名
select se.last_name,se.dept_id,sd.id,sd.name
from s_emp se,s_dept sd
where se.dept_id=sd.id;
2.不等值连接
假设数据库中还有一张工资等级表:salgrade
工资等级表salgrade:
gradeName列表示等级名称
losal 列表示这个级别的最低工资数
hisal 列表示这个级别的最高工资数
表中的数据类似于下面内容:
id salgrade losal hisal
1 初级程序员 2000 4000
2 中级程序员 4000 6000
create table salgrade(
id number primary key,
gradeName varchar2(200) not null,
losal number,
hisal number
);
例如:
查询出员工的名字、职位、工资、工资等级名称
SELECT e.last_name, e.title, e.salary, s.gradeName
FROM s_emp e, salgrade s
WHERE e.salary BETWEEN s.losal AND s.hisal
3.外连接
外连接分为: 左外连接 右外连接 全连接
先分别在俩s_emp和s_dept表中插入新的数据
特点:新员工tom不在任何部门,新增部门st下面没有任何员工
insert into s_emp(id,last_name) values(26,'tom');
insert into s_dept(id,name) values(60,'st');
commit;
下面条sql语句可以把上面插入的数据给删除掉
delete from s_emp where id=26;
delete from s_dept where id=60;
commit;
这个时候再使用等值连接的话,查询出来的数据就会少,因为新增的员工tom和部门表中的数据连接不上,
当然新增的部门st也和员工表中的数据连接不上.那么这俩条数据都是在等值连接中查询不出来.
左外连接
例如:
查询所有员工 以及对应的部门的名字,没有部门的员工也要显示出来
select last_name,dept_id,name
from s_emp,s_dept
where s_emp.dept_id=s_dept.id(+);
或者 俩者是等价的
select last_name,dept_id,name
from s_emp left outer join s_dept
on s_emp.dept_id=s_dept.id;
注意:outer可以省去不写
右外连接
例如:
查询所有员工 以及对应的部门的名字,没有任何员工的部门也要显示出来
select last_name,dept_id,name
from s_emp,s_dept
where s_emp.dept_id(+)=s_dept.id;
select last_name,dept_id,name
from s_emp right outer join s_dept
on s_emp.dept_id=s_dept.id;
注意:outer可以省去不写
注:左右连接的操作要找好基准表。
全连接
例如:
查询所有员工 以及对应的部门的名字,没有任何员工的部门也要显示出来,没有部门的员工也要显示出来
select last_name,dept_id,name
from s_emp full outer join s_dept
on s_emp.dept_id=s_dept.id;
注意:outer可以省去不写
自连接
一张表,自己和自己连接
例如:
查询每个员工的名字以及员工对应的管理者的名字
select s1.last_name,s2.last_name manager_name
from s_emp s1,s_emp s2
where s1.manager_id = s2.id;
4.对查询结果集的操作
如果有俩条sql语句,每一条sql都可以查询出一个结果,这个被称之为结果集。那么我们可以使用下面的关键字对俩个结果集进行操作
union 取俩个结果集的并集
union all 把俩个结果集合在一起显示出来
minus 第一个结果集除去第二个结果集和它相同的部分
intersect 求俩个结果集的交集
注意:前提条件 俩个结果集中查询的列要完全一致
union 取俩个结果集的并集
例如:
select last_name,dept_id,name from s_emp,s_dept where s_emp.dept_id=s_dept.id(+)
union
select last_name,dept_id,name from s_emp,s_dept where s_emp.dept_id(+)=s_dept.id;
union all 把俩个结果集合在一起显示出来
例如:
select last_name,dept_id,name from s_emp,s_dept where s_emp.dept_id=s_dept.id(+)
union all
select last_name,dept_id,name from s_emp,s_dept where s_emp.dept_id(+)=s_dept.id;
minus 第一个结果集除去第二个结果集和它相同的部分
例如:
select last_name,dept_id,name from s_emp,s_dept where s_emp.dept_id=s_dept.id(+)
minus
select last_name,dept_id,name from s_emp,s_dept where s_emp.dept_id(+)=s_dept.id;
intersect 求俩个结果集的交集
select last_name,dept_id,name from s_emp,s_dept where s_emp.dept_id=s_dept.id(+)
intersect
select last_name,dept_id,name from s_emp,s_dept where s_emp.dept_id(+)=s_dept.id;
组函数
group by 在查询表中数据的时候进行分组的关键字
思考:为什么要对数据进行分组
having 分组之后的进行进一步数据筛选的关键字
having和where的功能类似
组函数(分组函数),是指将数据按照某列的值进行分组后,然后使用组函数分别对每个分好的小组中的数据进行处理。所以组函数一般要结合着分组关键字group来使用
组函数:
avg 求平均值
count 计算有多少条数据
max 最大值
min 最小值
sum 求和
stddev 标准差
variance 方差
sql语句的各部分构成
select ....
from ....
where ....
group by ...
having ...
order by ....
注:除了select和from之外其他的都不是必须的。
假如select..from..后面的语句都出现了,那么他们的执行顺序为:
where-->group by分组-->执行组函数-->having筛选->order by
组函数出现的位置:
1.select后面
2.having后面
3.order by后面
4.where后面一定【不能】出现组函数
!!!注意:如果select/having语句后面出现了组函数,那么select/having后面没有被组函数修饰的列,就必须出现在group by 后面
where和having对比:
1.where和having都是做条件筛选的
2.where执行的时间比having要早
3.where后面不能出现组函数
4.having后面可以出现组函数
5.where语句要紧跟from后面
6.having语句要紧跟group by后面
group by和having的关系:
1.group by可以单独存在,后面可以不出现having语句
2.having不能单独存在,有需要的话,必须出现在group by后面
order by语句
1.如果sql语句中需要排序,那么就一定要写在sql语句的最后面
2.order by后也可以出现组函数
使用组函数:不结合group分组使用
如果不使用group分组的话,那么默认当前查询到的所有数据是一组
使用组函数:结合group分组以及having筛选使用
例如:
查询s_emp表中部门的平均工资大于等于1400的部门
select dept_id,avg(salary)
from s_emp
group by dept_id
having avg(salary)>=1400;
思考:上面的sql语句修改为如下,是否可以?where后不可以使用组函数 group function is not allowed here
select max(s1.salary),s1.dept_id,s_dept.name,s2.last_name,s_region.name as regionname
from s_emp s1,s_emp s2,s_dept,s_region
where
s1.dept_id=s_dept.id
and
s_dept.region_id=s_region.id
group by s1.dept_id,s2.salary,s2.last_name,s_dept.name,s_region.name
having s2.salary=max(s1.salary)
order by s1.dept_id;
select dept_id
from s_emp
where last_name='Ngao';
select avg(salary)
from s_emp
where dept_id=(
select dept_id
from s_emp
where last_name='Ngao'
);
select last_name,salary,dept_id
from s_emp
where salary>(
select avg(salary)
from s_emp
where dept_id=(
select dept_id
from s_emp
where last_name='Ngao'
)
);
select t2.last_name,t2.salary,s_emp.dept_id,avg(s_emp.salary)
from s_emp,(
select last_name,salary,dept_id
from s_emp
where salary>(
select avg(salary)
from s_emp
where dept_id=(
select dept_id
from s_emp
where last_name='Ngao'
)
)
) t2
where s_emp.dept_id=t2.dept_id
group by s_emp.dept_id,t2.last_name,t2.salary
having s_emp.dept_id in(
select distinct dept_id
from s_emp
where salary>(
select avg(salary)
from s_emp
where dept_id=(
select dept_id
from s_emp
where last_name='Ngao'
)
)
)
and
avg(s_emp.salary)>(
select avg(salary)
from s_emp
where dept_id=(
select dept_id
from s_emp
where last_name='Ngao'
)
);