使用连接组合表

如何使用连接组合两个表中的信息。

您在一个表中拥有数据,并且想要将其与另一个表中的数据组合。为此,您需要使用 JOIN 来告知数据库一个表中的行与另一个表中的行如何关联。(请注意,连接仅适用于关系数据库。)

有关连接的背景知识,请查看Metabase 中的连接,其中介绍了如何使用查询构建器创建连接——无需代码。请注意,“连接”一词在某种程度上具有误导性,因为您并没有连接表。您正在从两个(或多个)不同的表中获取行,并返回一个新行集,该行集组合了两个表中的列。

本文介绍了如何使用 SQL 创建连接,您可以在 Metabase 中按照步骤操作:单击 + 新建,选择 SQL 查询,然后选择 原始数据 > 示例数据库

使用示例数据库的连接示例

假设我们要提出一个问题,该问题返回包含来自多个表的列的行。例如,我们想知道订单日期、ID 和总计,但也要包含有关订购产品的某些信息。以下是我们想要生成的表

| Order date | Order ID | Product Title       | Product Category | Product Rating | Order total |
|------------|----------|---------------------|------------------|----------------|-------------|
| 12/25/2016 | 448      | Rustic Paper Wallet | Gizmo            | 4.6            | 30.86326113 |
| ...        | ...      | ...                 | ...              | ...            | ...         |

现在让我们看一下示例数据库中的表。我们会发现我们可以从两个表中获取必要的列,即 orders

| ID  | Person | Product ID | Subtotal    | Tax  | Total       | Discount | Created At | Quantity | Person       | Product ID             |
|-----|--------|------------|-------------|------|-------------|----------|------------|----------|--------------|------------------------|
| 1   | 1      | 14         | 37.64814539 | 2.07 | 39.71814539 |          | 2/11/2019  | 2        | Hudson Borer | Awesome Concrete Shoes |
| ... | ...    | ...        | ...         | ...  | ...         | ...      | ...        | ...      | ...          | ...                    |

以及 Products

| ID  | Ean           | Title               | Category | Vendor                       | Price       | Rating | Created At |
|-----|---------------|---------------------|----------|------------------------------|-------------|--------|------------|
| 1   | 1018947080336 | Rustic Paper Wallet | Gizmo    | Swaniawski, Casper and Hilll | 29.46326113 | 4.6    | 7/19/2017  |
| ... | ...           | ...                 | ...      | ...                          | ...         | ...    | ...        |

请注意,在 SQL 编辑器 中,您可以通过单击书本图标打开数据参考侧边栏来查看有关数据库中表和字段的信息。学习如何连接表的大部分工作是了解您正在使用的表以及它们彼此之间的关系。

The Data Reference sidebar in the SQL editor shows info about tables and fields in your database.

我们需要从这两个表中抓取列以生成我们的目标结果,我们将使用连接来做到这一点。

查询

这是预先给出的答案

SELECT
  o.created_at AS "order date",
  o.id AS "order id",
  p.title AS "product title",
  p.category AS "product category",
  p.rating AS "product rating",
  o.total AS "order total"
FROM
-- joining orders to products
  orders AS o
  JOIN products AS p ON o.product_id = p.id

请注意,注释(以 -- 开头的行)不是必需的。它只是为了指出代码的关键部分:连接语句。

此查询告诉数据库,Orders 表中的行可以与 Products 表中的行组合,方法是将 Orders 表中的外键与 Products 表的实体键(也称为主键)“对齐”。我们还为每个表起了别名(orders AS oproducts AS p),因此您可以在 SELECT 语句中分辨出每个字段来自哪个表(o.created_at 来自 orders 表,依此类推)。

实体键是指表中包含每行唯一标识符的列。在 Orders 表中,每一行都是一个带有 ID 的订单。Products 表中的产品也是如此:每一行都是一个带有 ID 的产品。外键是指引用另一个表中的实体键的列。在本例中,Orders 表包含一个 product_id,它引用 Products 表中的特定行。如果我们检查数据参考侧边栏中的 Products 表,我们会看到它有两个连接,一个连接到 Orders 表,另一个连接到 reviews 表。

The Products table has connections to the Orders and Reviews tables.

我们可以通过将 Products 表中的每一行与 Orders 表中的每一行组合,然后使用 WHERE 子句进行过滤来获得相同的结果,如下所示

SELECT
  o.created_at as "order date",
  o.id as "order id",
  p.title as "product title",
  p.category as "product category",
  p.rating as "product rating",
  o.total as "order total"

-- a join using a WHERE clause to "line up" the entity and foreign keys
FROM
  orders AS o,
  products AS p
WHERE
  o.product_id = p.id

