<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ 数据库 - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ 数据库 - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 06 May 2026 14:33:25 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/database/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 关系型数据库 VS 非关系型数据库——SQL DB 和 NoSQL DB 的区别 ]]>
                </title>
                <description>
                    <![CDATA[ 原文：Relational VS Nonrelational Databases – the Difference Between a SQL DB and a NoSQL DB [https://www.freecodecamp.org/news/relational-vs-nonrelational-databases-difference-between-sql-db-and-nosql-db/] ，作者：Dionysia Lemonaki [https://www.freecodecamp.org/news/author/dionysia/] 本文概述了关系和非关系数据库的区别，你还将了解如何根据它们的优缺点来决定哪一个更适合相应的项目。 我们将讨论：  * 数据库的定义 * SQL 是什么?          * 关系型数据库 * 特点     * ACID 属性    ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/relational-vs-nonrelational-databases-difference-between-sql-db-and-nosql-db/</link>
                <guid isPermaLink="false">627cdf4ac9c067061df8b8bd</guid>
                
                    <category>
                        <![CDATA[ 数据库 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ ZhichengChen ]]>
                </dc:creator>
                <pubDate>Thu, 12 May 2022 05:19:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/05/valeriia-svitlini-5w0ZbF8P5-4-unsplash.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/relational-vs-nonrelational-databases-difference-between-sql-db-and-nosql-db/">Relational VS Nonrelational Databases – the Difference Between a SQL DB and a NoSQL DB</a>，作者：<a href="https://www.freecodecamp.org/news/author/dionysia/">Dionysia Lemonaki</a></p><!--kg-card-begin: markdown--><p>本文概述了关系和非关系数据库的区别，你还将了解如何根据它们的优缺点来决定哪一个更适合相应的项目。</p>
<p>我们将讨论：</p>
<ul>
<li><a href="#definition">数据库的定义</a>
<ul>
<li><a href="#sql">SQL 是什么?</a></li>
</ul>
</li>
<li><a href="#relational">关系型数据库</a>
<ul>
<li><a href="#characteristics">特点</a></li>
<li><a href="#acid">ACID 属性</a></li>
</ul>
</li>
<li><a href="#non-relational">非关系型数据库</a>
<ul>
<li><a href="#types">类型</a></li>
<li><a href="#base">BASE 属性</a></li>
</ul>
</li>
<li><a href="#pick">关系型数据库 VS 非关系型数据库</a></li>
<li><a href="#extra">拓展阅读</a></li>
</ul>
<h2 id="">什么是数据库？针对初学者的定义</h2>
<p>在计算机里，数据是以不同形式出现的信息片段。它可以是文本、数字、图像、音频片段或视频。</p>
<p>信息集合需要被存储、处理和解释。这时就需要一种可按需轻松搜索、访问、提取和检索已保存资源的方法。该方法可以使计算机或人类分析可访问的数据、执行计算和比较、做出逻辑决策得出结论。</p>
<p>当然可以使用 Excel 电子表格等软件将数据存储在文件中，这样也可以完成有限的工作。</p>
<p>但是，如果数据量很大，使用 Excel 处理就捉襟见肘了。</p>
<p>在数据量增大时，Excel 无法快速检索。并且 Excel 很难固定其数据结构。</p>
<p>数据库是一种更易于访问、更高效且更有条理的长期存储和处理信息的方式。</p>
<p>数据库存储数据的规范性和系统性以及其检索数据的便捷性使其成为基于 Web 的应用程序中重要的部分。</p>
<p>数据库几乎可以用于所有应用程序。它们可以用来存储用户信息，例如用户名、电子邮件地址、加密密码和物理地址。</p>
<p>它们还存储用户行为。例如，在电商网站中，数据库会保存并跟踪“收藏”的商品。</p>
<p>一般使用<strong>数据库管理系统</strong>（或简称 DBMS）来管理数据库。</p>
<p>数据库管理系统是一个软件程序，充当最终用户和数据库中间的媒介。可以通过数据库管理系统创建和管理数据库。也可以执行查询来访问、修改和操作存储在数据库中的数据。</p>
<p>只需通过一些命令就可以方便轻松地存储、检索、更新和删除数据。</p>
<p>谈到数据库管理系统，通常有<strong>两种</strong>类型可供选择：</p>
<ul>
<li><strong>关系型数据库</strong>（也就是 <strong>SQL 数据库</strong>）</li>
<li><strong>非关系型数据库</strong>（也就是 <strong>NoSQL 数据库</strong>）</li>
</ul>
<h3 id="sql">SQL 是什么?</h3>
<p>SQL 是 <strong>S</strong>tructured <strong>Q</strong>uery <strong>L</strong>anguage 的缩写。</p>
<p>你可能会听过它以两种发音方式 - “<em>S. Q. L.</em>”（ess-kew-ell）或“<em>se-quel</em>”（/ˈsēkwəl/）。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/04/Screenshot-2022-04-13-at-6.25.32-PM.png" alt="https://i.imgur.com/NtGaNA8.png" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>https://i.imgur.com/NtGaNA8.png</figcaption>
</figure>
<p>无论哪种方式，SQL 都代表数据库处理的语言。</p>
<p>具体来说，使用 SQL，可以编写数据库查询以和数据库进行通信。查询可以是用于执行任何 CRUD（创建、读取、更新、删除）操作的命令。</p>
<p>SQL 是关系数据库管理系统的首选语言，将在下一节中详细介绍。</p>
<h2 id="">什么是关系数据库？</h2>
<p>关系数据库（或 SQL 数据库）已经存在了一段时间。第一个关系数据库出现在 1970 年，关系数据库至今仍然很流行，一些最常见的如：</p>
<ul>
<li><a href="https://www.postgresql.org/">PostgreSQL</a></li>
<li><a href="https://www.microsoft.com/en-us/sql-server/sql-server-downloads">Microsoft SQL Server</a></li>
<li><a href="https://www.mysql.com/">MySQL</a></li>
<li><a href="https://www.oracle.com/index.html">Oracle</a></li>
<li><a href="https://sqlite.org/index.html">SQLite</a></li>
</ul>
<p>关系数据库以结构化和表的方式存储数据。也就是说，它将信息存储在<strong>表</strong>中，可以将其视为数据的存储容器。例如，一家公司可以有一个 <code>employees</code> 表来存储其员工的数据。</p>
<p>关系数据库具有严格的、静态的预定义逻辑<strong>架构（schema）</strong>。可以将数据库架构视为一个组织蓝图——一组规则：哪些可以插入表、哪些不能插入表、以及如何设置数据。</p>
<p>在每个表中，至少有一个<strong>列（column）</strong>。这些列具有特定的数据类型，例如 <code>INTEGER</code> 或 <code>VARCHAR</code>。在 <code>employees</code> 表中，一些列可能是 <code>employee_id</code>、<code>name</code>、<code>department</code>、<code>email</code> 和 <code>salary</code>。</p>
<p>所有列和其数据类型构成架构。</p>
<pre><code class="language-sql">             EMPLOYEES

+-------------+------+------------+-------+--------+
| employee_id | name | department | email | salary |
+-------------+------+------------+-------+--------+
</code></pre>
<p>一个表也会包含<strong>行（rows）</strong> 或 <em>记录（records）</em>。记录是遵守预定义架构的单个数据条目。本质上，它是一个数据项。</p>
<pre><code class="language-sql">             EMPLOYEES
+-------------+------------------+------------+-----------------------+--------+
| employee_id |       name       | department |         email         | salary |
+-------------+------------------+------------+-----------------------+--------+
|           1 |  John Doe        | IT         | johndoe@company.com   |   3500 |
|           2 |  Kelly Kellinson | Marketing  | kelly@company.com     |   1500 |
|           3 |  Mike Manson     | Product    | mikekane@company.com  |   2300 |
+-------------+------------------+------------+-----------------------+--------+
</code></pre>
<p>由于关系数据库支持 SQL，所以可以直接执行查询。例如，如果想 <code>view</code> 月薪 <code>greater than 2000 dollars</code> 的 <code>employees</code> 的 <code>names</code>，那么可以编写如下 SQL 查询：</p>
<pre><code class="language-SQL">SELECT name FROM employees
WHERE salary &gt; 2000;
</code></pre>
<p>执行上面的查询，会获得以下输出：</p>
<pre><code class="language-SQL">+-------------+
|    name     |
+-------------+
| John Doe    |
| Mike Manson |
+-------------+
</code></pre>
<h3 id="">关系数据库的特点</h3>
<p>到目前为止，已经了解了关系数据库：</p>
<ul>
<li>是表格格式</li>
<li>非常有条理，并且数据以某种结构存储</li>
<li>具有严格、预定义的架构</li>
<li>使用 SQL 执行数据库查询和操作数据</li>
</ul>
<p>此外，一个关系数据库可以有多个表，正如数据库管理系统的名称所暗示的那样，这些表可以是相互关联的。</p>
<p>例如，一家电商公司可能有一个 <code>products</code> 表、一个 <code>users</code> 表、一个 <code>emails</code> 表和一个 <code>orders</code> 表。</p>
<p>由于表和存储在其中的信息之间存在连接和关联，可以使用命令来连接表。</p>
<p>关系数据库有一个主键，它作为标识符，确保表中的每一项都是唯一的，从而确保表中没有重复和冗余的数据。</p>
<p><em>外键</em> 用于表示在表之间的关系。</p>
<p>不同表中的数据可以有不同的关系：</p>
<ul>
<li><strong>一对一的关系</strong>：在这种情况下，一个表中的记录仅与另一个表中的一条记录相关。电商网站中一对一关系的示例是，一个用户只能拥有一个电子邮件地址，且一个电子邮件地址只能属于一个用户。</li>
<li><strong>一对多关系</strong>：在这种情况下，一张表中的一条记录与另一张表中的多条其他记录相关。例如，在电商网站中，一个用户可以下许多订单，但每个订单都是由一个用户下的。</li>
<li><strong>多对多关系</strong>：在这种情况下，一个表中的一个或多个记录可以与另一个表中的一个或多个记录相关。例如，在电商网站中，一个订单可以有很多产品，而一个产品可以被购买多次。</li>
</ul>
<h3 id="acid">关系数据库中的 ACID 属性</h3>
<p>关系数据库提供 ACID 数据一致性模型。</p>
<p>ACID 是原子性（<strong>A</strong>tomicity）、一致性（<strong>C</strong>onsistency）、事务隔离（<strong>I</strong>solation）、持久性（<strong>D</strong>urability） 的首字母缩写词。</p>
<p><strong>原子性</strong>意味着事务是原子的并且采取 “all or nothing” 的方法。</p>
<p>也就是，要么整个操作成功，从头到尾完成，要么不成功，整个操作“回滚”。</p>
<p>所有操作都保证以成功或失败结束，不存在部分成功。</p>
<p><strong>一致性</strong>是确保数据库结构从事务开始到结束保持不变。一致性确保进入数据库的任何数据都遵循已设置的规则和约束。它可以保护和维护关系数据库中数据的完整性。</p>
<p><strong>事务隔离</strong>意味着尽管在任何时候都发生了许多事务，但每个事务都被视为一个原子的、独立的单元，并且事务似乎是按顺序发生的。</p>
<p>例如，如果两个事务同时发生，此属性可确保一个事务以及那里发生的更改不会以任何方式影响另一个事务。</p>
<p>最后，<strong>持久性</strong>意味着事务的任何结果和更改都已提交，因此是永久性的，并且将持续存在，即使出现系统故障也是如此。</p>
<p>ACID 模型可确保数据库可靠且安全。</p>
<h2 id="">什么是非关系数据库？</h2>
<p>非关系型数据库也称为 NoSQL 数据库。经常会看到 NoSQL 代表“<strong>N</strong>ot <strong>o</strong>nly <strong>SQL</strong>”和“Non-SQL”。</p>
<p>无论哪种方式，非关系数据库都是指不使用关系数据模型的数据库。</p>
<p>尽管这个术语和这种类型的数据库已经存在了几十年，但 NoSQL 数据库在 1990 年代后期才开始受欢迎，当时 Internet 也变得越来越流行。</p>
<p>关系数据库已无法满足互联网海量且复杂的数据。</p>
<p>一些最流行的非关系数据库：</p>
<ul>
<li><a href="https://www.mongodb.com/">MongoDB</a></li>
<li><a href="https://redis.io/">Redis</a></li>
<li><a href="https://cassandra.apache.org/_/index.html">Apache Cassandra</a></li>
<li><a href="https://cloud.google.com/bigtable">Google Cloud Bigtable</a></li>
<li><a href="https://aws.amazon.com/dynamodb/">Amazon DynamoDB</a></li>
</ul>
<p>非关系数据库不以表格式存储和组织数据。不同数据点之间没有表、行、列或关系。</p>
<p>相反，数据存储在<strong>集合</strong>中。数据库通常是非结构化的并使用动态架构。</p>
<h3 id="">非关系数据库的类型</h3>
<p>有四种主要类型的非关系数据库：</p>
<ul>
<li><strong>列式数据库</strong></li>
<li><strong>键-值数据库</strong></li>
<li><strong>面向文档的数据库</strong></li>
<li><strong>图数据库</strong></li>
</ul>
<p><strong>列式数据库</strong>在概念上类似于关系数据库，但它们使用组或列集（也称为列族）而不是行来逻辑组织相关数据。可以通过使用与单个列关联的唯一行键来独立访问列族。列式数据库搜索特定数据的速度很快，因为无需通过不相关的信息行来查找要搜索的内容。</p>
<p><strong>键-值数据库</strong>是最简单的非关系数据库类型之一。数据以键值对集合的形式存储在字典或哈希表中。这种类型的数据库具有唯一的键。键充当指向特定值的指针并与该值相关联。分配给键的值可以是任何信息和数据类型。要检索和访问该值，请使用唯一键作为引用。</p>
<p><strong>面向文档的数据库</strong>也以键值对的方式存储数据。但是其值是一个文档，它有一个唯一的键作为它的标识符。文档可以是任何格式，例如 XML、YAML 或二进制，通常采用 JSON 格式。这种类型的数据库以半结构化的方式存储数据。没有架构或预定义的结构。正因为如此，它更灵活，可以在项目需求发生变化时重新安排和重新设计数据库结构。它还提供了类似 SQL 的查询语言或者通过 API 来对数据执行查询以及 CRUD 操作。</p>
<p><strong>图数据库</strong>是最复杂的非关系数据库类型，它们可以处理大量数据。图数据库专注于数据元素之间的连接和关系，并使用图论来存储、搜索和管理这些关系。图数据库使用 <em>nodes</em> 来存储数据，用 <em>nodes</em> 表示单个实体或数据。一个节点连接到另一个节点。为了表示实体之间的连接或关系，图数据库还用到了 <em>edges</em>。</p>
<h3 id="base">非关系数据库中的 BASE 属性</h3>
<p>非关系数据库提供 BASE 数据库一致性模型。该模型不像关系数据库的 ACID 模型那样严格。</p>
<p>BASE 是以下的首字母缩写词：</p>
<ul>
<li><strong>B</strong>asic <strong>A</strong>vailability 基本可用，该模型不关注数据的即时一致性。但是，该系统似乎在持续工作，并始终保证数据的可用性。</li>
<li><strong>S</strong>oft 软状态，由于缺乏即时一致性，系统的状态可能会随着时间而改变。软状态意味着系统不需要写一致性。</li>
<li><strong>E</strong>ventual 最终一致性，主要优先事项是数据的持续可用，而不是数据一致性。但是，最终在某个时候，可以期望数据是一致的。当系统停止接收输入时，可能会发生这种情况。</li>
</ul>
<h2 id="sqlnosql">如何在 SQL 和 NoSQL 数据库之间进行选择</h2>
<p>在学习了 SQL 和 NoSQL 数据库的基础知识之后，可能想知道如何为项目选择适合类型的数据库。</p>
<p>嗯，这个问题没有明确的答案。</p>
<p>两种数据库都有优点和缺点，这在很大程度上取决于正在构建的应用程序的类型、将使用的数据的类型以及的未来目标。</p>
<p>通常在产品中都会涉及到这两种类型的数据库。</p>
<p>以下是它们特征的快速摘要，可帮助决定哪一个可能更适合。</p>
<h3 id="sql">何时使用 SQL 数据库</h3>
<ul>
<li>需要分布在多个表中的高度结构化的数据，需要数据遵守严格的、可预测的、预定义的和已经计划好的模式。</li>
<li>数据将保持相对不变。如果不打算频繁更改数据库的结构并且不需要定期更新项目，SQL 数据库会很方便。请记住，它们提供的灵活性很小。</li>
<li>需要一致的数据。</li>
<li>数据完整性和安全性是重中之重。</li>
<li>需要复杂查询的准确结果。</li>
</ul>
<p>SQL 数据库的一个缺点是它们是垂直扩展的。当存储变多时，需要增加当前机器上的硬件和提高计算能力。这可能代价高昂。需要增加处理能力和内存存储来处理增加的负载以提高性能。</p>
<h3 id="nosql">何时使用 NoSQL 数据库</h3>
<ul>
<li>在一个快速的开发环境中工作，需要经常调整需求并不断更改数据库结构。</li>
<li>正在处理大量性质不同但不需要大量结构或准确性的数据。</li>
<li>正在处理需要频繁更新的数据。NoSQL 数据库提供了一个松散、灵活和动态的模式，允许对数据进行定期更改。</li>
<li>需要快速的查询结果和系统的持续可用性。</li>
<li>不想对数据库进行任何前期规划、准备或设计，而是想立即开始构建。</li>
</ul>
<p>NoSQL 数据库的一大优势是它们可以水平扩展。</p>
<p>它们的设计方式可以将更多机器添加到现有机器（例如云服务器）中。与需要额外 CPU（中央处理单元）或 RAM（随机存取存储器）资源的垂直缩放相比，这种行为更可取。</p>
<p>但当然，NoSQL 数据库的一个缺点是它们不能确保数据的完整性和一致性。</p>
<h2 id="">拓展阅读</h2>
<p>这篇文章只是触及了皮毛，最好的学习方法就是边做边学。</p>
<p>以下是一些学习资源，可用于了解有关数据库和 SQL 的更多信息：</p>
<ul>
<li><a href="https://www.freecodecamp.org/news/learn-sql-free-relational-database-courses-for-beginners/">Learn SQL – Free Relational Database Courses for Beginners</a>，为这篇文章添加书签以获取免费 SQL 课程列表。</li>
<li><a href="https://chinese.freecodecamp.org/learn/relational-database/">freeCodeCamp 关系数据库（Beta） 认证</a>，在本课程中，你将学习到必要的开发人员工具，然后将学习如何使用代码编辑器、命令行和 Git，还将学习使用 PostgreSQL（一种关系数据库管理系统）和 SQL——它的查询语言。</li>
<li><a href="https://www.freecodecamp.org/news/learn-nosql-in-3-hours/">Learn About NoSQL Databases in This 3-hour Course</a>，在本课程中，将了解四种不同的 NoSQL 数据库类型。除了理论之外，还有实战。</li>
</ul>
<h3 id="">结语</h3>
<p>你已经到了文章的结尾！</p>
<p>希望你了解了关系数据库和非关系数据库之间的主要区别。文末的额外的资源可以继续学习，将新技能付诸实践。</p>
<p>感谢你阅读本文，祝你编码愉快！</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ MongoDB 基础教程 ]]>
                </title>
                <description>
                    <![CDATA[ MongoDB 是一个丰富的面向文档的 NoSQL 数据库。 在本文中，我会分享一些有关 MongoDB 命令的基本知识，例如查询、过滤数据、删除、更新等等。 好的，我们开始吧！ 配置 为了使用 MongoDB，首先需要在计算机上安装 MongoDB。为此，请访问官方下载中心 [https://www.mongodb.com/download-center/community]，下载适用于你的操作系统的版本。在这篇文章里，我会以 Windows 为例。 下载 MongoDB 社区服务器设置后，你将依次点击“下一个”完成安装。完成后，转到安装了 MongoDB 的 C 驱动器。打开程序文件，然后选择 MongoDB 目录。 C: -> Program Files -> MongoDB -> Server -> 4.0(version) -> bin 在 bin 目录中，你会发现几个有趣的可执行文件。  * mongod  * mongo 我们看一下这两个文件。 mongod 代表“ Mongo Daemon”。mongod ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/learn-mongodb/</link>
                <guid isPermaLink="false">5ff7d58b39641a0517d5359e</guid>
                
                    <category>
                        <![CDATA[ MongoDB ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 数据库 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chengjun.L ]]>
                </dc:creator>
                <pubDate>Wed, 02 Jun 2021 09:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/01/pietro-jeng-n6B49lTx7NM-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>MongoDB 是一个丰富的面向文档的 NoSQL 数据库。</p><p>在本文中，我会分享一些有关 MongoDB 命令的基本知识，例如查询、过滤数据、删除、更新等等。</p><p><strong>好的，我们开始吧！</strong></p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2021/01/image.png" class="kg-image" alt="image" width="800" height="211" loading="lazy"></figure><h3 id="-"><strong>配置</strong></h3><p>为了使用 MongoDB，首先需要在计算机上安装 MongoDB。为此，请访问<a href="https://www.mongodb.com/download-center/community">官方下载中心</a>，下载适用于你的操作系统的版本。在这篇文章里，我会以 Windows 为例。</p><p>下载 MongoDB 社区服务器设置后，你将依次点击“下一个”完成安装。完成后，转到安装了 MongoDB 的 C 驱动器。打开程序文件，然后选择 MongoDB 目录。</p><pre><code>C: -&gt; Program Files -&gt; MongoDB -&gt; Server -&gt; 4.0(version) -&gt; bin</code></pre><p>在 bin 目录中，你会发现几个有趣的可执行文件。</p><ul><li>mongod</li><li>mongo</li></ul><p>我们看一下这两个文件。</p><p>mongod 代表“ Mongo Daemon”。mongod 是 MongoDB 使用的后台进程。mongod 的主要目的是管理所有 MongoDB 服务器任务，例如：接受请求，响应客户端和内存管理。</p><p>mongo 是可以与客户端（例如，系统管理员和开发人员）进行交互的命令行外壳。</p><p>现在，让我们看看如何启动和运行该服务器。在 Windows 上，首先需要在 C 驱动器中创建几个目录。在 C 驱动器中打开命令提示符，然后执行以下操作：</p><pre><code>C:\&gt; mkdir data/dbC:\&gt; cd dataC:\&gt; mkdir db</code></pre><p>这些目录的目的是 MongoDB 需要一个文件夹来存储所有数据。MongoDB 的默认数据目录路径是驱动器上的 <code>/data/db</code>。因此，有必要像这样提供这些目录。</p><p>如果启动不带这些目录的 MongoDB 服务器，则可能会看到以下错误：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/r04FRmRGqKUaclGh4ZDo3YsMwOlXMVm2T3bJ" class="kg-image" alt="r04FRmRGqKUaclGh4ZDo3YsMwOlXMVm2T3bJ" width="800" height="275" loading="lazy"><figcaption>尝试启动 mongodb 服务器，不含 \data\db 目录</figcaption></figure><p>创建完这两个文件后，再次转到 mongodb 目录中的 bin 文件夹，然后打开其中的 shell。运行以下命令：</p><pre><code>mongod</code></pre><p>瞧！ 现在，我们的 MongoDB 服务器已启动并正在运行！？</p><p>为了使用此服务器，我们需要一个中介器。在 bind 文件夹中打开另一个命令窗口，然后运行以下命令：</p><pre><code>mongo</code></pre><p>运行此命令后，导航到运行 mongod 命令的外壳程序（这是我们的服务器）。你会在最后看到“接受连接”消息。这意味着我们的安装和配置成功！</p><p>只需在 mongo shell 中运行即可：</p><pre><code>db</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/TK2JGg4JXAj0eG9JBzl89ABEF3JuKAwnw2dx" class="kg-image" alt="TK2JGg4JXAj0eG9JBzl89ABEF3JuKAwnw2dx" width="561" height="141" loading="lazy"><figcaption>最初你有一个 db 叫作 ‘test’</figcaption></figure><h4 id="--1"><strong>配置环境变量</strong></h4><p>为了节省时间，你可以设置环境变量。在 Windows 中，通过以下菜单完成：</p><pre><code>Advanced System Settings -&gt; Environment Variables -&gt; Path(Under System Variables) -&gt; Edit</code></pre><p>只需复制我们的 bin 文件夹的路径，然后运行它。在我的这个示例里，是这样的：</p><p> <code>C:\Program Files\MongoDB\Server\4.0\bin</code></p><p>现在你就完成设置了！</p><h3 id="-mongodb"><strong>使用 MongoDB</strong></h3><p>大量的 GUI 图形用户界面（例如 MongoDB Compass，Studio 3T 等）可和 MongoDB &nbsp;服务器一起使用。</p><p>它们提供了图形界面，因此你可以轻松地使用数据库并执行查询，而无需使用 Shell 并手动键入查询。</p><p>但是在本文中，我们将使用命令提示符来完成工作。</p><p>现在我们深入研究 MongoDB 命令，这些命令将帮助你在将来的项目中使用。</p><ul><li>打开命令提示符，然后键入 <code>mongod</code> 以启动 MongoDB 服务器。</li><li>打开另一个外壳，然后键入 <code>mongo</code> 以连接到 MongoDB 数据库服务器。</li></ul><h4 id="1-"><strong>1. 确定你目前在哪个数据库</strong></h4><pre><code>db</code></pre><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/o6puQoPSpGCW8-AgizHzAv3Qpywtzsgwd26N" class="kg-image" alt="o6puQoPSpGCW8-AgizHzAv3Qpywtzsgwd26N" width="561" height="126" loading="lazy"></figure><p>此命令将显示你当前所在的数据库。<code>test</code> 是默认情况下的初始数据库。</p><h4 id="2-"><strong>2. 罗列数据库</strong></h4><pre><code>show databases</code></pre><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/Q-G8NzP5OAXh0Y3OfdOtqFxlFG-tLErPlPSi" class="kg-image" alt="Q-G8NzP5OAXh0Y3OfdOtqFxlFG-tLErPlPSi" width="562" height="205" loading="lazy"></figure><p>我目前有四个数据库：<code>CrudDB</code>， <code>admin</code>， <code>config</code> 和 <code>local</code>。</p><h4 id="3-"><strong><strong><strong>3. 访问某个数据库</strong></strong></strong></h4><pre><code>use &lt;your_db_name&gt;</code></pre><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/UIRueBuX-r6nRXA-qd6Uv95IBd0UbhVvMZtZ" class="kg-image" alt="UIRueBuX-r6nRXA-qd6Uv95IBd0UbhVvMZtZ" width="219" height="100" loading="lazy"></figure><p>在这里，我已移至 <code>local</code> 数据库。你可以使用命令 <code>db</code> 打印出当前数据库的名称，进行检查。</p><h4 id="4-"><strong><strong><strong>4. 创建一个数据库</strong></strong></strong></h4><p>使用 RDBMS（关系数据库管理系统），我们可以获得数据库、表、行和列。</p><p>但是，在 NoSQL 数据库（例如 MongoDB）中，数据以 BSON 格式（JSON 的二进制版本）存储。它们存储在称为“集合”的结构中。</p><p>在 SQL 数据库中，这些类似于表。</p><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/e7ygVKXaPcqcqCyvurAeUzAbmmREoA6p72V2" class="kg-image" alt="e7ygVKXaPcqcqCyvurAeUzAbmmREoA6p72V2" width="650" height="450" loading="lazy"></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/oxeGaPqbZ2pmmZx3WcDo8CXIL4J09PbecBWW" class="kg-image" alt="oxeGaPqbZ2pmmZx3WcDo8CXIL4J09PbecBWW" width="702" height="362" loading="lazy"><figcaption>SQL terms and NoSQL terms by <a href="https://www.blogger.com/profile/18437865869379626284" rel="noopener" target="_blank" title="author profile" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 17.6px; vertical-align: baseline; background-color: transparent; color: var(--gray90); text-decoration: underline; cursor: pointer; word-break: break-word;">Victoria Malaya</a></figcaption></figure><p>接下来，我们谈谈如何在 mongo shell 中创建数据库。</p><pre><code>use &lt;your_db_name&gt;</code></pre><p>等一下，我们之前有这个命令！ 为什么我再次使用它？</p><p>在 MongoDB 服务器中，如果你的数据库已经存在，则使用该命令将导航到你的数据库。</p><p>但是，如果数据库尚不存在，则 MongoDB 服务器将为你创建数据库。然后，它将导航到其中。</p><p>创建新数据库后，运行 <code>show database</code> 命令将不会显示你新创建的数据库。这是因为，当数据库包含一些数据（文档）后，它才会显示在你的数据库列表中。</p><h4 id="5-"><strong><strong><strong>5. 创建一个集合</strong></strong></strong></h4><p>使用 <code>use</code> 命令导航到新创建的数据库。</p><p>实际上，有两种创建集合的方法。</p><p>一种方法是将数据插入到集合中：</p><pre><code>db.myCollection.insert({"name": "john", "age" : 22, "location": "colombo"})</code></pre><p>即使该集合不存在，这也将创建你的集合 <code>myCollection</code>。然后它将插入带有 <code>name</code> 和 <code>age</code> 的文档。这些是无上限的集合。</p><p>第二种方法如下所示：</p><p>2.1 创建一个无上限的集合</p><pre><code>db.createCollection("myCollection")</code></pre><p>2.2 创建一个有上限的集合</p><pre><code>db.createCollection("mySecondCollection", {capped : true, size : 2, max : 2})</code></pre><p>这样，你将无需插入数据即可创建集合。</p><p>“有上限的集合”规定了最大的文档数，可防止文档溢出。在此示例中，我通过将其值设置为 <code>true</code> 启用上限。</p><p><code>size : 2</code> 表示限制为 2MB，<code>max: 2</code> 将最大文档数设置为 2。</p><p>现在，如果你尝试在 <code>mySecondCollection</code> 中插入两个以上的文档，并使用 <code>find</code> &nbsp;命令（我们将在稍后讨论），你只会看到最近插入的文档。请记住，这并不意味着第一个文档已被删除——它只是未显示。</p><h4 id="6-"><strong><strong><strong>6. 插入数据</strong></strong></strong></h4><p>我们可以将数据插入到新集合或之前创建的集合中。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/uO4agHbI85kMJrQmF1L9pMmhn0WcgngmoPsI" class="kg-image" alt="uO4agHbI85kMJrQmF1L9pMmhn0WcgngmoPsI" width="605" height="698" loading="lazy"><figcaption>数据存储在 JSON 中</figcaption></figure><p>有三种插入数据的方法：</p><ul><li><code>insertOne()</code> 仅用于插入单个文档</li><li><code>insertMany()</code> 用于插入多个文档</li><li><code>insert()</code> 用于插入所需数量的文档</li></ul><p>下面是一些示例：</p><ul><li><strong><strong>insertOne()</strong></strong></li></ul><pre><code>db.myCollection.insertOne(
  {
    "name": "navindu", 
    "age": 22
  }
)</code></pre><ul><li><strong><strong>insertMany()</strong></strong></li></ul><pre><code>db.myCollection.insertMany([
  {
    "name": "navindu", 
    "age": 22
  },
  {
    "name": "kavindu", 
    "age": 20
  },

  {
    "name": "john doe", 
    "age": 25,
    "location": "colombo"
  }
])</code></pre><p><code>insert()</code> 方法类似于 <code>insertMany()</code> 方法。</p><p>另外，请注意，我们在文档中为 <code>John Doe</code> 插入了一个名为 <code>location</code> 的新属性。因此，如果你使用 <code>find</code>，则只会看到 <code>john doe</code> 具有 <code>location</code> 属性。</p><p>对于 NoSQL 数据库（例如 MongoDB），这可能是一个优势，有利于可扩展性。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/QyCgwWUHWoporNunUvoRgdVry-x0QyA8qSxd" class="kg-image" alt="QyCgwWUHWoporNunUvoRgdVry-x0QyA8qSxd" width="403" height="40" loading="lazy"><figcaption>成功插入数据</figcaption></figure><h4 id="7-"><strong><strong><strong>7. 查询数据</strong></strong></strong></h4><p>你可以通过以下方法查询集合中的所有数据：</p><pre><code>db.myCollection.find()</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/rzcViLqDrTy5gqSFoY6n3N7dciNxFTY62eRL" class="kg-image" alt="rzcViLqDrTy5gqSFoY6n3N7dciNxFTY62eRL" width="578" height="64" loading="lazy"><figcaption>结果</figcaption></figure><p>如果你想通过更清晰的方式查看此数据，只需在其末尾添加 <code>.pretty()</code> 即可。这将以漂亮的 JSON 格式显示文档。</p><pre><code>db.myCollection.find().pretty()</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/gMIbpNqjr9jmJ3YVDZruX1skX0PCvSruuZWB" class="kg-image" alt="gMIbpNqjr9jmJ3YVDZruX1skX0PCvSruuZWB" width="406" height="172" loading="lazy"><figcaption>结果</figcaption></figure><p>等等，在这些示例中，你是否注意到了类似 <code>_id</code> 的东西？</p><p>无论何时插入文档，MongoDB 都会自动添加一个 <code>_id</code> 字段。该字段唯一地标识每个文档。如果你不想显示它，只需运行以下命令：</p><pre><code>db.myCollection.find({}, _id: 0).pretty()</code></pre><p>接下来，我们看一下过滤数据。</p><p>如果要显示某些特定文档，则可以指定要显示的文档的单个详细信息。</p><pre><code>db.myCollection.find(
  {
    name: "john"
  }
)</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/TiBBNNp9gmxtPXaHd5BSZ7MkSrv1JkRzkMI1" class="kg-image" alt="TiBBNNp9gmxtPXaHd5BSZ7MkSrv1JkRzkMI1" width="710" height="44" loading="lazy"><figcaption>结果</figcaption></figure><p>假设你只想显示年龄在 25 岁以下的人。您可以使用 <code>$lt</code> 对此进行过滤。</p><pre><code>db.myCollection.find(
  {
    age : {$lt : 25}
  }
)</code></pre><p>同样，<code>$gt</code> 表示大于，<code>$lte</code> 表示“小于或等于”，<code>$gte</code> 表示“大于或等于”，<code>$ne</code> 是“不等于”。</p><h4 id="8-"><strong><strong><strong>8. 更新文档</strong></strong></strong></h4><p>假设你想更新某人的地址或年龄，该怎么做？ 请看下一个例子：</p><pre><code>db.myCollection.update({age : 20}, {$set: {age: 23}})</code></pre><p>第一个参数是要更新哪个文档的字段。在这里，我为简单起见指定了 <code>age</code>。在生产环境中，你可以使用 <code>_id</code> 字段。</p><p>最好使用 <code>_id</code> 之类的内容来更新特定的一行。 这是因为多个字段可以具有相同的年龄和名称。 因此，如果更新一行，它将影响具有相同名称和年龄的所有行。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/qQH53vM6-peOzS-z9k5YjMoS9R2z1APJrXvB" class="kg-image" alt="qQH53vM6-peOzS-z9k5YjMoS9R2z1APJrXvB" width="674" height="95" loading="lazy"><figcaption>结果</figcaption></figure><p>如果你使用新属性（例如 <code>location</code>）更新文档，则文档将使用新属性进行更新。如果你执行 <code>find</code>，那么结果是：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/YqJpPAw7d5NPSTzStCevUmgoDTm6FkgPLZ-7" class="kg-image" alt="YqJpPAw7d5NPSTzStCevUmgoDTm6FkgPLZ-7" width="516" height="76" loading="lazy"><figcaption>结果</figcaption></figure><p>如果你需要从单个文档中删除某个属性，则可以执行以下操作（假设你希望删除 <code>age</code>）：</p><pre><code>db.myCollection.update({name: "navindu"}, {$unset: age});</code></pre><h4 id="9-"><strong><strong><strong>9. 删除一个文档</strong></strong></strong></h4><p>如前所述，当你更新或删除文档时，你只需要指定 <code>_id</code> 即可，而不是 <code>name</code>，<code>age</code>，<code>location</code>。</p><pre><code>db.myCollection.remove({name: "navindu"});</code></pre><h4 id="10-"><strong><strong><strong>10. 删除一个集合</strong></strong></strong></h4><pre><code>db.myCollection.remove({});</code></pre><p>注意，这不等于 <code>drop()</code> 方法。不同之处在于 <code>drop()</code> 用于删除集合中的所有文档，而 <code>remove()</code> 方法用于删除集合本身以及所有文档。</p><h3 id="--2"><strong>逻辑运算符</strong></h3><p>MongoDB 提供逻辑运算符。下图总结了不同类型的逻辑运算符。</p><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/xO27jGeclafiAUt0a0VYRifhDpISvZcIkhRD" class="kg-image" alt="xO27jGeclafiAUt0a0VYRifhDpISvZcIkhRD" width="659" height="223" loading="lazy"></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/VsHbrchxUETWqCFhZc6QvmSPUdrbfOHYEH3L" class="kg-image" alt="VsHbrchxUETWqCFhZc6QvmSPUdrbfOHYEH3L" width="745" height="255" loading="lazy"><figcaption>参考：MongoDB 手册</figcaption></figure><p>假设你要显示年龄小于 25 岁且位置在科伦坡的人，怎么做呢？</p><p>我们可以使用 <code>$and</code> 运算符！</p><pre><code>db.myCollection.find({$and:[{age : {$lt : 25}}, {location: "colombo"}]});</code></pre><p>最后，我们来谈谈聚合。</p><h3 id="--3"><strong>聚合</strong></h3><p>快速看一下我们了解到的有关 SQL 数据库中聚合函数的知识：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/JHcuA7YLBiFiCBn1QiOS8NYCUELbGg-LKDSN" class="kg-image" alt="JHcuA7YLBiFiCBn1QiOS8NYCUELbGg-LKDSN" width="800" height="533" loading="lazy"><figcaption>SQL 数据库中的聚合函数，参考：Tutorial Gateway</figcaption></figure><p>简而言之，聚合对多个文档中的值进行分组并以某种方式进行汇总。</p><p>想象一下，如果我们在 <code>recordBook</code> 集合中有男女学生，而我们希望每个学生都有总数。为了获得男生和女生的总和，我们可以使用 <code>$group</code> 聚合函数。</p><pre><code>db.recordBook.aggregate([
  {
    $group : {_id : "$gender", result: {$sum: 1}}
  }  
]);</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/NeK7Wx3lQ1AaUhGD1VERqmaluAl9qrsXpDMs" class="kg-image" alt="NeK7Wx3lQ1AaUhGD1VERqmaluAl9qrsXpDMs" width="527" height="61" loading="lazy"><figcaption>结果</figcaption></figure><h4 id="--4"><strong>总结</strong></h4><p>我们讨论了 MongoDB 基础，将来构建应用程序时可能需要用到。希望你喜欢这篇文章。谢谢阅读！</p><p>如果你对此教程有任何疑问，请随时在下面的评论部分中进行评论，或者在 <a href="https://www.facebook.com/navinduuu">Facebook </a>或 <a href="https://twitter.com/NavinduJay">Twitter</a> 或 <a href="https://www.instagram.com/iamnavindu/">Instagram</a> 上与我联系。</p><p>下篇再见！ ❤️✌</p><p>原文：<a href="https://www.freecodecamp.org/news/learn-mongodb-a4ce205e7739/">How to get started with MongoDB in 10 minutes</a>，作者：Navindu Jayatilake</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ SQL 连接教程：交叉连接、全外连接、内连接、左连接和右连接 ]]>
                </title>
                <description>
                    <![CDATA[ SQL 连接让关系型数据库管理系统名副其实。 连接（join）允许我们将分离的数据表重新构造成驱动应用程序的关系（relationship）。 本文将逐一查看 SQL 中的各种连接及其使用方式。 这里是我们将要讨论的内容：  * 什么是连接？  * 设置数据库  * 交叉连接  * 准备示例数据（导演和电影）  * 左连接与右连接  * 全外连接  * 内连接  * 使用左连接过滤数据  * 多表连接  * 带额外条件的连接  * 连接查询的现实 （剧透警告：我们将介绍五种不同类型的连接，但实际上你只需要知道其中两种！） 什么是连接 连接  是一种将两行结合成一行的操作。 这些行通常来自不同的表，但这并不绝对。 在我们着眼于如何写连接之前，我们先看一看连接的结果长啥样。 我们就以一个保存用户信息和他们的地址信息的系统为例吧。 保存用户信息的表里面的行可能长这个样子：  ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/sql-joins-tutorial/</link>
                <guid isPermaLink="false">5ff27c9039641a0517d531dd</guid>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 数据库 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nicholas Zhan ]]>
                </dc:creator>
                <pubDate>Mon, 04 Jan 2021 02:53:01 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/01/1609728756536.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>SQL 连接让关系型数据库管理系统名副其实。</p>
