数据库管理系统02-子查询(Subqueries)

本教程深入解析 SQL 高级查询技术,涵盖 JOIN 连接、子查询、集合操作、聚合函数及复杂查询,帮助掌握数据库操作的核心技能。

主要内容

  • SQL 集合操作 (UNIONINTERSECTMINUS) 及 MySQL 替代方案
  • 子查询 (Subqueries)INEXISTSANYALL 的应用
  • 多表查询 (Multi-Table Queries)INNER JOINLEFT JOINRIGHT JOINCROSS JOIN
  • 聚合查询 (Aggregations):结合 GROUP BYHAVING 进行数据统计
  • SQL 练习题:解决常见数据分析问题,如查找未下单的客户、部门最高薪资、房产租赁分析等

SQL 集合操作(SET Operations)详细解析

1️⃣ SQL 集合操作的概念

集合操作用于 合并对比 不同 SELECT 语句的结果集。常见的集合操作有:

  • UNION:合并两个 SELECT 查询的结果,并去重。
  • UNION ALL:合并两个 SELECT 查询的结果,但不去重
  • INTERSECT(MySQL 不支持):返回两个 SELECT 查询的交集(共同存在的行)。
  • MINUS / EXCEPT(MySQL 不支持):返回仅在第一个 SELECT 查询中存在,但不在第二个查询中的行。

注意:MySQL 不支持 INTERSECTMINUS,但可以用 JOINNOT IN 来实现类似功能。


2️⃣ UNIONUNION ALL

UNION 语法

SELECT column1, column2 FROM table1
UNION
SELECT column1, column2 FROM table2;

作用

  • 合并两个查询的结果集自动去重
  • 去重会影响性能,适用于 结果需要唯一性 的情况。

UNION 示例

场景:查询所有客户和员工的姓名
SELECT fName AS Name FROM Customers
UNION
SELECT fName FROM Employees;

查询结果

Name
Alice
Bob
Charlie
David

去除了重复的姓名(即使 CustomersEmployees 表里可能有相同的 fName)。


UNION ALL 语法

SELECT column1, column2 FROM table1
UNION ALL
SELECT column1, column2 FROM table2;

作用

  • 合并两个查询的结果集,不会去重(比 UNION 更快)。
  • 适用于 不关心重复数据,且希望提高查询性能 的情况。

UNION ALL 示例

SELECT fName AS Name FROM Customers
UNION ALL
SELECT fName FROM Employees;

查询结果

Name
Alice
Bob
Bob
Charlie
David

区别

  • UNION 去重 → 只会出现一个 Bob
  • UNION ALL 不去重Bob 出现了两次。

UNION vs UNION ALL 对比

操作 是否去重 性能 适用场景
UNION 去重 较慢(需去重计算) 需要唯一值时,如去重后的客户名单
UNION ALL 不去重 较快(仅合并结果) 允许重复值,提高查询性能

3️⃣ INTERSECT(交集)

INTERSECT 概念

  • INTERSECT 返回 两个 SELECT 查询共同存在的记录(即交集)。
  • MySQL 不支持 INTERSECT,可以使用 INNER JOININ 代替。

INTERSECT 语法(适用于 SQL Server, PostgreSQL, Oracle)

SELECT column1, column2 FROM table1
INTERSECT
SELECT column1, column2 FROM table2;

INTERSECT MySQL 替代方案

场景:查询 CustomersEmployees 都有的 fName
SELECT fName FROM Customers
WHERE fName IN (SELECT fName FROM Employees);

查询结果

fName
Bob

等价于 INTERSECT

  • 只返回 CustomersEmployees 都存在fName
  • IN 的性能不如 INNER JOIN,大数据集时推荐 INNER JOIN

INTERSECT MySQL 替代方案:INNER JOIN

SELECT c.fName FROM Customers c
INNER JOIN Employees e ON c.fName = e.fName;

查询结果

fName
Bob

INNER JOIN 更高效,避免 IN 的子查询开销。


4️⃣ MINUS / EXCEPT(差集)

