news 2026/8/25 1:19:22

SQL查询优化:IN、EXISTS、JOIN与聚合函数详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQL查询优化:IN、EXISTS、JOIN与聚合函数详解

摘要:本文详细介绍了SQL查询中常用的IN与EXISTS运算符的区别、各种JOIN连接查询的使用场景、嵌套子查询的编写方法以及聚合函数的基本使用和嵌套应用。通过对比分析和实例演示,帮助读者深入理解这些SQL核心概念,提升查询编写和优化能力。

前言:阅读指南

本文旨在为不同层次的SQL学习者提供全面的查询优化知识体系。无论您是SQL初学者,还是希望提升查询性能的开发人员,本文都将帮助您深入理解SQL核心概念及其应用场景。

目标读者

  • SQL初学者:希望系统学习IN/EXISTS、JOIN、子查询和聚合函数等基础概念
  • 中级开发者:需要优化现有查询,理解不同查询方式的性能差异
  • 数据库管理员:希望掌握查询优化技巧,提升数据库性能
  • 数据分析师:需要编写高效的数据提取和聚合查询

先验知识要求

为了更好地理解本文内容,建议读者具备以下基础知识:

  • 基本的SQL语法(SELECT、FROM、WHERE等)
  • 了解关系型数据库的基本概念(表、行、列)
  • 熟悉简单的数据查询操作
  • 了解主键、外键等基本数据库约束概念

章节概览与学习路径

本文按照从基础到应用的逻辑顺序组织内容,建议按以下顺序阅读:

  1. 一、IN 与 EXISTS 的区别:深入对比两种运算符的语义、性能差异和使用场景,帮助您根据数据规模选择合适的查询方式。
  2. 二、连接查询(JOIN):系统讲解INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN四种连接类型,通过Mermaid流程图和实战示例直观展示不同JOIN的结果集差异。
  3. 三、嵌套子查询:涵盖WHERE、FROM、UPDATE、DELETE等子句中的子查询应用,提供多个实战示例和性能优化建议。
  4. 四、聚合函数的使用:介绍COUNT、SUM、AVG、MAX、MIN等聚合函数的基本用法,以及WHERE与HAVING子句的区别。
  5. 五、聚合函数的嵌套使用:深入探讨聚合函数在复杂查询中的应用,包括分组过滤和性能优化。
  6. 六、总结与参考资料:汇总各章节核心要点,提供官方文档和权威技术资源链接,方便进一步学习。

学习建议

  • 动手实践:建议在本地数据库或在线SQL练习平台(如SQL Fiddle、DB Fiddle)中运行文中的示例代码。
  • 对比分析:对于IN vs EXISTS、不同JOIN类型等对比性内容,建议创建测试数据并亲自验证结果差异。
  • 性能测试:对于涉及性能优化的部分(如子查询优化),可以在自己的数据环境中进行性能测试。
  • 循序渐进:如果对某个概念感到困惑,可以先跳过,继续阅读后续内容,待整体理解后再回头深入。

通过本文的学习,您将能够:

  • 准确区分IN和EXISTS的使用场景,并做出性能最优的选择
  • 根据业务需求选择最合适的JOIN类型
  • 编写高效、可维护的嵌套子查询
  • 正确使用聚合函数进行数据统计和分析
  • 识别和优化常见的SQL性能瓶颈

现在,让我们开始深入学习SQL查询优化的核心知识。

一、IN 与 EXISTS 的区别

IN和EXISTS是SQL查询中常用的两种运算符,它们在语义和使用方式上有明显区别。

1.1 IN运算符

IN是一个集合运算符,语法形式为:a IN {a,c,d,s,d....}。在这个运算中,前面是一个元素,后面是一个集合,集合中的元素类型必须和前面的元素类型一致。

IN运算用在语句中时,它后面带的SELECT语句一定是选一个字段,而不是SELECT *。例如,要判断某班是否存在一个名为"小明"的学生,可以使用IN运算:

"小明" IN (SELECT sname FROM student)

这样(SELECT sname FROM student)返回的是一个全班姓名的集合,IN用于判断"小明"是否为此集合中的一个数据。

1.2 EXISTS运算符

EXISTS是一个存在判断,如果后面的查询中有结果,则EXISTS为真,否则为假。例如:

EXISTS (SELECT * FROM student WHERE sname="小明")

1.3 性能对比与优化