<p>连接（join）允许我们将分离的数据表重新构造成驱动应用程序的关系（relationship）。</p>
<p>本文将逐一查看 SQL 中的各种连接及其使用方式。</p>
<p>这里是我们将要讨论的内容：</p>
<ul>
<li><a href="#%E4%BB%80%E4%B9%88%E6%98%AF%E8%BF%9E%E6%8E%A5">什么是连接？</a></li>
<li><a href="#%E8%AE%BE%E7%BD%AE%E6%95%B0%E6%8D%AE%E5%BA%93">设置数据库</a></li>
<li><a href="#%E4%BA%A4%E5%8F%89%E8%BF%9E%E6%8E%A5">交叉连接</a></li>
<li><a href="#%E5%87%86%E5%A4%87%E7%A4%BA%E4%BE%8B%E6%95%B0%E6%8D%AE%EF%BC%88%E5%AF%BC%E6%BC%94%E5%92%8C%E7%94%B5%E5%BD%B1%EF%BC%89">准备示例数据（导演和电影）</a></li>
<li><a href="#%E5%B7%A6%E8%BF%9E%E6%8E%A5%E4%B8%8E%E5%8F%B3%E8%BF%9E%E6%8E%A5">左连接与右连接</a></li>
<li><a href="#%E5%85%A8%E5%A4%96%E8%BF%9E%E6%8E%A5">全外连接</a></li>
<li><a href="#%E5%86%85%E8%BF%9E%E6%8E%A5">内连接</a></li>
<li><a href="#%E4%BD%BF%E7%94%A8%E5%B7%A6%E8%BF%9E%E6%8E%A5%E8%BF%87%E6%BB%A4%E6%95%B0%E6%8D%AE">使用左连接过滤数据</a></li>
<li><a href="#%E5%A4%9A%E8%A1%A8%E8%BF%9E%E6%8E%A5">多表连接</a></li>
<li><a href="#%E5%B8%A6%E9%A2%9D%E5%A4%96%E6%9D%A1%E4%BB%B6%E7%9A%84%E8%BF%9E%E6%8E%A5">带额外条件的连接</a></li>
<li><a href="#%E7%8E%B0%E5%AE%9E%E4%B8%AD%E7%9A%84%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A2">连接查询的现实</a></li>
</ul>
<p>（<em>剧透警告</em>：我们将介绍五种不同类型的连接，但实际上你只需要知道其中两种！）</p>
<h2 id="">什么是连接</h2>
<p><strong>连接</strong> 是一种将两行结合成一行的操作。</p>
<p>这些行通常来自不同的表，但这并不绝对。</p>
<p>在我们着眼于如何写连接之前，我们先看一看连接的结果长啥样。</p>
<p>我们就以一个保存用户信息和他们的地址信息的系统为例吧。</p>
<p>保存用户信息的表里面的行可能长这个样子：</p>
<pre><code> id |     name     |        email        | age
----+--------------+---------------------+-----
  1 | John Smith   | johnsmith@gmail.com |  25
  2 | Jane Doe     | janedoe@Gmail.com   |  28
  3 | Xavier Wills | xavier@wills.io     |  3
