MySQL INNER JOIN
关系型数据库中的数据通常分散在多个表中,以保持效率并避免冗余(正如我们在模块 2 和模块 6 第一课中讨论数据库设计和主/外键时所探讨的那样)。为了检索结合了这些相关表中数据的有意义信息,我们使用 JOIN (连接) 操作。
INNER JOIN(内连接)是最基本、最常用的 JOIN 类型之一,用于根据两个或多个表之间的相关列合并它们的数据行。它仅返回在基于指定的连接条件在两个表中都有匹配项的行。
1. 理解 INNER JOIN
只要两个表的列之间存在匹配,INNER JOIN 关键字就会从两个表中选择所有行。当一个公共字段(或一组字段)具有匹配的值时,它有效地将两个表中的行组合在一起。在另一个表中没有对应匹配项的行将被排除在结果集之外。
考虑我们一直在使用的 world 数据库。它有一个 city(城市)表和一个 country(国家)表。每个城市都属于一个特定的国家。city 表有一个 CountryCode 列,这是一个引用 country 表中 Code 列(主键)的外键。INNER JOIN 允许我们在单个结果集中合并来自两个表的信息,例如城市名称及其对应的国家名称。
INNER JOIN 的基本语法是:
SELECT
column_list
FROM
table1
INNER JOIN
table2 ON table1.matching_column = table2.matching_column;SELECT column_list: 指定你希望从连接后的表中检索的列。FROM table1: 指示从中检索数据的第一个表。INNER JOIN table2: 指定要与table1连接的第二个表。ON table1.matching_column = table2.matching_column: 这是连接条件 (join condition)。它指定了两个表之间的关系。INNER JOIN将仅返回table1.matching_column中的值等于table2.matching_column中的值的行。
使用别名 (aliases) 来表示表名是一种常见的做法,这可以使查询更简短、更易读,尤其是在处理多个表或较长的表名时。
SELECT
c.Name AS CityName,
co.Name AS CountryName
FROM
city AS c
INNER JOIN
country AS co ON c.CountryCode = co.Code;在这里,c 是 city 表的别名,co 是 country 表的别名。通过缩短列引用的长度,这极大地提高了代码的可读性。
2. INNER JOIN 的详细示例
让我们使用 world 数据库,通过几个实际例子来说明 INNER JOIN。
2.1 示例 1:列出城市及其所属国家
要查看所有城市及其所属国家的列表,我们可以基于它们共同的 CountryCode/Code 列连接 city 和 country 表。
SELECT
city.Name AS City,
country.Name AS Country
FROM
city
INNER JOIN
country ON city.CountryCode = country.Code
ORDER BY
country.Name, city.Name;在这个查询中:
- 我们选择
city表中的Name(别名为 City)和country表中的Name(别名为 Country)。 - 我们将
city与country连接起来。 ON子句指定city表中的CountryCode必须匹配country表中的Code。ORDER BY用于先按国家、再按城市进行字母排序。
结果集将仅包含在 country 表中具有匹配国家代码的城市。如果由于某种原因,city 表中的某个城市具有在 country 表中不存在的 CountryCode,那么该城市将不会出现在结果中。
2.2 示例 2:组合三个表
INNER JOIN 可以扩展为组合两个以上的表。我们只需链接多个 INNER JOIN 子句。假设我们想要列出所有城市、它们的国家以及该国家所在的大洲。由于 country 表已经有了一个 Continent 列,我们不需要额外的连接:
SELECT
c.Name AS City,
co.Name AS Country,
co.Continent AS Continent
FROM
city AS c
INNER JOIN
country AS co ON c.CountryCode = co.Code
ORDER BY
co.Continent, co.Name, c.Name;现在,让我们创建一个更复杂的场景。假设我们有一个名为 countrylanguage 的表,存储了每个国家使用的官方语言的信息。该表具有 CountryCode 和 Language 列。
要列出城市、它们的国家以及该国家的官方语言,我们需要将 city 与 country 连接,然后将 country 与 countrylanguage 连接。
SELECT
c.Name AS City,
co.Name AS Country,
cl.Language AS OfficialLanguage
FROM
city AS c
INNER JOIN
country AS co ON c.CountryCode = co.Code
INNER JOIN
countrylanguage AS cl ON co.Code = cl.CountryCode
WHERE
cl.IsOfficial = 'T' -- 假设 'T' 代表官方语言
ORDER BY
co.Name, c.Name, cl.Language;在这里:
- 第一个
INNER JOIN使用c.CountryCode = co.Code将city连接到country。 - 第二个
INNER JOIN使用co.Code = cl.CountryCode将country连接到countrylanguage。 - 添加了一个
WHERE子句来仅过滤官方语言,演示了WHERE子句如何与连接配合使用。
2.3 示例 3:结合 GROUP BY 进行统计
INNER JOIN 本质上会过滤掉没有匹配项的行。这意味着如果一个国家存在于 country 表中,但在 city 表中没有对应的条目(可能是新录入的国家或数据错误),那么该国家将不会出现在 INNER JOIN 的结果中。
这种行为可以用来计算或识别确实有匹配项的实体。例如,如果你想知道每个国家有多少个城市,你可以将 INNER JOIN 与 GROUP BY 和 COUNT 结合使用。
SELECT
co.Name AS Country,
COUNT(c.ID) AS NumberOfCities
FROM
country AS co
INNER JOIN
city AS c ON co.Code = c.CountryCode
GROUP BY
co.Name
ORDER BY
NumberOfCities DESC;这个查询:
- 连接了
country和city。 - 按
country.Name对结果进行分组。 - 计算每个国家的城市
ID数量。
INNER JOIN 确保结果集中仅包含在 city 表中至少拥有一个城市的国家。拥有零个城市的国家不会出现在这个结果集中。
3. 将 INNER JOIN 与 WHERE 和 ORDER BY 结合
通常,你需要对连接的结果进行过滤或排序。WHERE 子句和 ORDER BY 子句是在连接操作之后应用的。
以客户订单系统为例:检索由 'Alice Smith' 下的订单并按订单日期排序。
SELECT
o.OrderID,
o.OrderDate,
o.TotalAmount,
c.FirstName,
c.LastName
FROM
Orders AS o
INNER JOIN
Customers AS c ON o.CustomerID = c.CustomerID
WHERE
c.FirstName = 'Alice' AND c.LastName = 'Smith'
ORDER BY
o.OrderDate DESC;此查询首先连接 Orders(订单)和 Customers(客户)表。然后,它过滤连接后的结果,仅显示由 'Alice Smith' 下的订单。最后,它按 OrderDate(订单日期)降序排列这些特定的订单。