这两个函数在功能上相似,但由于优化方案的不同,通常NOT EXISTS要比NOT IN快,因为NOT EXISTS可以使用结合算法而NOT IN就不行了。而EXISTS则不如IN快,因为这时候IN可能更多的使用结合算法。

示例对比:

SELECT * FROM 表A WHERE EXISTS(SELECT * FROM 表B WHERE 表B.id=表A.id) -- 这句相当于 SELECT * FROM 表A WHERE id IN (SELECT id FROM 表B)

对于表A的每一条数据,都执行SELECT * FROM 表B WHERE 表B.id=表A.id的存在性判断,如果表B中存在表A当前行相同的id,则EXISTS为真,该行显示,否则不显示。

使用建议:EXISTS适合内大外小的查询,IN适合内小外大的查询。

1.4 官方定义对比

IN:确定给定的值是否与子查询或列表中的值相匹配。

EXISTS:指定一个子查询,检测行的存在。

1.5 实例对比

比较使用EXISTS和IN的查询。以下两个查询语义类似,返回相同的信息:

-- 使用EXISTS USE pubs GO SELECT DISTINCT pub_name FROM publishers WHERE EXISTS (SELECT * FROM titles WHERE pub_id = publishers.pub_id AND type = 'business') GO
-- 使用IN USE pubs GO SELECT DISTINCT pub_name FROM publishers WHERE pub_id IN (SELECT pub_id FROM titles WHERE type = 'business') GO

下面是任一查询的结果集:

pub_name ---------------------------------------- Algodata Infosystems New Moon Books (2 row(s) affected)

1.6 逻辑含义总结

EXISTS相当于存在量词:表示集合存在,也就是集合不为空只作用一个集合。例如EXIST P表示P不空时为真;NOT EXIST P表示P为空时为真。

IN表示一个标量和一元关系的关系。例如:s IN P表示当s与P中的某个值相等时为真;s NOT IN P表示s与P中的每一个值都不相等时为真。

二、连接查询(JOIN)

连接查询是SQL中用于从多个表中检索数据的重要技术。不同的SQL JOIN返回的结果集不一样。

SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo FROM Persons INNER JOIN Orders ON Persons.Id_P = Orders.Id_P ORDER BY Persons.LastName

除了INNER JOIN(内连接),还可以使用其他几种连接类型:

  • JOIN(INNER JOIN):如果表中有至少一个匹配,则返回行
  • LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行
  • RIGHT JOIN:即使左表中没有匹配,也从右表返回所有的行
  • FULL JOIN:只要其中一个表中存在匹配,就返回行
  • 注释:INNER JOIN与JOIN是相同的

为了更直观地理解这四种JOIN类型如何连接两个表并选取结果集,下面通过Mermaid流程图展示Persons表和Orders表的连接关系:

flowchart TD subgraph Persons[Persons表] P1[Person A] P2[Person B] P3[Person C] end subgraph Orders[Orders表] O1[Order 1 Person A] O2[Order 2 Person A] O3[Order 3 Person B] end %% INNER JOIN - 只返回匹配的行 subgraph InnerJoin[INNER JOIN结果集] IJ1[Person A - Order 1] IJ2[Person A - Order 2] IJ3[Person B - Order 3] end %% LEFT JOIN - 返回左表所有行,右表匹配的显示,不匹配的显示NULL subgraph LeftJoin[LEFT JOIN结果集] LJ1[Person A - Order 1] LJ2[Person A - Order 2] LJ3[Person B - Order 3] LJ4[Person C - NULL] end %% RIGHT JOIN - 返回右表所有行,左表匹配的显示,不匹配的显示NULL subgraph RightJoin[RIGHT JOIN结果集] RJ1[Person A - Order 1] RJ2[Person A - Order 2] RJ3[Person B - Order 3] RJ4[NULL - Order 4] end %% FULL JOIN - 返回所有行,匹配的显示,不匹配的显示NULL subgraph FullJoin[FULL JOIN结果集] FJ1[Person A - Order 1] FJ2[Person A - Order 2] FJ3[Person B - Order 3] FJ4[Person C - NULL] FJ5[NULL - Order 4] end %% 连接关系展示 P1 -->|匹配| O1 P1 -->|匹配| O2 P2 -->|匹配| O3 P3 -->|无匹配| LJ4 O4[Order 4 无对应Person] -->|无匹配| RJ4 %% 结果集说明 Persons -->|INNER JOIN| InnerJoin Persons -->|LEFT JOIN| LeftJoin Orders -->|RIGHT JOIN| RightJoin Persons & Orders -->|FULL JOIN| FullJoin