...
(7 rows)
</code></pre>
<p>保存地址信息的表里面的行可能是这个样子：</p>
<pre><code> id |      street       |     city      | state | user_id
----+-------------------+---------------+-------+---------
  1 | 1234 Main Street  | Oklahoma City | OK    |       1
  2 | 4444 Broadway Ave | Oklahoma City | OK    |       2
  3 | 5678 Party Ln     | Tulsa         | OK    |       3
(3 rows)
</code></pre>
<p>我们可以写多个独立的查询，既检索用户信息，又检索地址信息。但是在理想情况下，我们可以写 <em>一条查询</em>，然后用同一个结果集（result set）接收所有的用户和他们的地址信息。</p>
<p>这正是连接让我们做的事情！</p>
<p>我们很快就会看到如何写这些连接，但如果我们连接用户信息表和地址信息表，就可以得到一个像这样的结果：</p>
<pre><code> id |     name     |        email        | age | id |      street       |     city      | state | user_id
----+--------------+---------------------+-----+----+-------------------+---------------+-------+---------
  1 | John Smith   | johnsmith@gmail.com |  25 |  1 | 1234 Main Street  | Oklahoma City | OK    |       1
  2 | Jane Doe     | janedoe@Gmail.com   |  28 |  2 | 4444 Broadway Ave | Oklahoma City | OK    |       2
  3 | Xavier Wills | xavier@wills.io     |  35 |  3 | 5678 Party Ln     | Tulsa         | OK    |       3