MINUS / EXCEPT 概念

  • MINUS(Oracle) / EXCEPT(SQL Server、PostgreSQL)返回 第一个 SELECT 查询有,而第二个 SELECT 没有的记录
  • MySQL 不支持 MINUS / EXCEPT,但可以用 NOT INLEFT JOIN 代替。

MINUS 语法(适用于 Oracle)

SELECT column1 FROM table1
MINUS
SELECT column1 FROM table2;

效果

  • 仅返回 table1 中有,而 table2 没有 的数据。

MINUS MySQL 替代方案

场景:查询 Customers 里有,而 Employees 里没有的 fName
SELECT fName FROM Customers
WHERE fName NOT IN (SELECT fName FROM Employees);

查询结果

fName
Alice
Charlie

等价于 MINUS

  • 只返回 Customers 有,而 Employees 没有fName

MINUS MySQL 替代方案:LEFT JOIN

SELECT c.fName FROM Customers c
LEFT JOIN Employees e ON c.fName = e.fName
WHERE e.fName IS NULL;

查询结果

fName
Alice
Charlie

LEFT JOIN + IS NULL 更高效,避免 NOT IN 的子查询开销。


重点总结

操作 作用 MySQL 支持? 替代方案
UNION 合并两个查询结果,去重 ✅ 支持 -
UNION ALL 合并两个查询结果,不去重 ✅ 支持 -
INTERSECT 取两个查询的交集 ❌ 不支持 INNER JOIN / IN
MINUS / EXCEPT table1 有但 table2 没有的行 ❌ 不支持 NOT IN / LEFT JOIN

SQL 子查询(Subqueries)详细解析

2️⃣ SQL 子查询的概念

子查询(Subquery)是一个嵌套在 SELECTINSERTUPDATEDELETE 语句中的查询。它用于:

  • 进行 嵌套查询,让查询变得更灵活。
  • 动态获取数据,避免写死数据,提高查询的通用性。
  • 与主查询进行比较,用于 INEXISTSANYALL 语法中。

子查询必须返回一个或多个值,但不能在 WHERE 直接使用多列返回。


1️⃣ 子查询的基本语法

SELECT column1, column2 
FROM table1 
WHERE column3 OPERATOR (SELECT column4 FROM table2 WHERE condition);

说明

  • OPERATOR 可以是 =, >, <, >=, <=, !=, IN, EXISTS, ANY, ALL 等。

2️⃣ 子查询的分类

1. WHERE 中的子查询

子查询用于 WHERE 语句,用于 条件匹配

✅ 示例:查找薪资高于平均薪资的员工
SELECT staffNo, fName, salary 
FROM Staff
WHERE salary > (SELECT AVG(salary) FROM Staff);

解析

  1. 子查询 计算平均薪资 AVG(salary)
  2. 主查询 选出薪资 salary 高于该值的员工。

2. HAVING 中的子查询

用于 HAVING 过滤分组结果

✅ 示例:查找至少有 2 名员工的分支
SELECT branchNo, COUNT(*) AS num_employees
FROM Staff
GROUP BY branchNo
HAVING COUNT(*) >= (SELECT AVG(num_employees) FROM (
    SELECT COUNT(*) AS num_employees FROM Staff GROUP BY branchNo
) AS branch_counts);

解析

  1. 子查询 计算所有分支的平均员工数量。
  2. 主查询 选出员工数高于该平均值的分支。

3️⃣ INNOT IN 子查询

IN 子查询

用于查找 符合子查询返回的任意值 的行。

✅ 示例:查找所有在伦敦分支工作的员工
SELECT fName, lName 
FROM Staff
WHERE branchNo IN (SELECT branchNo FROM Branch WHERE city = 'London');

解析

  • 子查询获取 所有在伦敦的 branchNo
  • 主查询选择 branchNo 在这些值中的员工

NOT IN 子查询

用于查找 不符合子查询返回值 的记录。

✅ 示例:查找未分配到任何房产的顾客
SELECT customerNo, fName
FROM Customers
WHERE customerNo NOT IN (SELECT customerNo FROM PropertyRentals);

解析

  • 子查询 获取所有租过房产的 customerNo
  • 主查询 选择 不在这些 customerNo 中的客户