图例说明:

  • Persons表:包含Person A、Person B、Person C三条记录
  • Orders表:包含Order 1(属于Person A)、Order 2(属于Person A)、Order 3(属于Person B)、Order 4(无对应Person)四条记录
  • INNER JOIN:只返回两个表都匹配的记录(Person A-Order 1、Person A-Order 2、Person B-Order 3)
  • LEFT JOIN:返回左表(Persons)所有记录,右表匹配的显示订单信息,不匹配的显示NULL(Person C显示为NULL)
  • RIGHT JOIN:返回右表(Orders)所有记录,左表匹配的显示人员信息,不匹配的显示NULL(Order 4显示为NULL)
  • FULL JOIN:返回两个表的所有记录,匹配的显示对应信息,不匹配的显示NULL(Person C和Order 4都显示为NULL)

简而言之,就是你希望看到的结果集是怎样的。如两个表person和order连接:

  • 如果你只想看到person中和order有联系的记录(即满足order.id_p = person.id的记录),其他不匹配的全不显示,就用INNER JOIN
  • LEFT JOIN就是在返回INNER JOIN的结果集的基础上,返回左表person表中其余的记录(这些显示的记录行所有与order表相关的栏为空,因为它们不满足匹配关系)
  • 同理,RIGHT JOIN和FULL JOIN也有类似的逻辑

2.1 实战示例:员工与部门表 JOIN 查询

为了更好地理解不同 JOIN 类型的实际效果,我们使用一个具体的员工表(employees)和部门表(departments)进行演示。

表结构与示例数据

部门表 departments:

CREATE TABLE departments ( dept_id INT PRIMARY KEY, dept_name VARCHAR(50) ); INSERT INTO departments (dept_id, dept_name) VALUES (1, '技术部'), (2, '销售部'), (3, '人事部'), (4, '财务部');

员工表 employees:

CREATE TABLE employees ( emp_id INT PRIMARY KEY, emp_name VARCHAR(50), dept_id INT, salary DECIMAL(10, 2) ); INSERT INTO employees (emp_id, emp_name, dept_id, salary) VALUES (101, '张三', 1, 8000.00), (102, '李四', 1, 7500.00), (103, '王五', 2, 6000.00), (104, '赵六', NULL, 5500.00), (105, '钱七', 5, 7000.00); -- 注意:部门ID 5在departments表中不存在

数据说明:

  • 技术部(dept_id=1)有2名员工:张三、李四
  • 销售部(dept_id=2)有1名员工:王五
  • 人事部(dept_id=3)和财务部(dept_id=4)没有员工
  • 赵六(emp_id=104)没有分配部门(dept_id为NULL)
  • 钱七(emp_id=105)分配到了不存在的部门(dept_id=5)
1. INNER JOIN(内连接)

SQL 语句:

SELECT e.emp_id, e.emp_name, e.salary, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id ORDER BY e.emp_id;

查询结果集:

emp_id | emp_name | salary | dept_name -------+----------+---------+----------- 101 | 张三 | 8000.00 | 技术部 102 | 李四 | 7500.00 | 技术部 103 | 王五 | 6000.00 | 销售部 (3 rows)

解释:INNER JOIN 只返回两个表中都有匹配的记录。赵六(dept_id为NULL)和钱七(dept_id=5在departments中不存在)因为没有匹配的部门记录,所以不显示。人事部和财务部因为没有员工,也不显示。

2. LEFT JOIN(左连接)

SQL 语句:

SELECT e.emp_id, e.emp_name, e.salary, d.dept_name FROM employees e LEFT JOIN departments d ON e.dept_id = d.dept_id ORDER BY e.emp_id;

查询结果集:

emp_id | emp_name | salary | dept_name -------+----------+---------+----------- 101 | 张三 | 8000.00 | 技术部 102 | 李四 | 7500.00 | 技术部 103 | 王五 | 6000.00 | 销售部 104 | 赵六 | 5500.00 | NULL 105 | 钱七 | 7000.00 | NULL (5 rows)

解释:LEFT JOIN 返回左表(employees)的所有记录,右表(departments)匹配的显示部门名称,不匹配的显示NULL。赵六和钱七虽然部门不匹配,但仍然显示在结果中,对应的dept_name为NULL。