(3 rows)

</code></pre>
<p>在这里，我们可以在一个不错的结果集中看到用户和他们的地址。</p>
<p>除了产生组合结果集，连接的另一个重要用途就是为查询语句提供额外的信息，便于对结果集进行过滤。</p>
<p>例如，如果我们想给住在 Oklahoma 市的所有用户发送实体邮件，我们就可以使用这个连接在一起的结果集，然后根据 <code>city</code> 列进行过滤。</p>
<p>既然我们已经知道了连接的用途，那就开始写一些吧！</p>
<h2 id="">设置数据库</h2>
<p>在写查询语句之前，我们需要先设置好数据库。</p>
<p>在这些示例中，我们将使用 PostgreSQL，但是这里展示的查询和概念可以很容易地被翻译到任何其它的现代数据库系统中（比如 MySQL、SQL Server，等等）。</p>
<p>我们可以使用 <a href="https://www.postgresql.org/docs/current/app-psql.html"><code>psql</code></a>与 PostgreSQL 数据库一起工作，它是 PostgreSQL 的交互式命令行程序。如果你有其它的数据库客户端，你也可以用它们。</p>
<p>首先，创建我们的数据库。[安装]好 PostgreSQL 之后，在终端运行 <code>createdb &lt;database-name&gt;</code>命令，创建一个新的数据库。我管我的数据库叫 <code>fcc</code>：</p>
<pre><code class="language-bash">$ createdb fcc
</code></pre>
<p>接下来使用命令 <code>psql</code> 启动一个交互式控制台，然后使用 <code>\c &lt;database-name&gt;</code> 连接到我们刚才创建的数据库：</p>
<pre><code class="language-bash">$ psql
psql (11.5)
Type "help" for help.

