再探再报:SQL中的关联查询

在 SQL 中进行关联查询(或称联接查询)时,有几种不同的语法来执行这些查询,常见的包括:

  1. 使用 JOIN 关键字JOIN 关键字可与不同类型的联接组合使用,如 INNER JOINLEFT JOINRIGHT JOINFULL JOIN 等。例如:

    sql 复制代码
    SELECT *
    FROM TableA
    INNER JOIN TableB ON TableA.column = TableB.column;

    这种语法通过 ON 关键字指定连接条件进行表联接。

  2. 使用逗号分隔表 :在 FROM 子句中可以列出多个表名,使用逗号分隔。但不推荐在实际应用中使用这种方式,因为它与 ANSI SQL 标准不太一致,并且可读性较差。示例:

    sql 复制代码
    SELECT *
    FROM TableA, TableB
    WHERE TableA.column = TableB.column;

    此种方式实际上会执行笛卡尔积,然后通过 WHERE 子句进行过滤以得到想要的结果。不过,应该尽量避免使用这种语法,而是采用显式的 JOIN 语法。

  3. 子查询 :在 SELECT 语句中嵌套使用子查询来执行关联查询。例如:

    sql 复制代码
    SELECT *
    FROM TableA
    WHERE TableA.column IN (SELECT column FROM TableB WHERE condition);

    这种方式将一个查询的结果嵌套到另一个查询中,用于创建关联。这种方法适用于特定情况下的复杂查询。

以上是 SQL 中进行关联查询常用的几种语法形式。在实际应用中,推荐使用 JOIN 语法,因为它更为清晰明了,易于理解,并且符合 ANSI SQL 标准。

1.Join语法

1.1. LEFT JOIN

在数据库中,LEFT JOIN 是一种用于联结多个表的 SQL 查询语句。LEFT JOIN 返回左边表(也称为主表)中的所有记录,并且如果在右边表(也称为外部表)中存在匹配的记录,则返回匹配的记录。如果右边表中没有匹配的记录,则返回 NULL 值。

语法如下所示:

sql 复制代码
SELECT 列名列表
FROM 左边表
LEFT JOIN 右边表 ON 左边表.列名 = 右边表.列名;

这里有一个示例来说明 LEFT JOIN 的工作原理。假设有两个表 StudentsGrades

Students 表:

ID Name
1 Alice
2 Bob
3 Charlie

Grades 表:

Student_ID Grade
1 A
2 B
4 C

如果使用 LEFT JOIN 来结合这两个表,查询学生和他们的成绩(如果有的话),语句如下:

sql 复制代码
SELECT Students.Name, Grades.Grade
FROM Students
LEFT JOIN Grades ON Students.ID = Grades.Student_ID;

执行这个查询后,会得到如下结果:

Name Grade
Alice A
Bob B
Charlie NULL

这个结果解释如下:

  • Alice 和 Bob 在 Students 表和 Grades 表中都有匹配的记录,因此他们的成绩被显示出来。
  • Charlie 在 Students 表中有记录,但在 Grades 表中没有匹配的记录,因此他的成绩显示为 NULL

总之,LEFT JOIN 允许您检索左边表中的所有记录,并根据右边表中的匹配条件,返回相应的数据。如果右边表中没有匹配的记录,则返回 NULL

1.2. LEFT JOIN和INNER JOIN

INNER JOINLEFT JOIN 是 SQL 中常用的两种连接(或联结)表的方法,它们之间有一些重要的区别:

  1. INNER JOIN

    • INNER JOIN 返回两个表中满足连接条件的匹配行。

    • 结果集中只包含在两个表中都存在匹配的行。

    • 如果在左表或右表中没有匹配的行,则这些行不会出现在结果中。

    • 语法示例:

      sql 复制代码
      SELECT * FROM TableA INNER JOIN TableB ON TableA.column = TableB.column;
  2. LEFT JOIN

    • LEFT JOIN 返回左表中的所有行,以及右表中与左表中行匹配的行(如果有匹配的话)。

    • 如果右表中没有匹配的行,则在结果集中相关列显示为 NULL

    • 左表中的所有行都会出现在结果中,无论右表中是否有匹配。

    • 语法示例:

      sql 复制代码
      SELECT * FROM TableA LEFT JOIN TableB ON TableA.column = TableB.column;

举例说明:

假设有两个表:EmployeesDepartments

Employees

| EmployeeID | EmployeeName | DepartmentID |
|------------|--------------|--------------|
| 1          | Alice        | 1            |
| 2          | Bob          | 2            |
| 3          | Charlie      | 1            |

Departments 表:

| DepartmentID | DepartmentName |
|--------------|----------------|
| 1            | Sales          |
| 2            | Marketing      |
| 3            | HR             |

现在来比较 INNER JOINLEFT JOIN 的区别:

INNER JOIN 示例:

sql 复制代码
SELECT Employees.EmployeeName, Departments.DepartmentName
FROM Employees
INNER JOIN Departments ON Employees.DepartmentID = Departments.DepartmentID;

