使用联接合并表格
如何使用联接合并两个表的信息。
使用 Metabase 学习 SQL
您有一个表中的数据,想要将其与另一个表中的数据合并。为此,您需要使用 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 编辑器中,您可以通过点击书本图标打开数据参考侧边栏,查看数据库中的表和字段信息。学习如何联接表的大部分工作是了解您正在使用的表以及它们之间如何关联。
我们需要从这两个表中获取列来生成目标结果,我们将使用联接来实现。
查询
这是预先给出的答案
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
表中的行可以通过将 Orders
表中的外键与 Products
表的实体键(也称为主键)“对齐”来与 Products
表中的行合并。我们还为每个表起了别名(orders AS o
和 products AS p
),因此您可以在 SELECT
语句中看出每个字段来自哪个表(例如 o.created_at
来自 orders
表,依此类推)。
实体键是指表中包含每行唯一标识符的列。在 Orders
表中,每行都是一个带有 ID 的订单。对于 Products
表中的产品也是如此:每行都是一个带有 ID 的产品。外键是指引用另一个表中实体键的列。在本例中,Orders
表包含一个 product_id
,它引用 Products
表中的特定行。如果我们在数据参考侧边栏中查看 Products
表,我们会看到它有两个连接,一个连接到 Orders
表,另一个连接到 reviews
表。
我们可以通过将 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_id
与 Products
表中的 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
是一个解析为真或假的表达式;数据库将只返回表达式为真的行。
联接多个表
您可以通过列出每个联接类型及其条件来链接多个联接。这是一个包含 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 | labadie.lina@gmail.com | 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
语句中包含多个真/假表达式来限制结果。这些真/假表达式被称为谓词。我们已经在上面的表联接中使用了谓词,例如 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 联接类型所需的一切。