john=# \c fcc
You are now connected to database "fcc" as user "john".
fcc=#
</code></pre>
<blockquote>
<p><strong>注意：</strong> 为了便于阅读，我已经去掉了这些示例中 <code>psql</code> 的输出，所以如果你的终端显示的内容和这里的不完全相同，也不必担心。</p>
</blockquote>
<p>我鼓励你亲自动手跟着这些示例来一遍。如果你完成这些示例，你学到和记住的东西要比只阅读来的收获多得多。</p>
<p>现在看连接！</p>
<h2 id="">交叉连接</h2>
<p>交叉连接（<code>CROSS JOIN</code>）是我们能做的最简单的连接，它又叫 <em>笛卡尔乘积（Cartesian product）</em>。</p>
<p>这种连接将一张表中的每一行与另一张表中的每一行逐一进行连接。</p>
<p>如果我们有两个列表——一个包含 <code>1、2、3</code>，另一个包含 <code>A、B、C</code>。这两个列表的笛卡尔乘积就是这样：</p>
<pre><code>1A, 1B, 1C
2A, 2B, 2C
3A, 3B, 3C
</code></pre>
<p>第一个列表中的每一个值都与第二个列表中的每一个值进行了配对。</p>
<p>让我们把这个示例写成一条 SQL 查询。</p>
<p>我们先创建两个非常简单的表，再插入一些数据进去：</p>
<pre><code class="language-sql">CREATE TABLE letters(
  letter TEXT
);