3. RIGHT JOIN(右连接)

SQL 语句:

SELECT e.emp_id, e.emp_name, e.salary, d.dept_name FROM employees e RIGHT JOIN departments d ON e.dept_id = d.dept_id ORDER BY d.dept_id, e.emp_id;

查询结果集:

emp_id | emp_name | salary | dept_name -------+----------+---------+----------- 101 | 张三 | 8000.00 | 技术部 102 | 李四 | 7500.00 | 技术部 103 | 王五 | 6000.00 | 销售部 NULL | NULL | NULL | 人事部 NULL | NULL | NULL | 财务部 (5 rows)

解释:RIGHT JOIN 返回右表(departments)的所有记录,左表(employees)匹配的显示员工信息,不匹配的显示NULL。人事部和财务部虽然没有员工,但仍然显示在结果中,对应的员工信息为NULL。

4. FULL JOIN(全连接)

SQL 语句:

SELECT e.emp_id, e.emp_name, e.salary, d.dept_name FROM employees e FULL JOIN departments d ON e.dept_id = d.dept_id ORDER BY CASE WHEN e.emp_id IS NULL THEN 1 ELSE 0 END, e.emp_id, d.dept_id;

查询结果集:

emp_id | emp_name | salary | dept_name -------+----------+---------+----------- 101 | 张三 | 8000.00 | 技术部 102 | 李四 | 7500.00 | 技术部 103 | 王五 | 6000.00 | 销售部 104 | 赵六 | 5500.00 | NULL 105 | 钱七 | 7000.00 | NULL NULL | NULL | NULL | 人事部 NULL | NULL | NULL | 财务部 (7 rows)

解释:FULL JOIN 返回两个表的所有记录,匹配的记录显示对应信息,不匹配的部分显示NULL。结果集包含了:

  1. INNER JOIN 的结果(3条匹配记录)
  2. LEFT JOIN 独有的结果(赵六、钱七,共2条)
  3. RIGHT JOIN 独有的结果(人事部、财务部,共2条)
四种 JOIN 类型对比总结
JOIN 类型返回结果适用场景示例结果行数
INNER JOIN只返回两个表都匹配的记录需要获取有关联的数据,如"有订单的客户"3行
LEFT JOIN返回左表所有记录 + 右表匹配记录(不匹配为NULL)需要获取左表所有数据,无论右表是否有匹配,如"所有员工及其部门"5行
RIGHT JOIN返回右表所有记录 + 左表匹配记录(不匹配为NULL)需要获取右表所有数据,无论左表是否有匹配,如"所有部门及其员工"5行
FULL JOIN返回两个表所有记录(匹配的显示对应信息,不匹配为NULL)需要获取两个表的完整数据视图,如"员工与部门完整对应关系"7行

关键差异:

  • INNER JOIN:只关心"有对应关系"的数据
  • LEFT JOIN:以左表为主,右表为补充
  • RIGHT JOIN:以右表为主,左表为补充
  • FULL JOIN:两个表的数据都要,无论是否有对应关系

在实际应用中,LEFT JOIN 使用频率最高,因为通常我们有一个主表(如员工表),需要关联其他表(如部门表)获取补充信息,同时不希望丢失主表的任何记录。

三、嵌套子查询

嵌套子查询是指在一个查询(父查询)中嵌套另一个查询(子查询)。子查询可以出现在 SELECT、FROM、WHERE、HAVING 等子句中,用于提供中间结果集或过滤条件。根据子查询与父查询的关联性,可分为相关子查询(引用了父查询中的列)和非相关子查询(独立执行)。父查询可以是 SELECT、UPDATE、DELETE 语句。

WHERE 子句中常用的运算符包括:=, <, >, >=, <=, <> 以及 IN, NOT IN, EXISTS, NOT EXISTS, ALL, ANY 等。

3.1 实战示例一:使用子查询进行数据过滤

场景:查询工资高于本部门平均工资的员工信息。

SQL 语句:

-- 使用非相关子查询(在 FROM 子句中) SELECT e.emp_id, e.emp_name, e.salary, e.dept_id, d.dept_name, dept_avg.avg_salary FROM employees e JOIN departments d ON e.dept_id = d.dept_id JOIN ( -- 子查询:计算每个部门的平均工资 SELECT dept_id, AVG(salary) AS avg_salary FROM employees WHERE dept_id IS NOT NULL GROUP BY dept_id ) dept_avg ON e.dept_id = dept_avg.dept_id WHERE e.salary > dept_avg.avg_salary ORDER BY e.dept_id, e.salary DESC;

