子查询和JOIN的用法和区别

引言

在数据库查询中,子查询和JOIN是两个常用的工具,用于从数据库中检索和组织数据。这篇文章将介绍这两者的用法和区别,以帮助读者更好地理解如何在SQL中利用它们。

一、表结构和数据

sql 复制代码
CREATE TABLE Task (
    task_id INT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    status VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE WorkHour (
    workhour_id INT PRIMARY KEY AUTO_INCREMENT,
    task_id INT,
    hours_spent DECIMAL(8, 2) NOT NULL,
    work_date DATE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (task_id) REFERENCES Task(task_id)
);

INSERT INTO Task (title, description, status) VALUES
    ('Task 1', 'Description for Task 1', 'In Progress'),
    ('Task 2', 'Description for Task 2', 'Completed'),
    ('Task 3', 'Description for Task 3', 'Not Started');

INSERT INTO WorkHour (task_id, hours_spent, work_date) VALUES
    (1, 5.5, '2023-01-01'),
    (1, 2.0, '2023-01-02'),
    (2, 3.0, '2023-01-02'),
    (2, 4.5, '2023-01-03'),
    (3, 6.0, '2023-01-01');

二、子查询的用法

0. 子查询的基本概念

子查询是指在查询语句内嵌套另一个查询语句,将嵌套的查询的结果用于主查询。它可以出现在SELECT、FROM、WHERE或HAVING子句中,执行不同的任务。

1. 在WHERE子句中使用子查询

例如,可以使用子查询在同一表中找到工时超过10小时的任务:

sql 复制代码
SELECT task_id, title
FROM Task
WHERE task_id IN (SELECT task_id FROM WorkHour WHERE hours_spent > 10);

2. 在SELECT子句中使用子查询

子查询也可以用于在SELECT子句中执行聚合操作,例如计算每个任务的平均工时:

sql 复制代码
SELECT task_id, title, (SELECT AVG(hours_spent) FROM WorkHour WHERE Task.task_id = WorkHour.task_id) AS avg_hours
FROM Task;

3. 在 FROM 子句中使用子查询:查询每个任务及其对应的工时总和

在 FROM 子句中使用子查询:查询每个任务及其对应的工时总和

  1. 第一种:先算出总和然后join连表
  2. 第二种:先连表join然后分组算总和
sql 复制代码
SELECT Task.task_id, Task.title, TotalHours.total_hours
FROM Task
LEFT JOIN (
    SELECT task_id, SUM(hours_spent) AS total_hours
    FROM WorkHour
    GROUP BY task_id
) AS TotalHours ON Task.task_id = TotalHours.task_id;

4. 在 HAVING 子句中使用子查询:找到工时总和超过6小时的任务

sql 复制代码
SELECT Task.task_id, Task.title, COALESCE(SUM(WorkHour.hours_spent), 0) AS total_hours
FROM Task
LEFT JOIN WorkHour ON Task.task_id = WorkHour.task_id
GROUP BY Task.task_id, Task.title
HAVING total_hours > 6;

子查询也可以用于在SELECT子句中执行聚合操作,例如计算每个任务的平均工时:

sql 复制代码
SELECT Task.task_id, Task.title, TotalHours.total_hours
FROM Task
LEFT JOIN (
    SELECT task_id, SUM(hours_spent) AS total_hours
    FROM WorkHour
    GROUP BY task_id
) AS TotalHours ON Task.task_id = TotalHours.task_id;

三、JOIN的用法

0. JOIN的基本概念

JOIN用于在多个表之间建立关联,通过共享列将它们连接在一起。JOIN操作通常用于处理多表关系,执行更复杂的数据检索任务。上述的业务有人喜欢子查询去实现,但有人喜欢先将表连一起,再做处理,下面我们来看看join如何实现上述业务的。

1. 使用JOIN连接表

例如,使用INNER JOIN找到工时超过2小时的任务:

sql 复制代码
SELECT Task.task_id, Task.title
FROM Task
INNER JOIN WorkHour ON Task.task_id = WorkHour.task_id
WHERE WorkHour.hours_spent > 2;

2. 查询每个任务的平均工时

sql 复制代码
SELECT Task.task_id, Task.title, AVG(WorkHour.hours_spent) AS avg_hours
FROM Task
LEFT JOIN WorkHour ON Task.task_id = WorkHour.task_id
GROUP BY Task.task_id, Task.title;

3. 查询每个任务及其对应的工时总和

sql 复制代码
SELECT Task.task_id, Task.title, COALESCE(SUM(WorkHour.hours_spent), 0) AS total_hours
FROM Task
LEFT JOIN WorkHour ON Task.task_id = WorkHour.task_id
GROUP BY Task.task_id, Task.title;

四、区别和适用场景

1. 可读性和语法简洁性

子查询通常更直观,语法相对简单,对于一些简单的嵌套查询场景更易于理解。JOIN语句在处理多表关联时提供了更清晰的语法。

2. 性能

在性能方面,JOIN通常更为高效,尤其是在大型数据集上。数据库系统可以通过优化JOIN操作来提高查询效率。

3. 子查询的适用场景

  • 在同一表内部执行比较、过滤和聚合操作时。
  • 用于在WHERE子句中进行条件过滤。
  • 在SELECT子句中执行嵌套聚合操作。

4. JOIN的适用场景

  • 处理多表关联,解决复杂的查询需求。
  • 查询多个表之间的关系,进行更灵活的数据检索。
  • 需要在查询中使用多个表的列时。

五、结论

子查询和JOIN是SQL中强大的工具,各有其优势。在实际应用中,根据具体需求和数据模型,选择使用哪种方式是关键。可读性、性能、适用场景等因素都应该被综合考虑,以确保查询既正确又高效。根据情况选择合适的工具,将有助于编写清晰、高效的数据库查询。 通常情况下,JOIN 和子查询都可以用来实现相同的功能,但它们在某些情况下可能有不同的性能表现,并且在某些情境下更适合使用一种而不是另一种。

下面是一些比较 JOIN 和子查询的一般指导原则:

  1. 可读性: 子查询通常对于理解查询的意图更直观,因为它们嵌套在主查询中。在某些情况下,子查询可以使 SQL 查询更易读。

  2. 性能: 在某些情况下,JOIN 操作可能比子查询更高效。数据库系统可以通过优化 JOIN 操作来更有效地执行查询。然而,优化可能因数据库引擎而异,有时子查询可能更为高效。

  3. 存在性: JOIN 通常用于关联两个或多个表,而子查询通常用于在单个表中进行嵌套查询。如果您需要关联表,则使用 JOIN 更合适。如果只是在单个表中进行嵌套查询,可以考虑使用子查询。

在单个表中进行嵌套查询通常指的是在同一个表中的子查询。这种情况可能包括对表中的某些列进行聚合、过滤或比较,并将子查询的结果用作主查询的一部分。

  1. 聚合函数的使用: 如果您想在同一表的不同行之间执行聚合函数(如计算总和、平均值、最大值等),则可以考虑使用子查询。例如,找到表中所有销售额高于平均销售额的行:

    sql 复制代码
    SELECT * FROM sales
    WHERE amount > (SELECT AVG(amount) FROM sales);
  2. 子查询作为过滤条件: 如果您想根据同一表中的子集行进行过滤,可以使用子查询作为过滤条件。例如,找到所有销售额高于某个阈值的销售记录:

    sql 复制代码
    SELECT * FROM sales
    WHERE customer_id IN (SELECT customer_id FROM sales WHERE amount > 1000);
  3. 比较列与子查询结果: 比较表中的某一列与子查询的结果,以确定是否满足某些条件。例如,找到比某个日期之后注册的所有用户:

    sql 复制代码
    SELECT * FROM users
    WHERE registration_date > (SELECT MAX(registration_date) FROM users);

总的来说,单个表中的嵌套查询主要用于在同一表内部执行比较、过滤和聚合操作。这可以帮助您根据表内的某些条件选择特定的行。

  1. 复杂性: 在一些情况下,使用 JOIN 可以更容易理解和维护,尤其是在处理多个表之间的复杂关系时。

在实际应用中,性能往往是一个关键因素,您可能需要根据您的具体数据库和数据模型测试和优化查询性能。在一些情况下,选择 JOIN 或子查询可能取决于具体的查询要求、数据量和索引的使用情况。

相关推荐
有想法的py工程师19 分钟前
PostgreSQL + Debezium CDC 踩坑总结
数据库·postgresql
Nandeska33 分钟前
2、数据库的索引与底层数据结构
数据结构·数据库
Victor3561 小时前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易1 小时前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
小卒过河01041 小时前
使用apache nifi 从数据库文件表路径拉取远程文件至远程服务器目的地址
运维·服务器·数据库
Kiri霧1 小时前
Range循环和切片
前端·后端·学习·golang
过期动态1 小时前
JDBC高级篇:优化、封装与事务全流程指南
android·java·开发语言·数据库·python·mysql
WizLC1 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Mr.朱鹏1 小时前
SQL深度分页问题案例实战
java·数据库·spring boot·sql·spring·spring cloud·kafka
Victor3561 小时前
Netty(19)Netty的性能优化手段有哪些?
后端