INSERT INTO letters(letter) VALUES ('A'), ('B'), ('C');

CREATE TABLE numbers(
  number TEXT
);

INSERT INTO numbers(number) VALUES (1), (2), (3);
</code></pre>
<p>我们的两个表（<code>letters</code> 和 <code>numbers</code>）都只有一列：一个简单的文本字段。</p>
<p>现在使用 <code>CROSS JOIN</code> 将它们连接在一起：</p>
<pre><code class="language-sql">SELECT *
FROM letters
CROSS JOIN numbers;
</code></pre>
<pre><code> letter | number
--------+--------
 A      | 1
 A      | 2
 A      | 3
 B      | 1
 B      | 2
 B      | 3
 C      | 1
 C      | 2
 C      | 3
(9 rows)

</code></pre>
<p>这是我们能做的最简单的连接，但是即使是在这个简单的示例中，我们也能看见连接在起作用：分开的两行（一行来自 <code>letter</code>，一行来自 <code>numbers</code>）已经被 <em>连接</em> 成一行。</p>
<p>虽然这种连接通常只被当作学术范例进行讨论，但它至少有一个很好的用例：覆盖日期范围。</p>
<h3 id="">带日期范围的交叉连接</h3>
<p>交叉连接的一个好用例就是从表中读取每一行，然后将其用于某个日期范围内的每一天。</p>
<p>例如你正在构建一个追踪每日任务的应用，如刷牙、吃早饭或洗澡。</p>
<p>如果你想为上周 <em>每天</em> 和 <em>每个任务</em> 都生成一条记录，你可以在某个日期范围上使用 <code>CROSS JOIN</code>。</p>
<p>我们可以使用 <a href="https://www.postgresql.org/docs/current/functions-srf.html"><code>generate_series</code></a> 函数产生这个日期范围：</p>
<pre><code class="language-sql">SELECT generate_series(
  (CURRENT_DATE - INTERVAL '5 day'),
  CURRENT_DATE,
  INTERVAL '1 day'
)::DATE AS day;

</code></pre>
<p><code>generate_series</code> 函数接收三个参数。</p>
<p>第一个参数是起始值，我们在示例中使用的是 <code>CURRENT_DATE - INTERVAL '5 day'</code>，它返回当前日期减去五天（五天前）的值。</p>
<p>第二个参数是当前日期（<code>CURRENT_DATE</code>）。</p>
<p>第三个参数是阶距（step interval），也就是值每次的增量大小。因为这些是日常任务，所以我们使用一天做为间隔（<code>INTERVAL '1 day'</code>）。</p>
<p>把这三个参数放在一起，就会生成一个日期序列：从五天前开始，到今天结束，每次前进一天。</p>
<p>最后，我们通过使用 <code>::DATE</code> 将这些值的输出转为日期类型，去掉时间部分。我们还使用 <code>AS day</code> 为这一列设置了别名，让输出看起来更友好。</p>
<p>这个查询的输出就是过去五天加今天：</p>
<pre><code>    day
------------
 2020-08-19
 2020-08-20
 2020-08-21
 2020-08-22
 2020-08-23
 2020-08-24
(6 rows)
</code></pre>
<p>回到我们的每日任务示例，一起创建一个存放我们想要完成的任务的简单表吧，然后插入几个任务：</p>
<pre><code class="language-sql">CREATE TABLE tasks(
  name TEXT
);

INSERT INTO tasks(name) VALUES
('Brush teeth'),
('Eat breakfast'),
('Shower'),
('Get dressed');
</code></pre>
<p><code>task</code> 表只有一列（<code>name</code>），我们往这个表中插入了四个任务。</p>
<p>现在让我们把任务和生成日期的查询交叉连接在一起吧：</p>
<pre><code class="language-sql">SELECT
  tasks.name,
  dates.day
FROM tasks
CROSS JOIN
(
  SELECT generate_series(
    (CURRENT_DATE - INTERVAL '5 day'),
    CURRENT_DATE,
    INTERVAL '1 day'
  )::DATE    AS day
) AS dates

</code></pre>
<p>（因为我们生成日期的查询并不是一个真实的表，所以我们将它写成了子查询。）</p>
<p>这个查询返回了任务名和日期，结果集看起来像这样：</p>
<pre><code>     name      |    day
---------------+------------
 Brush teeth   | 2020-08-19
 Brush teeth   | 2020-08-20
 Brush teeth   | 2020-08-21
 Brush teeth   | 2020-08-22
 Brush teeth   | 2020-08-23
 Brush teeth   | 2020-08-24
 Eat breakfast | 2020-08-19
 Eat breakfast | 2020-08-20
 Eat breakfast | 2020-08-21
 Eat breakfast | 2020-08-22
 ...
 (24 rows)

</code></pre>
<p>不出所料，日期范围内的每一天中的每一个任务都对应着一行。</p>
<p><code>CROSS JOIN</code> 是我们能使用的最简单的连接。但是，为了查看接下来的几种类型的连接，我们需要设置更加真实的表。</p>
<h2 id="">准备示例数据（导演和电影）</h2>
<p>为了展示接下来的几种连接类型，我们需要使用 <em>电影</em> 和 <em>电影导演</em> 这个例子。</p>
<p>在这种情况下，一部电影有一位导演，但是这不是 <em>必需</em> 的——想像一部新电影被宣布，但是它的导演还未确定。</p>
<p>我们的演员表（<code>directors</code>）将会保存每位演员的姓名，电影表（<code>movie</code>）将会保存每部电影的名字以及一个指向导演的引用（如果有的话）。</p>
<p>咱们先创建那两个表并插入一些数据吧：</p>
<pre><code class="language-sql">CREATE TABLE directors(
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);

INSERT INTO directors(name) VALUES
('John Smith'),
('Jane Doe'),
('Xavier Wills'),
('Bev Scott'),
('Bree Jensen');

CREATE TABLE movies(
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  director_id INTEGER REFERENCES directors 
);

INSERT INTO movies(name, director_id) VALUES
('Movie 1', 1),
('Movie 2', 1),
('Movie 3', 2),
('Movie 4', NULL),
('Movie 5', NULL);
</code></pre>
<p>我们有五位导演和五部电影，其中三部电影是有导演的。ID 为 1 的导演有两部电影，ID 为 2 的导演有一部电影。</p>
<h2 id="">全外连接</h2>
<p>既然已经有了一些数据，我们就看看全外连接（<code>FULL OUTER JOIN</code>）吧。</p>
<p><code>FULL OUTER JOIN</code> 与 <code>CROSS JOIN</code> 有些类似，但又有两个关键的区别。</p>
<p>第一个区别是 <code>FULL OUTER JOIN</code> 需要有一个 <strong>连接条件（join condition）</strong>。</p>
<p>连接条件声明了两张表的行与行之间是如何关联的，以及将它们连接在一起的条件。</p>
<p>在我们的示例中，<code>movies</code> 表通过 <code>director_id</code> 列引用演员，这一列与 <code>directors</code> 表的 <code>id</code> 列相匹配。我们将把这两列用作连接条件。</p>
<p>这是我们用来连接两张表的 SQL：</p>
<pre><code class="language-sql">SELECT *
FROM movies
FULL OUTER JOIN directors
  ON directors.id = movies.director_id;
</code></pre>
<p>注意：我们将匹配电影和导演的连接连接声明为：<code>ON movie.director_id = directors.id</code>。</p>
<p>我们的结果集看起来就像一个奇怪的笛卡尔乘积：</p>
<pre><code>  id  |  name   | director_id |  id  |     name
------+---------+-------------+------+--------------
    1 | Movie 1 |           1 |    1 | John Smith
    2 | Movie 2 |           1 |    1 | John Smith
    3 | Movie 3 |           2 |    2 | Jane Doe
    4 | Movie 4 |        NULL | NULL | NULL
    5 | Movie 5 |        NULL | NULL | NULL
 NULL | NULL    |        NULL |    5 | Bree Jensen
 NULL | NULL    |        NULL |    4 | Bev Scott
 NULL | NULL    |        NULL |    3 | Xavier Wills
(8 rows)
</code></pre>
<p>我们首先看到的是那些有导演的电影对应的行，这时连接条件的值为真。</p>
<p>然而，我们还能在这些行后面看到 <em>每张表</em> 中剩余的行，只是未匹配上的另一张表的对应值为 <code>NULL</code>。</p>
<blockquote>
<p>**注意：**如果你不熟悉 <code>NULL</code> 值，可以从<a href="https://www.freecodecamp.org/news/sql-operators-tutorial/#dealing-with-missing-data-null-"> SQL 运算符教程</a>中查看我的解释。</p>
</blockquote>
<p>我们还看到了 <code>CROSS JOIN</code> 与 <code>FULL OUTER JOIN</code> 的另一个区别：对于每张表，<code>FULL OUTER JOIN</code> 只会返回一行，而 <code>CROSS JOIN</code> 会返回多行。</p>
<h2 id="">内连接</h2>
<p>下一种连接类型——内连接（<code>INNER JOIN</code>），是最常用的连接类型之一。</p>
<p>内连接 <strong>只返回连接条件为真的那些行</strong>。</p>
<p>在我们的示例中，<code>movies</code> 和 <code>directors</code> 表之间的内连接只会返回指派了导演的电影的记录。</p>
<p>语法与之前的基本一致：</p>
<pre><code class="language-sql">SELECT *
FROM movies
INNER JOIN directors
  ON directors.id = movies.director_id;
