SQL 学习 (六): NOT EXISTS 用法

在 SQL 中,NOT EXISTS 是一个非常有用的子查询操作符,用来检查某个条件是否不成立,通常用于在查询中排除某些数据。它与 EXISTS 正好相反,EXISTS 用来检查子查询返回的结果集是否存在,而 NOT EXISTS 则用来检查子查询返回的结果集是否为空

1. NOT EXISTS 的基本语法

SELECT column_name(s)
FROM table_name
WHERE NOT EXISTS (subquery);
  • subquery:是一个返回结果集的子查询。如果子查询返回结果,NOT EXISTS 将返回 FALSE,因此外层查询不会返回任何行。如果子查询没有返回任何行,NOT EXISTS 返回 TRUE,外层查询将返回结果。

2. NOT EXISTS 与 EXISTS 的区别

  • EXISTS:如果子查询返回至少一行数据,则 EXISTS 返回 TRUE
  • NOT EXISTS:如果子查询返回任何数据,则 NOT EXISTS 返回 FALSE;如果子查询不返回任何数据,则 NOT EXISTS 返回 TRUE

简单来说,EXISTS 用于检查子查询的结果是否存在,而 NOT EXISTS 用于检查子查询的结果是否不存在。

3. NOT EXISTS 的使用场景

NOT EXISTS 常用于排除在其他表中存在的记录。例如,我们可以用它来查询某个表中没有在另一个表中出现的记录。

3.1. 示例 1:排除已存在的记录

假设有两个表:

  • employees 表,包含员工信息(idnamedepartment_id)。
  • sales 表,包含销售记录(idemployee_idamount)。

我们希望查询出没有做过任何销售的员工,即员工在 sales 表中没有对应记录。

SELECT e.id, e.name
FROM employees e
WHERE NOT EXISTS (
    SELECT 1
    FROM sales s
    WHERE s.employee_id = e.id
);

解释

  • 子查询 SELECT 1 FROM sales s WHERE s.employee_id = e.id 用来查找当前员工是否在 sales 表中有记录。
  • 如果子查询返回任何行,说明该员工做过销售,那么 NOT EXISTS 返回 FALSE,该员工不出现在结果中。
  • 如果子查询没有返回任何行,说明该员工没有做过销售,NOT EXISTS 返回 TRUE,该员工出现在结果中。

3.2. 示例 2:排除满足某条件的记录

假设我们有一个 students 表,包含学生的 id 和 name,另一个 enrollments 表,记录了学生的选课信息,包含 student_id 和 course_id。我们想要查询没有选修过某门课程(例如课程 101)的学生:

SELECT s.id, s.name
FROM students s
WHERE NOT EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE e.student_id = s.id AND e.course_id = 101
);

解释

  • 子查询检查学生是否选修过课程 101
  • 如果子查询没有返回任何行,说明该学生没有选修过该课程,NOT EXISTS 返回 TRUE,该学生出现在查询结果中。

3.3. 示例 3:与 IN 对比

使用 NOT EXISTS 与 IN 也有相似的作用,但 NOT EXISTS 的效率通常较高,特别是在处理大型数据集时。以下是等效的查询,使用 IN 语法来排除已做销售的员工:

SELECT e.id, e.name
FROM employees e
WHERE e.id NOT IN (
    SELECT s.employee_id
    FROM sales s
);

区别

  • 使用 IN 时,如果 sales 表中有 NULL 值,可能会导致意外结果,因为 NULL 会导致 IN 子查询失败。
  • 使用 NOT EXISTS 时,NULL 不会影响查询结果,因此在某些情况下,NOT EXISTS 比 IN 更加可靠。

4. NOT EXISTS 的性能考虑

  • 性能问题NOT EXISTS 通常在子查询返回的数据量较少时表现较好,因为它会在找到第一个匹配项时就停止查询(短路)。然而,如果子查询结果集较大,可能会导致性能问题。
  • 索引优化:确保子查询中的条件列(例如 sales.employee_id)已经建立索引,可以显著提高查询性能,特别是在子查询涉及到大量数据时。

5. 总结

  • NOT EXISTS 用来检查子查询是否没有返回任何数据,通常用于排除某些记录。
  • 它与 EXISTS 的功能相反,EXISTS 用来检查子查询是否返回了至少一行数据。
  • 使用 NOT EXISTS 时,通常可以避免 IN 可能带来的问题,尤其是在处理 NULL 值时。
  • 在某些情况下,NOT EXISTS 相较于 IN 可以提供更好的性能。

希望通过这些示例,你对 NOT EXISTS 的使用有了更深入的理解!