执行逻辑:

  1. 子查询(SELECT dept_id, AVG(salary) ... GROUP BY dept_id)首先独立执行,生成一个临时结果集dept_avg,包含每个部门的平均工资。
  2. 主查询将employees表与departments表连接,再与子查询结果dept_avg连接。
  3. WHERE e.salary > dept_avg.avg_salary过滤出工资高于本部门平均工资的员工。

注意事项:

  • 性能:当子查询结果集较大时,连接操作可能较慢。可以考虑使用窗口函数(如AVG() OVER (PARTITION BY dept_id))进行优化。
  • NULL 值处理:子查询中通过WHERE dept_id IS NOT NULL排除了未分配部门的员工,避免因 NULL 值导致分组错误。
  • 可读性:将子查询放在 FROM 子句中并为它起别名(dept_avg),可以提高 SQL 的可读性。

3.2 实战示例二:在 UPDATE 语句中使用子查询

场景:为销售部(dept_id=2)所有员工的工资增加 10%,但上限不超过公司最高工资的 80%。

SQL 语句:

-- 使用子查询确定工资上限 UPDATE employees SET salary = LEAST( salary * 1.1, -- 增加10% ( -- 子查询:获取公司最高工资的80% SELECT MAX(salary) * 0.8 FROM employees WHERE dept_id IS NOT NULL ) ) WHERE dept_id = 2; -- 仅针对销售部

执行逻辑:

  1. 子查询(SELECT MAX(salary) * 0.8 FROM employees ...)首先执行一次,计算出公司最高工资的 80% 作为上限值。
  2. 主 UPDATE 语句对销售部(dept_id = 2)的每一行,计算salary * 1.1(涨薪后)与子查询返回的上限值,取两者中较小者(LEAST函数)。
  3. 最终更新符合条件的员工工资。

注意事项:

  • 单值要求:在 SET 子句中使用子查询时,子查询必须返回单个标量值(一行一列)。本例中,MAX(salary) * 0.8返回一个数值。
  • 相关子查询风险:如果子查询引用了正在更新的表(且未妥善处理),可能导致不可预测的结果或性能问题。本例中子查询独立于更新行,是安全的。
  • 事务与锁:UPDATE 语句通常会在事务中执行,并可能锁定相关行。对于大数据量,建议分批更新或评估性能影响。

3.3 实战示例三:在 DELETE 语句中使用子查询

场景:删除那些部门已被撤销(在 departments 表中不存在)的员工记录。

SQL 语句:

-- 使用子查询识别无效部门ID DELETE FROM employees WHERE dept_id IS NOT NULL AND dept_id NOT IN ( -- 子查询:获取所有有效的部门ID SELECT dept_id FROM departments );

执行逻辑:

  1. 子查询(SELECT dept_id FROM departments)返回所有有效的部门 ID 列表。
  2. 主 DELETE 语句删除employees表中那些dept_id不为 NULL 且不在有效部门 ID 列表中的记录。

注意事项:

  • NOT IN 与 NULL:如果子查询可能返回 NULL 值,NOT IN的行为会变得复杂(任何与 NULL 的比较结果都是 UNKNOWN)。确保子查询结果集不包含 NULL,或使用NOT EXISTS替代。
  • 性能:对于大型表,NOT IN子查询可能效率较低。如果 departments 表很大,可考虑在 dept_id 上建立索引,或使用NOT EXISTS配合相关子查询。
  • 数据安全:执行 DELETE 前,建议先用 SELECT 语句验证将要删除的记录,或使用事务以便回滚。

3.4 嵌套子查询的通用注意事项

  • 执行顺序:通常,子查询先于父查询执行(非相关子查询),但相关子查询需要为父查询的每一行执行一次,可能导致性能问题。
  • 替代方案:许多嵌套子查询可以用 JOIN 或窗口函数重写,后者往往性能更优、可读性更好。
  • 可读性与维护:过度嵌套会使 SQL 难以理解和调试。建议将复杂逻辑拆分为多个 CTE(Common Table Expressions,使用 WITH 子句)或临时视图。
  • 测试:在应用到生产环境前,务必在测试环境中验证子查询的结果和性能。

通过以上示例可以看出,嵌套子查询是 SQL 中强大的工具,合理使用可以解决复杂的数据操作需求,但需注意其执行逻辑和潜在的性能影响。