</code></pre>
<p>我们的结果展示了有导演的那三部电影：</p>
<pre><code> id |  name   | director_id | id |    name
----+---------+-------------+----+------------
  1 | Movie 1 |           1 |  1 | John Smith
  2 | Movie 2 |           1 |  1 | John Smith
  3 | Movie 3 |           2 |  2 | Jane Doe
(3 rows)
</code></pre>
<p>由于内连接只包含满足连接条件的那些行，所以 <em>连接两个表时，谁先谁后并不重要</em>。</p>
<p>如果我们反转查询中表的顺序，我们还是会得到相同的结果：</p>
<pre><code class="language-sql">SELECT *
FROM directors
INNER JOIN movies
  ON movies.director_id = directors.id;
</code></pre>
<pre><code> id |    name    | id |  name   | director_id
----+------------+----+---------+-------------
  1 | John Smith |  1 | Movie 1 |           1
  1 | John Smith |  2 | Movie 2 |           1
  2 | Jane Doe   |  3 | Movie 3 |           2
(3 rows)
</code></pre>
<p>在这个查询中，由于我们先列出了 <code>directors</code> 表并选取所有的列（<code>SELECT *</code>），所以我们会先看到 <code>directors</code> 表中的列，然后才是 <code>movies</code> 表中的列，但是得到的数据是一样的。</p>
<p>内连接的这个性质非常有用，但是它并不适用于所有的连接类型——比如接下来的这种类型。</p>
<h2 id="">左连接与右连接</h2>
<p>接下来的两种连接类型用了一个修饰符（<code>LEFT</code> 或 <code>RIGHT</code>），它会决定哪个表的数据被包含到结果集中。</p>
<blockquote>
<p><strong>注意：</strong> <code>LEFT JOIN</code> 和 <code>RIGHT JOIN</code> 也可以被称为 <code>LEFT OUTER JOIN</code> 和 <code>RIGHT OUTER JOIN</code>。</p>
</blockquote>
<p>这两种连接的使用场景是：我们想返回某个特定表中的所有数据，<em>如果关联表中存在相应的数据</em>，也将其返回。</p>
<p>如果关联的数据不存在，我们仍然可以得到“主表”的所有记录。</p>
<p>它是一个针对特定事物的信息和奖励信息（如果存在的话）的查询。</p>
<p>举个例子就好理解了。让我们找出所有的电影和它们的导演，但是我们不关心电影是否有导演——导演就是奖励：</p>
<pre><code class="language-sql">SELECT *
FROM movies
LEFT JOIN directors
  ON directors.id = movies.director_id;
</code></pre>
<p>这个查询的形式与之前一样——我们只是将连接声明为了 <code>LEFT JOIN</code>。</p>
<p>在这个示例中，<code>movies</code> 表就是“左表”。</p>
<p>如果我们将查询写成一行，看起来会更容易一些：</p>
<pre><code class="language-sql">... FROM movies LEFT JOIN directors ...
</code></pre>
<p><strong>左连接返回“左表”中的所有记录</strong>。</p>
<p>左连接会返回“右表”中 <strong>满足连接条件</strong> 的所有记录。</p>
<p>“右表”中 <strong>不满足连接条件的记录会以 <code>NULL</code> 值返回</strong>。</p>
<pre><code> id |  name   | director_id |  id  |    name
----+---------+-------------+------+------------
  1 | Movie 1 |           1 |    1 | John Smith
  2 | Movie 2 |           1 |    1 | John Smith
  3 | Movie 3 |           2 |    2 | Jane Doe
  4 | Movie 4 |        NULL | NULL | NULL
  5 | Movie 5 |        NULL | NULL | NULL
(5 rows)
</code></pre>
<p>看着这个结果集，我们就能明白为何这种连接在 <em>所有的这个和部分的那个（如果存在的话）</em> 类型的连接中特别有用了。</p>
<h3 id="">右连接</h3>
<p>除了调换了关于两表的要求外，右连接（<code>RIGHT JOIN</code>）的工作原理与 <code>LEFT JOIN</code> 一模一样。</p>
<p>在右连接中，“右表”中的所有行全部返回，而“左表”根据连接条件返回。</p>
<p>我们还是使用上面的那个查询，只不过这一次要将 <code>LEFT JOIN</code> 替换成 <code>RIGHT JOIN</code>：</p>
<pre><code class="language-sql">SELECT *
FROM movies
RIGHT JOIN directors
  ON directors.id = movies.director_id;
</code></pre>
<pre><code>  id  |  name   | director_id | id |     name
------+---------+-------------+----+--------------
    1 | Movie 1 |           1 |  1 | John Smith
    2 | Movie 2 |           1 |  1 | John Smith
    3 | Movie 3 |           2 |  2 | Jane Doe
 NULL | NULL    |        NULL |  5 | Bree Jensen
 NULL | NULL    |        NULL |  4 | Bev Scott
 NULL | NULL    |        NULL |  3 | Xavier Wills
(6 rows)

</code></pre>
<p>我们现在的结果集返回了 <code>directors</code> 表的所有行和 <code>movies</code> 数据（如果存在的话）。</p>
<p>我们所做的就是切换“主表”，“主表”就是不管数据存在与否，我们都想查看到所有数据的那个表。</p>
<h3 id="">生产应用程序中的左连接与右连接</h3>
<p>在生产应用程序中，我只用过 <code>LEFT JOIN</code>，从未用过 <code>RIGHT JOIN</code>。</p>
<p>我这么做的原因是：我认为 <code>LEFT JOIN</code> 让查询更易于阅读和理解。</p>
<p>当我写查询语句时，我喜欢从“基础”结果集开始思考，比如所有的电影。然后在它的基础上引入（或去除）一些东西。</p>
<p>因为我喜欢从基础开始，而 <code>LEFT JOIN</code> 恰好满足了这种思路。我想要的是基础表（“左表”）中的所有行和“右表”中满足条件的行。</p>
<p>在实践中，我觉得我没有在生产应用中见过 <code>RIGHT JOIN</code>。<code>RIGHT JOIN</code> 并没有错，我只是认为它会让查询更难理解。</p>
<h3 id="">重写右连接</h3>
<p>如果我们想翻转上面的场景，返回所有的导演和满足条件的电影，我们可以将 <code>RIGHT JOIN</code> 重写为 <code>LEFT JOIN</code>。</p>
<p>我们只需要翻转两个表在查询语句中的顺序，再把 <code>RIGHT</code> 改成 <code>LEFT</code> 即可：</p>
<pre><code class="language-sql">SELECT *
FROM directors
LEFT JOIN movies
  ON movies.director_id = directors.id;
