PostgreSQL 子查询

子查询(Subquery)是指嵌套在其他SELECT、INSERT、UPDATE以及DELETE语句中的查询语句。子查询的作用与多表连接查询有点类似,也是为了从多个关联的表中返回或者过滤数据。例如,我们想要知道哪些员工的月薪大于平均月薪,可以通过子查询实现:

SELECT e.first_name, e.last_name, e.salary FROM employees e
WHERE salary > (SELECT avg(salary) FROM employees);

其中,WHERE子句中使用了一个子查询,用于计算平均月薪。PostgreSQL在执行以上语句时,先执行子查询返回平均月薪;然后将该值传递给外查询使用。子查询必须位于括号中,也称为内查询,包含子查询的查询语句被称为外查询。除了WHERE子句之外,其他子句中也可以使用子查询,例如SELECT列表、FROM子句等。

派生表

FROM子句中的子查询被称为派生表(Derived table),语法如下

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;
Purchasing 24900
Human Resources    6500
Shipping    156400
IT    28800
Public Relations    10000

其中,子查询返回了部门编号和部门月薪合计;然后再和departments表进行连接查询

IN操作符

如果WHERE子查询返回多个记录,可以使用IN操作符进行条件过滤:

SELECT d.department_id, d.department_name
FROM departments d
WHERE d.department_id in (SELECT department_id FROM employees WHERE hire_date >= date '2008-01-01');
50    Shipping
80    Sales

以上查询返回了存在2008 年01月01日以后入职员工的部门。如果想要返回包含该日期之前入职的员工的部门,可以使用NOT IN操作符。

ALL操作符

ALL操作符与比较运算符一起使用,可以将一个值与子查询返回的列表进行比较:

SELECT first_name, last_name, salary
FROM employees
WHERE salary > all (SELECT salary FROM employees WHERE department_id = 80);
Steven    King    24000.00
Neena    Kochhar    17000.00
Lex    De Haan    17000.00

此场景效果等价于

SELECT first_name, last_name, salary
FROM employees
WHERE salary >   (SELECT max(salary) FROM employees WHERE department_id = 80);

如果是< all 则子查询需要些min

ANY操作符

ANY操作符和ALL操作符使用方法类似,只是效果不同

SELECT first_name, last_name, salary
FROM employees
WHERE salary > any (SELECT salary FROM employees WHERE department_id = 80);

以上语句返回了月薪比销售部门(department_id = 80)任何员工高的员工。ANY也可以和其他比较运算符一起使用,例如= ANY实际上和IN的作用相同。另外,SOME和ANY是同义词。

横向子查询

一般来说,子查询只能引用外查询中的字段,而不能使用同一层级中其他表中的字段。例如:

SELECT d.department_name, t.avg_salary
FROM departments d
         JOIN
     (SELECT avg(e.salary) AS avg_salary FROM employees e WHERE e.department_id = d.department_id) t

以上语句在JOIN中引用了左侧departments表中的字段,产生了语法错误。为此,需要使用横向子查询(LATERAL subquery)。通过增加LATERAL关键字,子查询可以引用左侧表中的列:

SELECT d.department_name, t.sum_salary
FROM departments d
         CROSS JOIN LATERAL (SELECT sum(e.salary) AS sum_salary
                             FROM employees e
                             WHERE e.department_id = d.department_id) t
Administration    4400
Marketing    19000
Purchasing    24900
Human Resources    6500
Shipping    156400

效果等价于

select department_name, sum_salary
from departments d
         left join (select department_id, sum(salary) sum_salary from employees group by department_id) t
                   on d.department_id = t.department_id;

EXISTS 操作符

EXISTS操作符用于检查子查询结果的存在性。如果子查询返回任何结果,EXISTS返回True;否则,返回False。

SELECT d.department_id, d.department_name
FROM departments d
WHERE exists(SELECT 1 FROM employees WHERE department_id = d.department_id and hire_date >= date '2008-01-01');
50    Shipping
80    Sales

示例返回了存在2008年01月01日以后入职员工的部门,与上文中的IN操作符示例相同。NOT EXISTS操作符执行相反的操作,即子查询不返回任何结果,NOT EXISTS返回True;否则,返回False。[NOT] IN用于检查某个值是否属于(=)子查询的结果列表,[NOT] EXISTS只检查子查询结果的存在性。如果子查询的结果中存在NULL,NOT EXISTS结果为True;但是,NOT IN结果为False,因为NOT (X = NULL)的结果为NULL。例如:

SELECT d.department_id, d.department_name
FROM departments d
WHERE not exists(SELECT 1 FROM employees WHERE department_id = d.department_id);

以上语句查找没有任何员工的部门,结果返回了16条记录。如果使用NOT IN操作符

SELECT d.department_id, d.department_name
FROM departments d
WHERE department_id not in (SELECT department_id FROM employees WHERE department_id = d.department_id);
posted @ 2023-07-21 20:54  晓枫的春天  阅读(201)  评论(0编辑  收藏  举报