结果将是只有 Sales 和 Marketing 部门的员工信息:

| EmployeeName | DepartmentName |
|--------------|----------------|
| Alice        | Sales          |
| Bob          | Marketing      |
| Charlie      | Sales          |

注意,只有 Sales 和 Marketing 部门的员工被返回,因为只有这两个部门在 Employees 表和 Departments 表中都有匹配的记录。

LEFT JOIN 示例:

sql 复制代码
SELECT Employees.EmployeeName, Departments.DepartmentName
FROM Employees
LEFT JOIN Departments ON Employees.DepartmentID = Departments.DepartmentID;

结果将包括所有员工信息,但是对于 HR 部门(在 Departments 表中存在但在 Employees 表中没有匹配的记录),相关的部门信息显示为 NULL

| EmployeeName | DepartmentName |
|--------------|----------------|
| Alice        | Sales          |
| Bob          | Marketing      |
| Charlie      | Sales          |
| NULL         | HR             |

在这个例子中,LEFT JOIN 返回了 Employees 表中的所有员工信息,包括 HR 部门,因为它是左连接。但是,由于在 Employees 表中没有与 HR 部门相关的记录,所以在 DepartmentName 列中显示为 NULL

1.3. FULL JOIN

FULL JOIN 是 SQL 中用于连接两个表并返回它们之间所有匹配和不匹配行的联接操作。FULL JOIN 结合了 LEFT JOINRIGHT JOIN 的功能,它返回两个表中的所有行,并且对于不匹配的行,其中一个表中无对应匹配的行,相关列会显示 NULL 值。

语法结构如下:

sql 复制代码
SELECT *
FROM TableA
FULL JOIN TableB ON TableA.column = TableB.column;
  • FULL JOIN 结果包括了 LEFT JOINRIGHT JOIN 的结果,以及两个表中不匹配的行。
  • 返回的结果集包括了左表中所有的行和右表中所有的行,以及根据连接条件匹配的行。
  • 如果在连接条件中找不到匹配的行,则相关列将显示为 NULL
  • 如果一个表中有匹配而另一个表中没有匹配的行,结果集中将分别显示两个表中的数据,但没有对应的匹配值。

举个例子说明:

假设有两个表 EmployeesDepartments

Employees 表:

EmployeeID EmployeeName DepartmentID
1 Alice 1
2 Bob 2
3 Charlie 3

Departments 表:

DepartmentID DepartmentName
1 Sales
2 Marketing
4 HR

使用 FULL JOIN 进行联接:

sql 复制代码
SELECT Employees.EmployeeID, Employees.EmployeeName, Departments.DepartmentName
FROM Employees
FULL JOIN Departments ON Employees.DepartmentID = Departments.DepartmentID;

结果可能如下:

EmployeeID EmployeeName DepartmentName
1 Alice Sales
2 Bob Marketing
3 Charlie NULL
NULL NULL HR

以上结果显示了两个表中的所有行。前两行分别表示有匹配的员工和他们的部门,第三行表示一个员工在 Employees 表中有记录但在 Departments 表中没有匹配的部门信息,第四行表示在 Departments 表中有记录但在 Employees 表中没有匹配的员工信息。

1.4. CROSS JOIN

CROSS JOIN 是 SQL 中用于执行笛卡尔积(Cartesian Product)的一种联接方式。它返回两个表中所有可能的组合,即左表中的每一行都与右表中的每一行进行组合,生成一个新的虚拟表。

CROSS JOIN 不需要任何连接条件,因此它不关心两个表之间是否有相关的列或条件。它简单地将一个表的每一行与另一个表的每一行进行匹配,生成的结果行数为两个表行数的乘积。

语法如下:

sql 复制代码
SELECT *
FROM TableA
CROSS JOIN TableB;

举例说明:

假设有两个表 StudentsCourses

Students 表:

StudentID StudentName
1 Alice
2 Bob

Courses 表:

CourseID CourseName
101 Math
102 Science

使用 CROSS JOIN 进行联接:

sql 复制代码
SELECT Students.StudentID, Students.StudentName, Courses.CourseID, Courses.CourseName
FROM Students
CROSS JOIN Courses;

结果将是两个表中所有行的组合:

StudentID StudentName CourseID CourseName
1 Alice 101 Math
1 Alice 102 Science
2 Bob 101 Math
2 Bob 102 Science

在这个例子中,CROSS JOIN 返回了所有可能的学生和课程的组合,因为它不需要任何连接条件,所以它生成了两个表中所有行的笛卡尔积。需要注意的是,笛卡尔积会导致结果集行数快速增加,因此在实际应用中,应慎重使用 CROSS JOIN,特别是当两个表的行数较大时,可能会生成巨大的结果集。

2.逗号分隔表

