postgresql-子查询
- 子查询简介
- 派生表
- IN 操作符
- ALL 操作符
- ANY 操作符
- 关联子查询
- 横向子查询
- EXISTS 操作符
子查询简介
子查询(Subquery)是指嵌套在其他 SELECT、INSERT、UPDATE 以及 DELETE 语句中的
查询语句
子查询的作用与多表连接查询有点类似,也是为了从多个关联的表中返回或者过滤数据。例
如,我们想要知道哪些员工的月薪大于平均月薪,可以通过子查询实现
select e.first_name, e.last_name, e.salaryfrom employees e
where salary > (select avg(salary) from employees);
子查询必须位于括号中,也称为内查询,包含子查询的查询语句被称为外查询。除了 WHERE
子句之外,其他子句中也可以使用子查询,例如 SELECT 列表、FROM 子句等
派生表
FROM 子句中的子查询被称为派生表(Derived table)
SELECT column1, column2, ...FROM (subquery) AS table_alias;
其中子查询相当于创建了一个临时表 table_alias
-- 获取每个部门的总月薪
select d.department_name,ds.sum_salaryfrom departments djoin (select department_id,sum(salary) as sum_salaryfrom employeesgroup by department_id) dson (d.department_id = ds.department_id);
子查询返回了部门编号和部门月薪合计;然后再和 departments 表进行连接查询
IN 操作符
如果 WHERE 子查询返回多个记录,可以使用 IN 操作符进行条件过滤:
-- in
-- 存在 2008 年 01 月 01 日以后入职员工的部门
select *
from cps.public.departments d
where d.department_id in
(
select distinct e.department_id
from cps.public.employees e
where e.hire_date >= date '2008-01-01'
);
ALL 操作符
ALL 操作符与比较运算符一起使用,可以将一个值与子查询返回的列表进行比较:
-- all
-- 返回了月薪比销售部门(department_id = 80)所有员工都高的员工
select e.first_name ,
e.salary
from cps.public.employees e
where e.salary > all
(select e2.salary from cps.public.employees e2 where e2.department_id= 80);
上述sql等价于下面的sql
-- all
-- 返回了月薪比销售部门(department_id = 80)所有员工都高的员工
select e.first_name ,
e.salary
from cps.public.employees e
where e.salary >
(select max(e2.salary) from cps.public.employees e2 where e2.department_id= 80);
ANY 操作符
ANY 操作符和 ALL 操作符使用方法类似,只是效果不同:
-- any
-- 返回了月薪比销售部门(department_id = 80)任何一个员工都高的员工
select e.first_name ,
e.salary
from cps.public.employees e
where e.salary >
(select min(e2.salary) from cps.public.employees e2 where e2.department_id= 80);
上述sql等价于下面的sql
-- any
-- 返回了月薪比销售部门(department_id = 80)任何一个员工都高的员工
select e.first_name ,
e.salary
from cps.public.employees e
where e.salary > any
(select e2.salary from cps.public.employees e2 where e2.department_id= 80);
另外,SOME 和 ANY 是同义词。some的效果和any是一样的
关联子查询
有另一类子查询,它们会引用外部查询中的列,因而与外部查询产生关联,被称为关联子查询
/** 子查询中使用了外查询的字段(e.department_id)。对于外部查询中的每个员工,* 运行子查询返回他/她所在部门的平均月薪,然后传递给外部查询进行判断* */
-- 返回月薪大于所在部门平均月薪的员工
select
e.first_name,
e.salary
from cps.public.employees e
where e.salary >
(select avg(e2.salary) from cps.public.employees e2 where e2.department_id = e.department_id);
关联子查询对于外查询中的每一行都会运行一次(数据库可能会对此进行优化),而非
关联子查询在整个查询运行时只会执行一次
横向子查询
一般来说,子查询只能引用外查询中的字段,而不能使用同一层级中其他表中的字段。例如:
/**/
select
d.department_name ,t.sum_sal
from cps.public.departments d
join (
select sum(e.salary) sum_sal from cps.public.employees e where e.department_id = d.department_id
) t on true;
以上语句在 JOIN 中引用了左侧 departments 表中的字段,产生了语法错误。为此,我们需
要使用横向子查询(LATERAL subquery)。通过增加 LATERAL 关键字,子查询可以引用左侧
表中的列:
/** 每个部门的名称和总月薪* */
select
d.department_name ,t.sum_sal
from cps.public.departments d
cross join lateral (
select sum(e.salary) sum_sal from cps.public.employees e where e.department_id = d.department_id
) t;
EXISTS 操作符
EXISTS 操作符用于检查子查询结果的存在性。如果子查询返回任何结果,EXISTS 返回 True;
否则,返回 False
-- EXISTS
select *
from cps.public.departments d
where exists
(select 1 from cps.public.employees e
where e.department_id = d.department_id
and e.hire_date > date '2008-01-01'
);
-- not exists不存在
select *
from cps.public.departments d
where not exists
(select 1 from cps.public.employees e
where e.department_id = d.department_id
and e.hire_date > date '2008-01-01'
);
[NOT] IN 用于检查某个值是否属于(=)子查询的结果列表,[NOT] EXISTS 只检查子查询
结果的存在性。如果子查询的结果中存在 NULL,NOT EXISTS 结果为 True;但是,NOT IN 结
果为 False,因为 NOT (X = NULL) 的结果为 NULL
-- in 在什么取值范围内
select *
from cps.public.departments d
where d.department_id in
(select distinct e.department_id from cps.public.employees e)
-- not in 不属于子查询结果列
select *
from cps.public.departments d
where d.department_id not in
(select distinct e.department_id from cps.public.employees e)