四、聚合函数的使用

聚集函数和大多数其它关系数据库产品一样,PostgreSQL支持聚集函数。一个聚集函数从多个输入行中计算出一个结果。比如,我们有在一个行集合上计算COUNT(数目)、SUM(总和)、AVG(均值)、MAX(最大值)、MIN(最小值)的函数。

例如,我们可以用下面的语句找出所有低温中的最高温度:

SELECT MAX(temp_lo) FROM weather;

结果:

max ----- 46 (1 row)

如果我们想知道该读数发生在哪个城市,可能会用:

SELECT city FROM weather WHERE temp_lo = MAX(temp_lo); -- 错!

不过这个方法不能运转,因为聚集函数MAX不能用于WHERE子句中。存在这个限制是因为WHERE子句决定哪些行可以进入聚集阶段;因此它必需在聚集函数之前计算。不过,我们可以用其它方法实现这个目的;这里我们使用子查询:

SELECT city FROM weather WHERE temp_lo = (SELECT MAX(temp_lo) FROM weather);

结果:

city --------------- San Francisco (1 row)

这样做是可以的,因为子查询是一次独立的计算,它独立于外层查询计算自己的聚集。

聚集同样也常用于GROUP BY子句。比如,我们可以获取每个城市低温的最高值:

SELECT city, MAX(temp_lo) FROM weather GROUP BY city;

结果:

city | max ---------------+----- Hayward | 37 San Francisco | 46 (2 rows)

这样每个城市一个输出。每个聚集结果都是在匹配该城市的行上面计算的。我们可以用HAVING过滤这些分组:GROUP BY可以没有HAVING过滤,但HAVING必须跟在GROUP BY的后面。

SELECT city, MAX(temp_lo) FROM weather GROUP BY city HAVING MAX(temp_lo) < 40;

结果:

city | max ---------+----- Hayward | 37 (1 row)

这样就只给出那些temp_lo值曾经有低于40度的城市。最后,如果我们只关心那些名字以"S"开头的城市,我们可以用:

SELECT city, MAX(temp_lo) FROM weather WHERE city LIKE 'S%' GROUP BY city HAVING MAX(temp_lo) < 40;

五、聚合函数的嵌套使用

语句中的LIKE执行模式匹配。理解聚集和SQL的WHERE和HAVING子句之间的关系非常重要。WHERE和HAVING的基本区别如下:

  • WHERE:在分组和聚集计算之前选取输入行(它控制哪些行进入聚集计算)
  • HAVING:在分组和聚集之后选取输出行

因此,WHERE子句不能包含聚集函数;因为试图用聚集函数判断那些行将要输入给聚集运算是没有意义的。相反,HAVING子句总是包含聚集函数。当然,你可以写不使用聚集的HAVING子句,但这样做没什么好处,因为同样的条件可以更有效地用于WHERE阶段。

在前面的例子里,我们可以在WHERE里应用城市名称限制,因为它不需要聚集。这样比在HAVING里增加限制更加高效,因为我们避免了为那些未通过WHERE检查的行进行分组和聚集计算。

六、总结与参考资料

6.1 核心要点总结

本文详细介绍了SQL查询优化中的四个核心概念:IN与EXISTS运算符、连接查询(JOIN)、嵌套子查询以及聚合函数的使用。以下是各主题的核心要点与选用原则:

1. IN 与 EXISTS 运算符
  • IN运算符:用于判断某个值是否在集合中,适合内小外大的查询场景。
  • EXISTS运算符:用于检测子查询是否存在结果,适合内大外小的查询场景。
  • 性能对比:NOT EXISTS通常比NOT IN快,因为可以使用结合算法;而EXISTS在多数情况下不如IN快。
  • 选用原则:当子查询结果集较小时使用IN,结果集较大时使用EXISTS;处理NULL值时需特别注意NOT IN的行为。
2. 连接查询(JOIN)
  • INNER JOIN:只返回两个表都匹配的记录,适用于需要获取有关联数据的场景。
  • LEFT JOIN:返回左表所有记录+右表匹配记录(不匹配为NULL),适用于以左表为主表的查询。
  • RIGHT JOIN:返回右表所有记录+左表匹配记录(不匹配为NULL),适用于以右表为主表的查询。
  • FULL JOIN:返回两个表所有记录(匹配的显示对应信息,不匹配为NULL),适用于需要完整数据视图的场景。
  • 选用原则:LEFT JOIN使用频率最高,通常用于主表关联补充表且不希望丢失主表记录的场景。