此查询表示:将 Orders 表中的每一行与 Products 表中的每一行组合,然后过滤结果以仅包含 Orders 表中的 product_idProducts 表中的 id 匹配的行。我们建议您使用第一种形式(使用 ON)以避免混淆:ON 始终引入连接的条件,而 WHERE 用于各种类型的过滤。有关更多信息,请查看编写 SQL 查询的最佳实践

让我们详细了解一下连接语句

FROM
-- joining orders to products
  orders AS o
  JOIN products AS p ON o.product_id = p.id

当您连接表时,您使用 ON 关键字来指定表 A (Orders) 中的哪个字段对应于表 B (Products),以便数据库了解如何“对齐”数据。o.product_id = p.id 是一个解析为 true 或 false 的表达式;数据库将仅返回表达式为 true 的行。

连接多个表

您可以通过列出每种连接类型及其条件来链接多个连接。这是一个包含来自 people 表字段的连接

SELECT
  *
FROM
  orders AS o
  JOIN products AS p ON o.product_id = p.id
  JOIN people AS u ON o.user_id = u.id

您需要在此处进行大量水平滚动,因为此查询将产生许多列(实际上是每个表的所有列)。并且您会看到行与行之间重复的信息。例如,结果将为客户下的每个订单重复客户地址。

| ID  | USER_ID | PRODUCT_ID | SUBTOTAL    | TAX | TOTAL       | DISCOUNT | CREATED_AT       | QUANTITY | ID  | EAN           | TITLE               | CATEGORY | VENDOR                       | PRICE       | RATING | CREATED_AT      | ID  | ADDRESS                 | EMAIL                  | PASSWORD                             | NAME         | CITY    | LONGITUDE  | STATE | SOURCE | BIRTH_DATE | ZIP   | LATITUDE | CREATED_AT    |
|-----|---------|------------|-------------|-----|-------------|----------|------------------|----------|-----|---------------|---------------------|----------|------------------------------|-------------|--------|-----------------|-----|-------------------------|------------------------|--------------------------------------|--------------|---------|------------|-------|--------|------------|-------|----------|---------------|
| 448 | 61      | 1          | 29.46326113 | 1.4 | 30.86326113 |          | 12/25/2016 22:19 | 2        | 1   | 1018947080336 | Rustic Paper Wallet | Gizmo    | Swaniawski, Casper and Hilll | 29.46326113 | 4.6    | 7/19/2017 19:44 | 61  | 7100 Hudson Chapel Road | [email protected] | 2da78e08-2bf7-41b8-a737-1acd815fb99c | Lina Labadie | Catawba | -81.017265 | NC    | Google | 3/28/1984  | 28609 | 35.69917 | 6/5/2016 4:03 |
| ... | ...     | ...        | ...         | ... | ...         | ...      | ...              | ...      | ... | ...           | ...                 | ...      | ...                          | ...         | ...    | ...             | ... | ...                     | ...                    | ...                                  | ...          | ...     | ...        | ...   | ...    | ...        | ...   | ...      | ...           |

在上面的查询中,您可以通过替换 SELECT 语句中的 * 来指定您只需要的列,从而减少返回的列数。

多条件连接

您可以在 ON 语句中包含多个 true/false 表达式以限制结果。这些 true/false 表达式称为谓词。我们已经一直在使用谓词来连接上面的表,例如 o.user_id = u.id

假设我们想知道

  • 订单的平均价格,
  • 按产品类别,
  • 以全价出售的。

我们需要使用来自 Orders 表的数据计算单价,并从 Products 表中获取产品类别和标价。这是我们的查询

SELECT
  p.category AS "Product category",
  AVG(p.price) AS "Average price",
  COUNT(*) AS "Count of full price orders"
FROM
  orders AS o
  -- first predicate
  JOIN products AS p ON o.product_id = p.id
  -- second predicate: calculate the unit price
  -- and see if it corresponds to the product's listed price.
  AND o.subtotal / o.quantity = p.price
WHERE
  -- guard against divide-by-zero scenarios
  o.quantity > 0
GROUP BY
  p.category
ORDER BY
  COUNT(*) DESC

这给了我们

|Product category|Average price|Count of full price orders|
|----------------|-------------|--------------------------|
|Widget          |54.96699655  |168                       |
|Gizmo           |51.49700878  |137                       |
|Gadget          |54.87034242  |136                       |
|Doohickey       |51.69197973  |123                       |

不同类型的连接

我们在本文中一直使用的特定连接类型称为内连接。还有其他连接表的方法;请参阅我们关于 SQL 连接类型的文章。

下一步:SQL 连接类型

了解您需要了解的关于使用不同 SQL 连接类型的所有信息。

下一篇文章