4️⃣ EXISTSNOT EXISTS 子查询

EXISTS

  • 如果子查询返回至少一行数据,EXISTS 返回 TRUE,否则返回 FALSE
  • 适用于 检查某个条件是否成立
✅ 示例:查找有租房记录的客户
SELECT customerNo, fName
FROM Customers c
WHERE EXISTS (SELECT * FROM PropertyRentals p WHERE c.customerNo = p.customerNo);

解析

  • 子查询 查找 customerNo 是否存在于 PropertyRentals
  • 只返回 有租房记录的客户

NOT EXISTS

  • 反向逻辑:子查询返回空时,NOT EXISTS 才返回 TRUE
✅ 示例:查找没有租房记录的客户
SELECT customerNo, fName
FROM Customers c
WHERE NOT EXISTS (SELECT * FROM PropertyRentals p WHERE c.customerNo = p.customerNo);

解析

  • 子查询 检查 customerNo 是否存在 PropertyRentals 表。
  • 仅返回 没有租房记录的客户

5️⃣ ANYALL 子查询

ANY

  • 如果主查询的值与子查询返回的 任意一个值匹配,则返回 TRUE
  • 适用于 ><>=<=!= 这些运算符
✅ 示例:查找薪资高于 branchNo='B003' 任意一个员工的员工
SELECT staffNo, fName, salary
FROM Staff
WHERE salary > ANY (SELECT salary FROM Staff WHERE branchNo = 'B003');

解析

  • 子查询 获取 B003 的所有薪资。
  • 主查询 选出薪资大于这些值中的最小值的员工。

ALL

  • 如果主查询的值与子查询返回的所有值都匹配,则返回 TRUE
  • 适用于 ><>=<=!= 这些运算符
✅ 示例:查找薪资高于 branchNo='B003' 所有员工的员工
SELECT staffNo, fName, salary
FROM Staff
WHERE salary > ALL (SELECT salary FROM Staff WHERE branchNo = 'B003');

解析

  • 子查询 获取 B003 所有员工的薪资。
  • 主查询 选出薪资大于 B003 最高薪资的员工

6️⃣ 相关子查询(Correlated Subqueries)

相关子查询是指子查询依赖于主查询的值,即子查询在执行时,每一行都要重新计算一次。

✅ 示例:查找比自己所在分支平均薪资高的员工
SELECT staffNo, fName, salary, branchNo
FROM Staff s1
WHERE salary > (SELECT AVG(salary) FROM Staff s2 WHERE s1.branchNo = s2.branchNo);

解析

  • s1 代表主查询中的员工。
  • s2 在子查询中计算 s1.branchNo 对应的平均薪资。

重点总结

子查询类型 作用 示例
IN 匹配子查询返回的值 WHERE branchNo IN (SELECT branchNo FROM Branch WHERE city='London')
NOT IN 过滤子查询返回的值 WHERE customerNo NOT IN (SELECT customerNo FROM PropertyRentals)
EXISTS 检查子查询是否有数据 WHERE EXISTS (SELECT * FROM PropertyRentals p WHERE c.customerNo = p.customerNo)
NOT EXISTS 检查子查询是否返回空 WHERE NOT EXISTS (SELECT * FROM PropertyRentals p WHERE c.customerNo = p.customerNo)
ANY 比较子查询返回的某个值 WHERE salary > ANY (SELECT salary FROM Staff WHERE branchNo='B003')
ALL 比较子查询返回的所有值 WHERE salary > ALL (SELECT salary FROM Staff WHERE branchNo='B003')

SQL 多表查询(Multi-Table Queries)详细解析

在 SQL 中,多表查询(Multi-Table Queries)是数据库操作的关键技能。JOIN 连接多个表,可以整合数据、实现复杂查询,并提高查询效率。本节将详细讲解 SQL 中的 JOIN 连接、笛卡尔积(Cartesian Product)、JOIN 结合聚合查询(Aggregations)等核心知识


1️⃣ SQL JOIN 连接

JOIN 用于 将两个或多个表按关联字段连接起来,从而合并数据,主要类型有:

JOIN 类型 作用
INNER JOIN 只返回两张表都匹配的行
LEFT JOIN(或 LEFT OUTER JOIN 返回左表所有行,右表没有匹配时返回 NULL
RIGHT JOIN(或 RIGHT OUTER JOIN 返回右表所有行,左表没有匹配时返回 NULL
FULL JOIN(或 FULL OUTER JOIN 返回左右表的所有匹配和不匹配数据(MySQL 不支持)
CROSS JOIN 生成笛卡尔积(所有可能组合)

MySQL 不支持 FULL JOIN,可以用 LEFT JOINRIGHT JOIN 结合 UNION 实现。


2️⃣ INNER JOIN(内连接)

INNER JOIN 作用

  • 只返回 两张表都匹配的行,不会返回 NULL
  • 最常用的 JOIN 类型,适用于数据完全匹配的查询。

✅ 示例:查询所有员工及其所在城市

SELECT s.staffNo, s.fName, b.city
FROM Staff s
INNER JOIN Branch b ON s.branchNo = b.branchNo;

查询结果

staffNo fName city
SA1 John London
SA2 Alice Glasgow
SA3 Bob Bristol

解析

  • ON s.branchNo = b.branchNo 确保 Staff 表的 branchNoBranch 表的 branchNo 匹配
  • 如果某个 Staff 没有对应 Branch,该员工不会出现在结果中!

3️⃣ LEFT JOIN(左连接)

LEFT JOIN 作用

  • 返回左表所有行,即使右表没有匹配,也会返回 NULL
  • 用于查询左表有数据,但右表可能没有数据的情况

✅ 示例:查询所有分支及其员工(即使某些分支没有员工)

SELECT b.branchNo, b.city, s.staffNo, s.fName
FROM Branch b
LEFT JOIN Staff s ON b.branchNo = s.branchNo;

查询结果

branchNo city staffNo fName
B001 London SA1 John
B002 Glasgow SA2 Alice
B003 Bristol NULL NULL

解析

  • B003 没有员工,但仍然显示 NULL,因为 LEFT JOIN 保留了 Branch 表的所有行。

4️⃣ RIGHT JOIN(右连接)

RIGHT JOIN 作用

  • 返回右表所有行,即使左表没有匹配,也会返回 NULL
  • 适用于查询右表中所有数据,即使左表没有对应匹配

✅ 示例:查询所有员工及其分支(即使某些员工没有分支)

SELECT s.staffNo, s.fName, b.city
FROM Staff s
RIGHT JOIN Branch b ON s.branchNo = b.branchNo;

查询结果

staffNo fName city
SA1 John London
SA2 Alice Glasgow
NULL NULL Bristol

解析

  • B003 没有员工,但仍然在 Branch 表中,因此 staffNofNameNULL

5️⃣ CROSS JOIN(笛卡尔积)

CROSS JOIN 作用

  • 不使用 ON 条件,会返回两个表的所有可能组合m × n 行)。
  • 容易造成数据爆炸,一般不常用。

✅ 示例:获取所有员工和所有分支的所有可能组合

SELECT s.staffNo, s.fName, b.branchNo, b.city
FROM Staff s
CROSS JOIN Branch b;

如果 Staff 有 3 行,Branch 有 3 行,则结果有 3 × 3 = 9 行。

避免使用 CROSS JOIN,除非明确需要计算所有可能组合!


6️⃣ JOIN 结合 GROUP BYHAVING

JOIN 结合 GROUP BYHAVING 用于 统计数据、进行聚合分析

统计每个分支的员工数量

SELECT b.branchNo, COUNT(s.staffNo) AS num_employees
FROM Staff s
JOIN Branch b ON s.branchNo = b.branchNo
GROUP BY b.branchNo;

查询结果

branchNo num_employees
B001 2
B002 1

解析

  • GROUP BY b.branchNobranchNo 分组。
  • COUNT(s.staffNo) 统计每个分支的员工数。

统计没有员工的分支

SELECT b.branchNo, COUNT(s.staffNo) AS num_employees
FROM Branch b
LEFT JOIN Staff s ON b.branchNo = s.branchNo
GROUP BY b.branchNo
HAVING COUNT(s.staffNo) = 0;

查询结果

branchNo num_employees
B003 0

解析

  • 使用 LEFT JOIN 保留 Branch 表的所有记录。
  • HAVING COUNT(s.staffNo) = 0 过滤出没有员工的分支

重点总结

JOIN 类型 作用 是否支持 NULL
INNER JOIN 只返回两表都匹配的行 ❌ 不支持
LEFT JOIN 左表所有行,右表匹配不到填 NULL ✅ 支持
RIGHT JOIN 右表所有行,左表匹配不到填 NULL ✅ 支持
CROSS JOIN 笛卡尔积(所有可能组合) ❌ 不支持

JOIN 查询实战

  1. 查询所有员工的姓名及其所在分支城市(INNER JOIN)。
  2. 查询所有分支及其员工数量(LEFT JOIN + GROUP BY)。
  3. 查询没有员工的分支(LEFT JOIN + HAVING)。
  4. 查询所有分支和所有可能的员工组合(CROSS JOIN)。

SQL JOIN 结合聚合查询(Aggregations)详细解析

在 SQL 中,JOIN 结合聚合函数(Aggregation Functions),可以在多表查询的基础上进行统计、汇总、分组分析,从而实现更复杂的数据查询需求。


1️⃣ SQL 聚合函数(Aggregation Functions)

SQL 聚合函数用于计算一组值的统计信息,常见的聚合函数包括:

聚合函数 作用
COUNT() 计算行数
SUM() 计算总和
AVG() 计算平均值
MAX() 计算最大值
MIN() 计算最小值

2️⃣ JOIN 结合 GROUP BY

JOIN 多表查询的基础上,可以使用 GROUP BY 进行分组统计

语法

SELECT column1, AGGREGATE_FUNCTION(column2)
FROM table1
JOIN table2 ON table1.common_column = table2.common_column
GROUP BY column1;

示例:统计每个分支机构的员工数量

SELECT b.branchNo, COUNT(s.staffNo) AS num_employees
FROM Staff s
JOIN Branch b ON s.branchNo = b.branchNo
GROUP BY b.branchNo;

查询结果

branchNo num_employees
B001 2
B002 1

解析

  • GROUP BY b.branchNo → 按分支机构 branchNo 分组。
  • COUNT(s.staffNo) → 统计每个分支的员工数。

3️⃣ HAVING 过滤分组结果

WHERE 不能用于聚合函数的筛选,因为 WHEREGROUP BY 之前执行。
要过滤分组后的聚合结果,需要用 HAVING

示例:筛选至少有 2 名员工的分支

SELECT b.branchNo, COUNT(s.staffNo) AS num_employees
FROM Staff s
JOIN Branch b ON s.branchNo = b.branchNo
GROUP BY b.branchNo
HAVING COUNT(s.staffNo) >= 2;

查询结果

branchNo num_employees
B001 2

解析

  • HAVING COUNT(s.staffNo) >= 2 → 只返回员工数量大于等于 2 的分支。

4️⃣ JOIN 结合 SUM()

示例:统计每个分支机构的总工资

SELECT b.branchNo, SUM(s.salary) AS total_salary
FROM Staff s
JOIN Branch b ON s.branchNo = b.branchNo
GROUP BY b.branchNo;

查询结果

branchNo total_salary
B001 60000
B002 45000

解析

  • SUM(s.salary) → 计算该分支所有员工的工资总和。
  • 适用场景:财务报表、预算分析等。

5️⃣ JOIN 结合 AVG()

示例:统计每个分支机构的平均工资

SELECT b.branchNo, AVG(s.salary) AS avg_salary
FROM Staff s
JOIN Branch b ON s.branchNo = b.branchNo
GROUP BY b.branchNo;

查询结果

branchNo avg_salary
B001 30000
B002 45000

解析

  • AVG(s.salary) → 计算该分支所有员工的平均工资。

6️⃣ JOIN 结合 MAX()MIN()

示例:查询每个分支机构最高和最低薪资

SELECT b.branchNo, MAX(s.salary) AS max_salary, MIN(s.salary) AS min_salary
FROM Staff s
JOIN Branch b ON s.branchNo = b.branchNo
GROUP BY b.branchNo;

查询结果

branchNo max_salary min_salary
B001 50000 25000
B002 45000 45000

解析

  • MAX(s.salary) → 查询最高薪资。
  • MIN(s.salary) → 查询最低薪资。

7️⃣ LEFT JOIN 处理无匹配数据

当某些分支机构没有员工时,INNER JOIN丢失这些分支。
可以用 LEFT JOIN 保留所有分支,并将 NULL 替换为 0

示例:统计每个分支机构的员工数(即使没有员工)

SELECT b.branchNo, COALESCE(COUNT(s.staffNo), 0) AS num_employees
FROM Branch b
LEFT JOIN Staff s ON b.branchNo = s.branchNo
GROUP BY b.branchNo;

查询结果

branchNo num_employees
B001 2
B002 1
B003 0

解析

  • B003 没有员工,但 LEFT JOIN 仍然保留该分支,并返回 0 而不是 NULLCOALESCE() 处理 NULL)。

8️⃣ JOIN 结合 ORDER BY

可以结合 ORDER BY 进行排序,如按员工总数降序排序

示例:按员工数降序排列

SELECT b.branchNo, COUNT(s.staffNo) AS num_employees
FROM Staff s
JOIN Branch b ON s.branchNo = b.branchNo
GROUP BY b.branchNo
ORDER BY num_employees DESC;

查询结果

branchNo num_employees
B001 3
B002 2
B003 1

解析

  • ORDER BY num_employees DESC → 按员工数量降序排列。

重点总结

查询类型 SQL 语法 作用
统计员工数量 COUNT(s.staffNo) 统计每个分支的员工数量
计算工资总额 SUM(s.salary) 计算每个分支的工资总额
计算平均工资 AVG(s.salary) 计算每个分支的平均工资
查询最高工资 MAX(s.salary) 找出每个分支的最高工资
查询最低工资 MIN(s.salary) 找出每个分支的最低工资
按分支分组 GROUP BY b.branchNo branchNo 统计数据
过滤聚合结果 HAVING COUNT(*) > 2 只显示符合条件的分支
保留所有分支 LEFT JOIN 确保无员工的分支也被显示
按员工数排序 ORDER BY num_employees DESC 让数据更易读

SQL 练习题解析(综合运用 JOINGROUP BY、子查询等)

在这一部分,我们将深入分析 SQL 练习题,重点是使用 JOIN 结合 GROUP BYHAVING、子查询、LEFT JOINORDER BY 等 SQL 技巧来解决常见的数据查询问题。


1️⃣ 练习题 1:找出从未下单的顾客

题目要求

查询 从未在 Orders 表中下过单的顾客,即 Customers 表中的 customerNo Orders 表中不存在

✅ 解决方案 1:使用 NOT IN

SELECT customerNo, fName
FROM Customers
WHERE customerNo NOT IN (SELECT customerNo FROM Orders);

解析

  • 子查询 SELECT customerNo FROM Orders 获取所有下过单的 customerNo
  • NOT IN 过滤掉这些客户,只保留从未下单的顾客。

✅ 解决方案 2:使用 LEFT JOIN + IS NULL(推荐,效率更高)

SELECT c.customerNo, c.fName
FROM Customers c
LEFT JOIN Orders o ON c.customerNo = o.customerNo
WHERE o.customerNo IS NULL;

解析

  • LEFT JOIN 保留所有 Customers 数据,即使 Orders 里没有匹配项。
  • WHERE o.customerNo IS NULL 过滤掉有订单的顾客,只保留未下单的顾客。

推荐用 LEFT JOIN,避免 NOT IN 在大数据集上的性能问题。


2️⃣ 练习题 2:查询部门最高薪资

题目要求

查找 每个部门薪资最高的员工,返回 departmentNo, fName, salary

✅ 解决方案 1:使用 IN 子查询

SELECT departmentNo, fName, salary
FROM Employees
WHERE (departmentNo, salary) IN (
    SELECT departmentNo, MAX(salary)
    FROM Employees
    GROUP BY departmentNo
);

解析

  • 子查询 SELECT departmentNo, MAX(salary) FROM Employees GROUP BY departmentNo 获取每个部门的最高薪资
  • 主查询 过滤出 departmentNosalary 符合该最大值的员工。

✅ 解决方案 2:使用 JOIN

SELECT e.departmentNo, e.fName, e.salary
FROM Employees e
JOIN (
    SELECT departmentNo, MAX(salary) AS max_salary
    FROM Employees
    GROUP BY departmentNo
) sub ON e.departmentNo = sub.departmentNo AND e.salary = sub.max_salary;

解析

  • 子查询 sub 获取每个 departmentNoMAX(salary)
  • JOIN 结合 subEmployees,选出符合该最大薪资的员工。

推荐用 JOIN,在大数据集上比 IN 子查询更快。


3️⃣ 练习题 3:查询温度上升的日期

题目要求

假设 Weather 表有以下列:

CREATE TABLE Weather (
    id INT PRIMARY KEY,
    recordDate DATE,
    temperature INT
);
  • 查询温度比前一天高的日期,返回 recordDate, temperature

✅ 解决方案:使用 JOIN 进行自关联

SELECT w1.recordDate, w1.temperature
FROM Weather w1
JOIN Weather w2
ON DATEDIFF(w1.recordDate, w2.recordDate) = 1
WHERE w1.temperature > w2.temperature;

解析

  • JOINWeather 自连接w1w2 分别代表不同日期的记录。
  • DATEDIFF(w1.recordDate, w2.recordDate) = 1 确保 w1 的日期比 w2 晚 1 天。
  • w1.temperature > w2.temperature 过滤掉温度未上升的日期。

JOIN 方式是处理比较当前行与上一行」的常见方法。


4️⃣ 练习题 4:查询至少有 2 套房产的分支机构

题目要求

查找 拥有至少 2 套出租房产 的分支机构 branchNo

✅ 解决方案

SELECT branchNo, COUNT(*) AS num_properties
FROM PropertyRentals
GROUP BY branchNo
HAVING COUNT(*) >= 2;

解析

  • GROUP BY branchNo → 按分支机构分组。
  • COUNT(*) → 计算每个分支机构的房产数量。
  • HAVING COUNT(*) >= 2 → 过滤出拥有至少 2 套房产的分支。

使用 HAVING 进行分组后筛选。


5️⃣ 练习题 5:统计不同职位的员工人数

题目要求

查找 不同职位(position)的员工数量

✅ 解决方案

SELECT position, COUNT(*) AS num_employees
FROM Employees
GROUP BY position;

查询结果

position num_employees
Manager 3
Assistant 5
Clerk 2

解析

  • GROUP BY position → 按 position 进行分组。
  • COUNT(*) → 统计每个职位的员工人数。

常用于 HR 数据分析,如统计每个职位的招聘需求。


6️⃣ 练习题 6:查询从未出租过的房产

题目要求

查询 Property 表中所有从未被出租的房产信息。

✅ 解决方案

SELECT p.propertyNo, p.address
FROM Property p
LEFT JOIN PropertyRentals r ON p.propertyNo = r.propertyNo
WHERE r.propertyNo IS NULL;

解析

  • LEFT JOIN 保留所有 Property 数据
  • WHERE r.propertyNo IS NULL → 只保留没有匹配租赁记录的房产

适用于查找“没有关联记录”的情况。


重点总结

查询目标 SQL 解决方案
找出从未下单的顾客 LEFT JOIN + IS NULLNOT IN
查询部门最高薪资 JOIN 结合 MAX(salary)
查询温度上升的日期 JOINWeather 自连接
查询至少 2 套房产的分支 GROUP BY + HAVING COUNT(*) >= 2
统计不同职位的员工人数 GROUP BY position + COUNT(*)
查询从未出租的房产 LEFT JOIN + IS NULL

你可能感兴趣的:(数据库)