</code></pre>
<blockquote>
<p><strong>注意：</strong> 我喜欢将被连接的表（“右表”，即上面示例中的 <code>movies</code>）写在连接条件中的第一个（(<code>ON movies.director_id = ...</code>），不过这只是我的个人偏好。</p>
</blockquote>
<h2 id="">使用左连接过滤数据</h2>
<p><code>LEFT JOIN</code>（或 <code>RIGHT JOIN</code>）有两个用例。</p>
<p>第一个用例我们已经讲了：返回一个表中的所有数据，以及另一个表中满足条件的数据。</p>
<p>第二个用例是 <strong>在第二个表中的数据不存在的情况下</strong>，返回第一个表中的所有行。</p>
<p>这个场景看起来像这样：找出 <em>不属于任何电影</em> 的导演。</p>
<p>为此，我们从 <code>LEFT JOIN</code> 开始，将 <code>directors</code> 作为主表或“左表”：</p>
<pre><code class="language-sql">SELECT *
FROM directors
LEFT JOIN movies
  ON movies.director_id = directors.id;
</code></pre>
<p>对于那些不属于任何电影的导演来说，来自 <code>movies</code> 表的列都是 <code>NULL</code>：</p>
<pre><code> id |     name     |  id  |  name   | director_id
----+--------------+------+---------+-------------
  1 | John Smith   |    1 | Movie 1 |           1
  1 | John Smith   |    2 | Movie 2 |           1
  2 | Jane Doe     |    3 | Movie 3 |           2
  5 | Bree Jensen  | NULL | NULL    |        NULL
  4 | Bev Scott    | NULL | NULL    |        NULL
  3 | Xavier Wills | NULL | NULL    |        NULL
(6 rows)
</code></pre>
<p>在我们的示例中，ID 为 3、4 和 5 的导演不属于任何一部电影。</p>
<p>要从结果集中过滤出这些行，我们可以加一个 <code>WHERE</code> 子句，只返回电影数据为 <code>NULL</code> 的行：</p>
<pre><code class="language-sql">SELECT *
FROM directors
LEFT JOIN movies
  ON movies.director_id = directors.id
WHERE movies.id IS NULL;
</code></pre>
<pre><code> id |     name     |  id  | name | director_id
----+--------------+------+------+-------------
  5 | Bree Jensen  | NULL | NULL |        NULL
  4 | Bev Scott    | NULL | NULL |        NULL
  3 | Xavier Wills | NULL | NULL |        NULL
(3 rows)
</code></pre>
<p>有三部电影没有导演！</p>
<p>使用表的 <code>id</code> 列进行过滤（<code>WHERE movies.id IS NULL</code>）是很普遍的。但是，因为 <code>movies</code> 表的所有列都是 <code>NULL</code>，所以使用其中任何一个都能达到目的。</p>
<p>（由于我们知道 <code>movies</code> 表中的所有列都会是 <code>NULL</code>，所以在上面的那个查询中，我们可以将 <code>SELECT *</code> 替换成 <code>SELECT directors.*</code>，从而只返回所有的导演信息）。</p>
<h3 id="">使用左连接查找匹配</h3>
<p>在我们之前的查询中，我们找到了那些 <em>不</em> 属于任何电影的导演。</p>
<p>使用同样的结构，我们可以找到那些 <em>肯定</em> 属于电影的导演。只需要将 <code>WHERE</code> 条件改成寻找电影数据 <em>不是</em> <code>NULL</code> 的电影数据即可：</p>
<pre><code class="language-sql">SELECT *
FROM directors
LEFT JOIN movies
  ON movies.director_id = directors.id
WHERE movies.id IS NOT NULL;
</code></pre>
<pre><code> id |    name    | id |  name   | director_id
----+------------+----+---------+-------------
  1 | John Smith |  1 | Movie 1 |           1
  1 | John Smith |  2 | Movie 2 |           1
  2 | Jane Doe   |  3 | Movie 3 |           2
(3 rows)
</code></pre>
<p>这可能看起来很方便，但我们实际上只是在重新实现 <code>INNER JOIN</code> 而已。</p>
<h2 id="">多表连接</h2>
<p>我们已经看到如何将两张表连接在一起了，但对于多表连接会怎么样呢？</p>
<p>实际上很简单，但是我们需要第三个表（<code>tickets</code>）来进行举例：</p>
<p>这个表将代表已经售出的电影票：</p>
<pre><code class="language-sql">CREATE TABLE tickets(
  id SERIAL PRIMARY KEY,
  movie_id INTEGER REFERENCES movies NOT NULL
);

INSERT INTO tickets(movie_id) VALUES (1), (1), (3);
</code></pre>
<p><code>tickets</code> 表只有一个 <code>id</code> 和一个电影的引用：<code>movie_id</code>。</p>
<p>我们也为 ID 为 1 的电影插入了两张票，为 ID 为 3 的电影插入了一张票。</p>
<p>现在，让我们连接 <code>directors</code> 和 <code>movies</code>，然后再连接 <code>movies</code> 和 <code>tickets</code>！</p>
<pre><code class="language-sql">SELECT *
FROM directors
INNER JOIN movies
  ON movies.director_id = directors.id
INNER JOIN tickets
  ON tickets.movie_id = movies.id;
</code></pre>
<p>因为这些是内连接，所以连接的顺序并不重要。我们可以从 <code>tickets</code> 开始，连接 <code>movies</code>，再连接 <code>directors</code>。</p>
<p>它的顺序也取决于你想要查询的什么以及什么让查询最易于理解。</p>
<p>在结果集中，我们将会注意到返回行的范围已经被进一步缩小了：</p>
<pre><code> id |    name    | id |  name   | director_id | id | movie_id
----+------------+----+---------+-------------+----+----------
  1 | John Smith |  1 | Movie 1 |           1 |  1 |        1
  1 | John Smith |  1 | Movie 1 |           1 |  2 |        1
  2 | Jane Doe   |  3 | Movie 3 |           2 |  3 |        3
(3 rows)
</code></pre>
<p>这是因为我们加了另一个 <code>INNER JOIN</code>，实际上它给我们的查询添加了另一个 <code>AND</code> 条件。</p>
<p>我们的查询本质上是在说：<em>“返回所有属于电影的导演，这些电影 <strong>也有</strong> 电影票售出”</em>。</p>
<p>如果我们想找出属于 <em>可能还没有电影票售出</em> 的电影的导演，我们可以将最后一个 <code>INNER JOIN</code> 替换成 <code>LEFT JOIN</code>：</p>
<pre><code class="language-sql">SELECT *
FROM directors
JOIN movies
  ON movies.director_id = directors.id
LEFT JOIN tickets
  ON tickets.movie_id = movies.id;
</code></pre>
<p>我们可以看到 <code>Movie 2</code> 现在又回到结果集中来了：</p>
<pre><code> id |    name    | id |  name   | director_id |  id  | movie_id
----+------------+----+---------+-------------+------+----------
  1 | John Smith |  1 | Movie 1 |           1 |    1 |        1
  1 | John Smith |  1 | Movie 1 |           1 |    2 |        1
  2 | Jane Doe   |  3 | Movie 3 |           2 |    3 |        3
  1 | John Smith |  2 | Movie 2 |           1 | NULL |     NULL
(4 rows)

</code></pre>
<p>这部电影一张票也没有卖出去，所以它因 <code>INNER JOIN</code> 被之前的结果集中排除掉了。</p>
<p>我将 <em>给读者留一个练习</em>，你如何能找出那些属于一张票也 <strong>没有</strong> 卖出去的电影的导演呢？</p>
<h3 id="">连接的执行顺序</h3>
<p>最后，我们并不关心连接是以何种顺序执行的。</p>
<p>SQL 和其它现代编程语言之间的一个关键区别就是 SQL 是一门 <strong>声明式</strong> 语言。</p>
<p>这意味着我们声明期望的结果，但并不声明执行细节——这些细节留给了给数据库查询规划器（database query planner）。我们只管声明我们想要的连接和条件，查询规划器会处理剩余部分。</p>
<p>但是，现实中的数据库并不会同时连接三张表。相反，它会先连接前两张表得到一个中间结果，然后把这个中间结果集与第三张表连接。</p>
<p>（<strong>注意：</strong> 这个解释有点简单。）</p>
<p>因此，当我们在查询中遇到多表连接时，我们可以将它们看成一连串的两表连接——尽管其中的一个表会变得非常大。</p>
<h2 id="">带额外条件的连接</h2>
<p>我们将要介绍的最后一个主题是带额外条件的连接。</p>
<p>和 <code>WHERE</code> 字句类似，我们可以添加任意多的条件到连接中。</p>
<p>例如，如果我们想找出电影和导演的信息，但导演的 <em>名字</em> 不能是 <em>“John Smith”</em>。我们可以用 <code>AND</code> 添加一个额外的条件到我们的连接中：</p>
<pre><code class="language-sql">SELECT *
FROM movies
INNER JOIN directors
  ON directors.id = movies.director_id
  AND directors.name &lt;&gt; 'John Smith';
</code></pre>
<p>我们可以在这个连接条件中使用任何放在 <code>WHERE</code> 子句中的运算符。</p>
<p>如果我们将条件放到 <code>WHERE</code> 子句中，也能得到同样的结果：</p>
<pre><code class="language-sql">SELECT *
FROM movies
INNER JOIN directors
  ON directors.id = movies.director_id
WHERE directors.name &lt;&gt; 'John Smith';
</code></pre>
<p>这里背后发生的事情有一些细微的差异，但是就本文而言，结果集是一样的。</p>
<p>（如果你不太熟悉过滤 SQL 查询的所有方式，你可以在<a href="https://www.freecodecamp.org/news/sql-operators-tutorial/">这里</a>查看之前提到的文章。）</p>
<h2 id="">现实中的连接查询</h2>
<p>事实上，我发现我自己只通过三种方式使用连接：</p>
<h4 id="">内连接</h4>
<p>第一个用例是两表之间的关系 <strong>肯定</strong> 存在的记录，通过 <code>INNER JOIN</code> 实现。</p>
<p>条件如查找 “<em>具有导演的电影</em>” 或 “<em>具有帖子的用户</em>”。</p>
<h4 id="">左连接</h4>
<p>第二个用例是来自一个表的记录，以及关系存在时来自第二个表的记录，通过 <code>LEFT JOIN</code> 实现。</p>
<p>条件如 <em>“具有导演的电影（如果有的话）”</em> 或 “<em>具有文章的用户（如果有的话）</em>”。</p>
<h4 id="">左连接排除数据</h4>
<p>第三个常见的用例是 <code>LEFT JOIN</code> 的第二个用例：查找一个表中的记录，但这些记录在另一个表中 <strong>没有</strong> 关系。</p>
<p>条件如 “<em>没有导演的电影</em>” 或 “<em>没有文章的用户</em>”。</p>
<h3 id="">两种非常有用的连接类型</h3>
<p>我认为我不曾在生产应用中用过 <code>FULL OUTER JOIN</code> 或 <code>RIGHT JOIN</code>，相关用例要么很少，要么查询可以被写成更加清晰的形式（<code>RIGHT JOIN</code> 的情况）。</p>
<p>我偶尔会为一些东西使用 <code>CROSS JOIN</code>，比如分散在某个时间范围内的记录（像我们一开始看到的那样），但是那种场景也不多。</p>
<p>所以，好消息来了！对于你将遇到的 99.9% 的用例，只需要理解 <code>INNER JOIN</code> 和 <code>LEFT JOIN</code> 这两种类型的连接就可以解决了。</p>
<p>如果你喜欢这篇文章，可以在 <a href="https://twitter.com/johnmosesman">twitter</a> 上关注我，我在那里谈论数据库和所有其它开发相关的话题。</p>
<p>感谢阅读！</p>
<p>John</p>
<p><strong>P.S.</strong> 文末小技巧：大多数数据库系统允许你用 <code>JOIN</code> 代替 <code>INNER JOIN</code>——可以让你少打几个字。:)</p>
<!--kg-card-end: markdown--><p>原文：<a href="https://www.freecodecamp.org/news/sql-joins-tutorial/">SQL Joins Tutorial: Cross Join, Full Outer Join, Inner Join, Left Join, and Right Join</a>，作者：<a href="https://www.freecodecamp.org/news/author/johnmosesman/">John Mosesman</a></p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