3. 嵌套子查询
  • 数据过滤:在WHERE、FROM子句中使用子查询进行数据筛选和过滤。
  • UPDATE/DELETE操作:在数据修改语句中使用子查询确定操作范围。
  • 执行逻辑:非相关子查询先于父查询执行,相关子查询为父查询的每一行执行一次。
  • 选用原则:优先考虑使用JOIN或窗口函数替代复杂嵌套子查询以提高性能;对于简单过滤可使用子查询。
4. 聚合函数
  • 基本函数:COUNT、SUM、AVG、MAX、MIN等用于数据统计和分析。
  • WHERE与HAVING区别:WHERE在分组前过滤行,不能使用聚合函数;HAVING在分组后过滤组,必须使用聚合函数。
  • 嵌套使用:聚合函数可以嵌套在子查询中,但不能直接在WHERE子句中使用。
  • 选用原则:在WHERE子句中使用子查询替代聚合函数过滤;在HAVING子句中使用聚合函数对分组结果进行筛选。

6.2 参考资料

  • MySQL官方文档 - 子查询:https://dev.mysql.com/doc/refman/8.0/en/subqueries.html - 详细介绍了MySQL中子查询的语法、类型和使用场景。
  • PostgreSQL官方文档 - 聚合函数:PostgreSQL: Documentation: 18: 9.21. Aggregate Functions - 包含了PostgreSQL中所有聚合函数的详细说明和示例。
  • SQL JOIN可视化指南:Qlik Talend Cloud | Trusted, AI-Ready Data Integration & Quality - 通过交互式图表直观展示各种JOIN类型的工作原理和结果集差异。
  • SQL性能优化最佳实践:SQL Indexing and Tuning e-Book for developers: Use The Index, Luke covers Oracle, MySQL, PostgreSQL, SQL Server, ... - 涵盖了SQL查询优化、索引使用、执行计划分析等高级主题。

通过掌握这些SQL核心概念和优化技巧,开发者可以编写出更高效、更易维护的数据库查询语句,提升应用程序的整体性能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/25 1:03:42

统计学硕士论文降AI教程:统计专业研究生论文AIGC超标免费4.8元知网维普达标完整操作指南

统计学硕士论文降AI教程&#xff1a;统计专业研究生论文AIGC超标免费4.8元知网维普达标完整操作指南 第一次用降AI工具有很多不确定——传什么格式、选哪个模式、怎么验收。 这篇教程把统计学硕士论文降AI的常见问题都覆盖了&#xff0c;主要基于嘎嘎降AI&#xff08;www.aig…

作者头像 李华
网站建设 2026/8/25 0:57:51

2026年高教社杯数学建模国赛必备项目(139):黑金脉动:国际原油价格波动对中国行业股市溢出效应的数学建模全解析——2026年国赛实战指南

国赛期间专栏内发布ABCDE题相关内容&#xff0c;开赛后恢复原价158. 一、从“黑金”到“红绿”&#xff1a;一个亟待建模的现实痛点 2026年的全球能源市场&#xff0c;早已不是简单的供需二体博弈。地缘政治摩擦、OPEC产量博弈、美联储利率路径摇摆、新能源替代进程加速——多…

作者头像 李华
网站建设 2026/8/25 0:56:23

Node.js 全栈 API 设计与 GraphQL 实:灰度阶段到底验证什么

Node.js 全栈 API 设计与 GraphQL 实&#xff1a;灰度阶段到底验证什么 API 灰度发布大家都在做&#xff0c;但很多团队的灰度过程流于形式&#xff1a;全量发布前放 5% 的流量跑半小时&#xff0c;只要 HTTP 200 状态码没报错&#xff0c;就闭着眼睛推到 100%。 对于基于 Grap…

作者头像 李华
网站建设 2026/8/25 0:54:54

天猫店群自动化管理系统:工程级可控的自动化,把封号概率压到极限

天猫店群自动化管理系统&#xff1a;工程级可控的自动化&#xff0c;把封号概率压到极限 电商自动化圈子里流传一句话&#xff1a;天猫的多店防关联管理&#xff0c;是店群运营中最耗人力也最容易出错的环节。 做店群的老板都知道&#xff0c;最怕的就是底层IP和硬件指纹穿帮…

作者头像 李华