サブクエリの概要
サブクエリとは、他の SELECT、INSERT、UPDATE、および DELETE ステートメントにネストされたクエリ ステートメントを指します
。サブクエリの機能は複数テーブルの結合クエリに似ており、複数の関連テーブルからデータを返したりフィルタリングしたりするためにも使用されます。たとえば
、どの従業員の月給が平均月給よりも高いかを知りたいとします。これはサブクエリを通じて達成できます。
select e.first_name, e.last_name, e.salary
from employees e
where salary > (select avg(salary) from employees);
サブクエリは括弧で囲む必要があり、内部クエリとも呼ばれ、サブクエリを含むクエリ ステートメントは外部クエリと呼ばれます。WHERE 句に加えて
、サブクエリは SELECT リスト、FROM 句などの他の句でも使用できます。
派生テーブル
FROM句内の副問合せを導出表(派生表)といいます。
SELECT column1, column2, ...
FROM (subquery) AS table_alias;
サブクエリは、一時テーブル table_alias を作成するのと同じです。
-- 获取每个部门的总月薪
select d.department_name,
ds.sum_salary
from departments d
join (select department_id,
sum(salary) as sum_salary
from employees
group by department_id) ds
on (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 演算子は 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 は同義語です。一部の効果は他の効果と同じです
相関サブクエリ
外部クエリ内の列を参照するため、外部クエリに関連する別のタイプのサブクエリがあり、これらは相関サブクエリと呼ばれます。
/*
* 子查询中使用了外查询的字段(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);
相関サブクエリは外側のクエリの行ごとに 1 回実行されます (データベースがこれを最適化する場合があります)。一方、非相関
サブクエリはクエリ実行全体で 1 回だけ実行されます。
水平サブクエリ
一般に、サブクエリは外側のクエリのフィールドのみを参照でき、同じ階層内の他のテーブルのフィールドを使用できません。例えば:
/**/
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 サブクエリを使用する必要があります。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 はサブクエリの結果
の存在のみを確認します。NOT EXISTS は、サブクエリの結果に NULL が存在する場合に True と評価されますが、
NOT (X = NULL) の結果が NULL であるため、NOT IN は False と評価されます。
-- 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)