|
|
<!--
|
|
|
CO_OP_TRANSLATOR_METADATA:
|
|
|
{
|
|
|
"original_hash": "9399d7b4767e75068f95ce5c660b285c",
|
|
|
"translation_date": "2025-09-05T11:31:40+00:00",
|
|
|
"source_file": "2-Working-With-Data/05-relational-databases/README.md",
|
|
|
"language_code": "zh"
|
|
|
}
|
|
|
-->
|
|
|
# 数据处理:关系型数据库
|
|
|
|
|
|
| ](../../sketchnotes/05-RelationalData.png)|
|
|
|
|:---:|
|
|
|
| 数据处理:关系型数据库 - _Sketchnote by [@nitya](https://twitter.com/nitya)_ |
|
|
|
|
|
|
你可能曾经使用过电子表格来存储信息。电子表格由一组行和列组成,其中行包含信息(或数据),列描述信息(有时称为元数据)。关系型数据库基于这种表格中行和列的核心原则构建,允许你将信息分散到多个表中。这使得你可以处理更复杂的数据,避免重复,并灵活地探索数据。让我们来探讨关系型数据库的概念。
|
|
|
|
|
|
## [课前测验](https://ff-quizzes.netlify.app/en/ds/quiz/8)
|
|
|
|
|
|
## 一切从表开始
|
|
|
|
|
|
关系型数据库的核心是表。就像电子表格一样,表是列和行的集合。行包含我们希望处理的数据或信息,例如城市名称或降雨量。列则描述存储的数据。
|
|
|
|
|
|
让我们从创建一个表来存储城市信息开始。我们可以从城市名称和国家开始。你可以将其存储在如下表中:
|
|
|
|
|
|
| 城市 | 国家 |
|
|
|
| -------- | ------------ |
|
|
|
| 东京 | 日本 |
|
|
|
| 亚特兰大 | 美国 |
|
|
|
| 奥克兰 | 新西兰 |
|
|
|
|
|
|
注意,**城市**、**国家**和**人口**列描述了存储的数据,每一行包含一个城市的信息。
|
|
|
|
|
|
## 单表方法的局限性
|
|
|
|
|
|
上述表可能对你来说比较熟悉。让我们开始向我们的数据库添加一些额外的数据——年度降雨量(单位:毫米)。我们将关注2018年、2019年和2020年。如果我们为东京添加数据,可能如下所示:
|
|
|
|
|
|
| 城市 | 国家 | 年份 | 降雨量 |
|
|
|
| ----- | ------ | ---- | ------ |
|
|
|
| 东京 | 日本 | 2020 | 1690 |
|
|
|
| 东京 | 日本 | 2019 | 1874 |
|
|
|
| 东京 | 日本 | 2018 | 1445 |
|
|
|
|
|
|
你注意到我们的表有什么问题了吗?你可能会发现我们重复了城市名称和国家信息。这会占用大量存储空间,而且没有必要重复这些信息。毕竟,我们只关心东京的一个名称。
|
|
|
|
|
|
好吧,让我们尝试另一种方法。我们为每一年添加新的列:
|
|
|
|
|
|
| 城市 | 国家 | 2018 | 2019 | 2020 |
|
|
|
| -------- | ------------ | ---- | ---- | ---- |
|
|
|
| 东京 | 日本 | 1445 | 1874 | 1690 |
|
|
|
| 亚特兰大 | 美国 | 1779 | 1111 | 1683 |
|
|
|
| 奥克兰 | 新西兰 | 1386 | 942 | 1176 |
|
|
|
|
|
|
虽然这种方法避免了行的重复,但也带来了其他问题。每次有新的一年,我们都需要修改表的结构。此外,随着数据的增长,将年份作为列会使得检索和计算值变得更加困难。
|
|
|
|
|
|
这就是为什么我们需要多个表和关系。通过拆分数据,我们可以避免重复,并在处理数据时拥有更大的灵活性。
|
|
|
|
|
|
## 关系的概念
|
|
|
|
|
|
让我们回到数据,确定如何拆分。我们知道需要存储城市的名称和国家信息,因此这部分数据最好存储在一个表中。
|
|
|
|
|
|
| 城市 | 国家 |
|
|
|
| -------- | ------------ |
|
|
|
| 东京 | 日本 |
|
|
|
| 亚特兰大 | 美国 |
|
|
|
| 奥克兰 | 新西兰 |
|
|
|
|
|
|
但在创建下一个表之前,我们需要弄清楚如何引用每个城市。我们需要某种形式的标识符、ID或(在技术数据库术语中)主键。主键是用于标识表中某一特定行的值。虽然这可以基于某个值本身(例如,我们可以使用城市名称),但它几乎总是一个数字或其他标识符。我们不希望ID发生变化,因为这会破坏关系。大多数情况下,主键或ID通常是自动生成的数字。
|
|
|
|
|
|
> ✅ 主键通常缩写为PK
|
|
|
|
|
|
### 城市表
|
|
|
|
|
|
| city_id | 城市 | 国家 |
|
|
|
| ------- | -------- | ------------ |
|
|
|
| 1 | 东京 | 日本 |
|
|
|
| 2 | 亚特兰大 | 美国 |
|
|
|
| 3 | 奥克兰 | 新西兰 |
|
|
|
|
|
|
> ✅ 在本课程中,你会注意到我们交替使用“id”和“主键”这两个术语。这里的概念同样适用于DataFrame,你将在后续学习中探索。虽然DataFrame不使用“主键”这一术语,但你会发现它们的行为非常相似。
|
|
|
|
|
|
创建城市表后,让我们存储降雨量。与其重复城市的完整信息,我们可以使用ID。我们还应该确保新创建的表也有一个*id*列,因为所有表都应该有一个ID或主键。
|
|
|
|
|
|
### 降雨量表
|
|
|
|
|
|
| rainfall_id | city_id | 年份 | 降雨量 |
|
|
|
| ----------- | ------- | ---- | ------ |
|
|
|
| 1 | 1 | 2018 | 1445 |
|
|
|
| 2 | 1 | 2019 | 1874 |
|
|
|
| 3 | 1 | 2020 | 1690 |
|
|
|
| 4 | 2 | 2018 | 1779 |
|
|
|
| 5 | 2 | 2019 | 1111 |
|
|
|
| 6 | 2 | 2020 | 1683 |
|
|
|
| 7 | 3 | 2018 | 1386 |
|
|
|
| 8 | 3 | 2019 | 942 |
|
|
|
| 9 | 3 | 2020 | 1176 |
|
|
|
|
|
|
注意新创建的**降雨量**表中的**city_id**列。该列包含引用**城市**表中ID的值。在关系型数据的技术术语中,这被称为**外键**;它是另一个表的主键。你可以简单地将其视为一个引用或指针。**city_id** 1引用的是东京。
|
|
|
|
|
|
> [!NOTE] 外键通常缩写为FK
|
|
|
|
|
|
## 数据检索
|
|
|
|
|
|
将数据分成两个表后,你可能会想如何检索它。如果我们使用MySQL、SQL Server或Oracle等关系型数据库,可以使用一种名为结构化查询语言(SQL)的语言。SQL(有时读作“sequel”)是一种标准语言,用于在关系型数据库中检索和修改数据。
|
|
|
|
|
|
要检索数据,你可以使用命令`SELECT`。其核心是,你**选择**想要查看的列,**从**它们所在的表中。如果你只想显示城市名称,可以使用以下命令:
|
|
|
|
|
|
```sql
|
|
|
SELECT city
|
|
|
FROM cities;
|
|
|
|
|
|
-- Output:
|
|
|
-- Tokyo
|
|
|
-- Atlanta
|
|
|
-- Auckland
|
|
|
```
|
|
|
|
|
|
`SELECT`用于列出列名,`FROM`用于列出表名。
|
|
|
|
|
|
> [NOTE] SQL语法不区分大小写,这意味着`select`和`SELECT`是一样的。然而,根据你使用的数据库类型,列名和表名可能区分大小写。因此,最佳实践是始终将编程中的所有内容视为区分大小写。在编写SQL查询时,常见的约定是将关键字全部写成大写字母。
|
|
|
|
|
|
上述查询将显示所有城市。假设我们只想显示新西兰的城市。我们需要某种形式的过滤器。SQL中的关键字是`WHERE`,即“某条件为真”。
|
|
|
|
|
|
```sql
|
|
|
SELECT city
|
|
|
FROM cities
|
|
|
WHERE country = 'New Zealand';
|
|
|
|
|
|
-- Output:
|
|
|
-- Auckland
|
|
|
```
|
|
|
|
|
|
## 数据连接
|
|
|
|
|
|
到目前为止,我们只从单个表中检索数据。现在我们想将**城市**和**降雨量**中的数据结合起来。这可以通过*连接*来实现。你实际上是在两个表之间创建一个连接,并将每个表中的列值匹配起来。
|
|
|
|
|
|
在我们的例子中,我们将匹配**降雨量**表中的**city_id**列和**城市**表中的**city_id**列。这将把降雨量值与其对应的城市匹配起来。我们将执行一种称为*内连接*的连接类型,这意味着如果某些行与另一个表中的任何内容不匹配,它们将不会被显示。在我们的例子中,每个城市都有降雨量,因此所有内容都会被显示。
|
|
|
|
|
|
让我们检索2019年所有城市的降雨量。
|
|
|
|
|
|
我们将分步骤进行。第一步是通过指明连接的列来连接数据——如前所述的**city_id**。
|
|
|
|
|
|
```sql
|
|
|
SELECT cities.city
|
|
|
rainfall.amount
|
|
|
FROM cities
|
|
|
INNER JOIN rainfall ON cities.city_id = rainfall.city_id
|
|
|
```
|
|
|
|
|
|
我们已经标出了我们需要的两列,以及我们希望通过**city_id**连接表的事实。现在我们可以添加`WHERE`语句来过滤出仅2019年的数据。
|
|
|
|
|
|
```sql
|
|
|
SELECT cities.city
|
|
|
rainfall.amount
|
|
|
FROM cities
|
|
|
INNER JOIN rainfall ON cities.city_id = rainfall.city_id
|
|
|
WHERE rainfall.year = 2019
|
|
|
|
|
|
-- Output
|
|
|
|
|
|
-- city | amount
|
|
|
-- -------- | ------
|
|
|
-- Tokyo | 1874
|
|
|
-- Atlanta | 1111
|
|
|
-- Auckland | 942
|
|
|
```
|
|
|
|
|
|
## 总结
|
|
|
|
|
|
关系型数据库的核心是将信息分成多个表,然后将其重新组合以进行显示和分析。这提供了极大的灵活性来执行计算或以其他方式操作数据。你已经了解了关系型数据库的核心概念,以及如何在两个表之间执行连接。
|
|
|
|
|
|
## 🚀 挑战
|
|
|
|
|
|
互联网上有许多关系型数据库可供使用。你可以通过使用上述技能来探索数据。
|
|
|
|
|
|
## 课后测验
|
|
|
|
|
|
## [课后测验](https://ff-quizzes.netlify.app/en/ds/quiz/9)
|
|
|
|
|
|
## 复习与自学
|
|
|
|
|
|
[Microsoft Learn](https://docs.microsoft.com/learn?WT.mc_id=academic-77958-bethanycheum)上有许多资源可供你继续探索SQL和关系型数据库的概念
|
|
|
|
|
|
- [描述关系型数据的概念](https://docs.microsoft.com//learn/modules/describe-concepts-of-relational-data?WT.mc_id=academic-77958-bethanycheum)
|
|
|
- [开始使用Transact-SQL进行查询](https://docs.microsoft.com//learn/paths/get-started-querying-with-transact-sql?WT.mc_id=academic-77958-bethanycheum)(Transact-SQL是SQL的一种版本)
|
|
|
- [Microsoft Learn上的SQL内容](https://docs.microsoft.com/learn/browse/?products=azure-sql-database%2Csql-server&expanded=azure&WT.mc_id=academic-77958-bethanycheum)
|
|
|
|
|
|
## 作业
|
|
|
|
|
|
[作业标题](assignment.md)
|
|
|
|
|
|
---
|
|
|
|
|
|
**免责声明**:
|
|
|
本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。虽然我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。我们不对因使用此翻译而产生的任何误解或误读承担责任。 |