逗号分隔表(Comma-Separated Tables)是 SQL 中一种在 FROM 子句中列出多个表的语法形式,用逗号 , 将表名分隔开来,从而允许同时查询多个表。这种语法可以用于执行表联接操作,但是它的使用方式不够明确,可读性较差,因此不推荐在实际开发中广泛使用。

2.1. 案例一

在逗号分隔表的语法中,可以列出多个表,并在查询中引用这些表。例如:

sql 复制代码
SELECT *
FROM TableA, TableB;

这样的语法形式将返回两个表的笛卡尔积。如果没有指定连接条件或者其他约束,将会生成两个表的所有可能的组合,称为笛卡尔积。

通常情况下,在逗号分隔表的写法下,如果没有指定 WHERE 子句或者其他连接条件,可能会意外地生成笛卡尔积,结果是两个表的所有行的组合。这可能会导致意外的大量数据,特别是当表中的行数很大时,这种查询会产生非常庞大的结果集。

2.2. 案例二

sql 复制代码
SELECT *
FROM TableA
INNER JOIN TableB ON TableA.id = TableB.id;

这个查询使用了逗号分隔表名的方式,并在 WHERE 子句中指定了连接条件 TableA.id = TableB.id。虽然这种写法可以执行表的联接操作,但实际上这是一个隐式的内连接。

这条查询语句会返回 TableATableB 中满足连接条件的行。在这种情况下,由于在 WHERE 子句中指定了 TableA.id = TableB.id,它执行了一个内连接,只返回两个表中在 id 列上匹配的行。

这种写法虽然能够实现表的联接,但不够明确。推荐使用显式的 JOIN 语法(如 INNER JOIN)来进行联接操作,因为它更清晰、易读,并且可以更明确地表达查询的意图,例如:

sql 复制代码
SELECT *
FROM TableA
INNER JOIN TableB ON TableA.id = TableB.id;

这种写法使用了明确的 INNER JOIN 语法来执行相同的内连接操作,更容易理解和维护。

2.3. 注意

  • 使用逗号分隔表进行表联接操作是一种较老的 SQL 写法,在现代的 SQL 标准中,更推荐使用显式的 JOIN 语法(如 INNER JOINLEFT JOIN 等),因为它们更为清晰和易读,并且能够明确表达查询的意图。
  • 显式使用 JOIN 语法(例如 INNER JOIN)能够提高代码的可读性,同时也更容易理解和维护。

3.子查询

子查询是指在 SQL 查询中嵌套的另一个查询。它允许在一个 SQL 查询中使用另一个查询的结果集作为条件或数据源。子查询可以嵌套在 SELECTINSERTUPDATEDELETE 语句的各个部分中,用于执行特定的子任务或过滤条件。

子查询通常用于以下场景:

  1. WHERE 子句中的子查询:作为过滤条件使用。
sql 复制代码
SELECT column1, column2
FROM table1
WHERE column1 = (SELECT column1 FROM table2 WHERE condition);
  1. FROM 子句中的子查询:作为数据源使用。
sql 复制代码
SELECT column1, column2
FROM (SELECT column1, column2 FROM table1) AS subquery;
  1. 在 INSERT 语句中的子查询:用于从另一个表中选择数据插入到目标表中。
sql 复制代码
INSERT INTO table1 (column1, column2)
SELECT column1, column2
FROM table2
WHERE condition;
  1. 在 UPDATE 语句中的子查询:用于根据另一个查询的结果更新数据。
sql 复制代码
UPDATE table1
SET column1 = (SELECT column1 FROM table2 WHERE condition)
WHERE condition;

子查询可以是标量子查询(返回单个值)、行子查询(返回一行结果集)或表子查询(返回多行结果集)。它们提供了更灵活的方式来构建复杂的查询,可以根据需要嵌套多个层级的子查询。

尽管子查询功能强大,但过多或复杂的嵌套子查询可能会降低查询性能。因此,在使用子查询时,需要谨慎选择合适的条件和结构,并进行必要的优化以确保查询的效率和性能。

相关推荐
难以触及的高度35 分钟前
mysql中between and怎么用
数据库·mysql
Jacky(易小天)1 小时前
MongoDB比较查询操作符中英对照表及实例详解
数据库·mongodb·typescript·比较操作符
Karoku0661 小时前
【企业级分布式系统】ELK优化
运维·服务器·数据库·elk·elasticsearch
莫叫石榴姐2 小时前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
小技与小术3 小时前
数据库表设计范式
数据库·mysql
安迁岚3 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验三 数据操作
运维·服务器·数据库·sql·mysql
安迁岚3 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验九 触发器
数据库·sql·mysql·oracle·实验报告
Loganer3 小时前
MongoDB分片集群搭建
数据库·mongodb
LKID体3 小时前
Python操作neo4j库py2neo使用之创建和查询(二)
数据库·python·neo4j
刘大浪3 小时前
后端数据增删改查基于Springboot+mybatis mysql 时间根据当时时间自动填充,数据库连接查询不一致,mysql数据库连接不好用
数据库·spring boot·mybatis