编辑
2024-12-11
SQL编程思想:基于5种主流数据库代码实现
00
请注意,本文编写于 49 天前,最后修改于 49 天前,其中某些信息可能已经过时。

目录

标量子查询 Scalar Subquery
行子查询 Row Subquery
表子查询 Table Subquery
where 中的子查询
ALL、ANY 子查询
FROM 中的子查询
关联子查询 Correlated Subquery
横向子查询 Lateral Subquery
EXISTS 运算符
参考
sql
SELECT emp_name, salary -- Outer Query FROM employee WHERE salary > ( SELECT AVG(salary) -- Inner Query FROM employee );

标量子查询 Scalar Subquery

返回单个值(一行一列),可以像常量一样被用于SELECT、WHERE、GROUP BY、HAVING以及ORDER BY等子句中。

sql
-- 员工月薪与平均月薪之间的差值 SELECT emp_name AS "员工姓名", salary AS "月薪", salary - (SELECT AVG(salary) FROM employee) AS "差值" FROM employee;

行子查询 Row Subquery

返回单个记录(一行多列),不常用。

sql
-- 查找所有与“孙乾”在同一个部门并且职位相同的员工 -- oracle mysql pg sqlite SELECT emp_name, dept_id, job_id FROM employee WHERE (dept_id, job_id) = ( SELECT dept_id, job_id FROM employee WHERE emp_name = '孙乾' ) AND emp_name != '孙乾'
  • mss 不支持行子查询。

表子查询 Table Subquery

返回一个临时表(多行多列)。

where 中的子查询

  • 因为子查询是表,所以不能使用比较运算符(=!=<<=>>=等)。
  • 可以使用 IN、NOT IN。
sql
-- 某个部门的全体员工 SELECT emp_name FROM employee WHERE job_id IN ( SELECT job_id FROM employee WHERE dept_id = 1 );

ALL、ANY 子查询

ALL 运算符相当于多个 AND 运算符的组合,IN/ANY 运算符相当于多个 OR 运算符的组合。

  • ALL 运算符与比较运算符(=!=<<=>>=)的组合分别表示等于、不等于、小于、小于或等于、大于、大于或等于子查询结果中的全部数据。
sql
-- 查找比“研发部”全体员工更晚入职的员工信息 -- oracle mysql mss pg SELECT emp_name AS "员工姓名", hire_date AS "入职日期" FROM employee WHERE hire_date >ALL ( SELECT e.hire_date FROM employee e JOIN department d ON (d.dept_id = e.dept_id) WHERE d.dept_name = '研发部' );
  • sqlite 不支持 ALL。
sql
-- oracle SELECT emp_name AS "员工姓名", hire_date AS "入职日期" FROM employee WHERE salary >ALL (10000, 15000, 20000);
  • ANY 运算符与比较运算符(=!=<<=>>=)的组合分别表示等于、不等于、小于、小于或等于、大于、大于或等于子查询结果中的任何数据。
sql
-- 某个部门的全体员工 SELECT emp_name FROM employee WHERE job_id =ANY ( SELECT job_id FROM employee WHERE dept_id = 1 );
  • sqlite 不支持 any。
  • SOME 关键字可以替代 ANY。
sql
-- oracle SELECT emp_name FROM employee WHERE job_id !=ANY (1, 2, 2); -- 等价于 job_id != 1 OR job_id != 2

FROM 中的子查询

FROM子句中的子查询相当于一个临时表。

sql
-- 部门平均工资 SELECT d.dept_name AS "部门名称", ds.avg_salary AS "平均月薪" FROM department d LEFT JOIN ( SELECT dept_id, AVG(salary) AS avg_salary FROM employee GROUP BY dept_id) ds ON (d.dept_id = ds.dept_id);

子查询中使用排序通常没有意义。

sql
-- mysql pg sqlite SELECT emp_name, salary FROM employee WHERE job_id IN ( SELECT job_id FROM job WHERE max_salary >= 20000 ORDER BY job_id ) ORDER BY emp_name;
  • oracle mss 在子查询中使用排序会报错。

关联子查询 Correlated Subquery

子查询使用外部查询中的字段。

对于外部查询返回的每条记录,关联子查询都会执行一次(数据库可能会对此进行优化)​,非关联子查询只会独立执行一次。

sql
-- 每个部门的平均月薪 SELECT d.dept_name AS "部门名称", (SELECT AVG(salary) AS avg_aslary FROM employee WHERE dept_id = d.dept_id) AS "平均月薪" FROM department d ORDER BY d.dept_id; -- 每个部门中最早入职的员工 SELECT d.dept_name AS "部门名称", o.emp_name AS "员工姓名", o.hire_date AS "入职日期" FROM employee o JOIN department d ON (d.dept_id = o.dept_id) WHERE o.hire_date = (SELECT MIN(i.hire_date) FROM employee i WHERE i.dept_id = o.dept_id);

横向子查询 Lateral Subquery

关联子查询可以使用外部查询中的字段,但不能使用同一级别的其他查询或者表中的字段。以下是一个错误的示例:

sql
-- 每个部门的最大月薪 SELECT d.dept_name, t.max_salary FROM department d JOIN (SELECT MAX(e.salary) AS max_salary FROM employee e WHERE e.dept_id = d.dept_id) t;

横向子查询,允许派生表使用它所在的FROM子句中左侧的其他查询或者表:

sql
-- oracle mysql pg SELECT d.dept_name "部门名称", t.max_salary AS "最高月薪" FROM department d CROSS JOIN LATERAL (SELECT MAX(e.salary) AS max_salary FROM employee e WHERE e.dept_id = d.dept_id) t; -- mss oracle SELECT d.dept_name "部门名称", t.max_salary AS "最高月薪" FROM department d CROSS APPLY (SELECT MAX(e.salary) AS max_salary FROM employee e WHERE e.dept_id = d.dept_id) t;
  • 除 CROSS APPLY 外,还有一个 OUTER APPLY,它是左外连接的一种变体,同样允许右侧的子查询使用 FROM 子句中左侧查询或者表中的字段。Microsoft SQL Server和Oracle提供了这两种查询语法。
  • sqlite 不支持横向子查询。

EXISTS 运算符

用于判断子查询结果的存在性。只要子查询返回了任何结果,就表示满足查询条件;如果子查询没有返回任何结果,就表示不满足查询条件。

sql
-- 拥有女性员工的部门 SELECT d.dept_name AS "部门名称" FROM department d WHERE EXISTS (SELECT 1 FROM employee e WHERE e.sex = '女' AND e.dept_id = d.dept_id) ORDER BY dept_name;

​[NOT] EXISTS运算符只检查结果的存在性,​[NOT] IN运算符需要比较实际的值是否相等。

当子查询的结果中只有 NULL 值时,EXISTS 运算符仍然可以返回结果,NOT EXISTS 运算符则不返回结果。但是,此时 IN 和 NOT IN 运算符都不会返回结果,因为任何数据和空值进行比较的结果均未知。

sql
-- 不拥有女性员工的部门 SELECT d.dept_name FROM department d WHERE NOT EXISTS (SELECT NULL FROM employee e WHERE e.dept_id = d.dept_id AND sex = '女');

NOT IN 的等价写法

sql
SELECT d.dept_name AS "部门名称" FROM department d WHERE d.dept_id NOT INT (3, 4, NULL); -- 等价于 SELECT d.dept_name AS "部门名称" FROM department d WHERE d.dept_id != 3 AND d.dept_id != 4 AND d.dept_id != NULL;

参考

本文作者:jdxj

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!