You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
9.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!--
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"
}
-->
# 数据处理:关系型数据库
|![ Sketchnote by [(@sketchthedocs)](https://sketchthedocs.dev) ](../../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) 进行翻译。虽然我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。我们不对因使用此翻译而产生的任何误解或误读承担责任。