我可以通过一个简单的例子来说明使用窗口函数ROW_NUMBER()结合LEFT JOIN来只映射(mapping)第一个匹配项的用法。

假设我们有两个表:orders和customers。我们想要连接这两个表,但只想要每个订单对应的第一个客户(如果有多个客户与同一订单相关联的话)。

使用窗口函数的情况:

  1. 表结构示例
  • orders表:

order_id

product

1

Apple

2

Banana

3

Cherry

  • customers表:

customer_id

order_id

name

100

1

Alice

101

1

Bob

102

2

Carol

  1. 目标:对于每个订单,我们只想连接第一个客户。例如,对于订单1,虽然有Alice和Bob两个客户,但我们只想选择Alice(基于某种排序标准,例如customer_id)。
  2. SQL查询:我们首先使用ROW_NUMBER()窗口函数为customers表中的每一行生成一个序号,按order_id分区,按customer_id排序。然后,我们将此结果与orders表进行LEFT JOIN,但只选择每个订单的第一个客户(即序号为1的行)。
WITH RankedCustomers AS (
    SELECT *,
           ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY customer_id) AS rn
    FROM customers
)
SELECT o.order_id, o.product, rc.name
FROM orders o
LEFT JOIN RankedCustomers rc ON o.order_id = rc.order_id AND rc.rn = 1;
  1. 结果:这个查询会返回每个订单和对应的第一个客户的名字。如果某个订单没有对应的客户,则该订单的客户名字为NULL。

order_id

product

name

1

Apple

Alice

2

Banana

Carol

3

Cherry

NULL

这个方法主要用于在进行LEFT JOIN时控制每个分组只映射一个匹配项,非常适用于处理一对多关系时只需获取一条记录的场景。

如果你不使用窗口函数,而是直接进行LEFT JOIN,结果将会不同,尤其是在处理一对多关系时。在你的例子中,如果直接使用LEFT JOIN连接orders表和customers表,结果将包括每个订单与每个相应客户的所有匹配项。

不使用窗口函数的情况:

假设你执行以下查询:

SELECT o.order_id, o.product, c.name
FROM orders o
LEFT JOIN customers c ON o.order_id = c.order_id;

这将产生以下结果:

order_id

product

name

1

Apple

Alice

1

Apple

Bob

2

Banana

Carol

3

Cherry

NULL

分析:

  • 对于order_id1,有两个匹配的客户:Alice 和 Bob。因此,这个订单会出现两次,每次与不同的客户。
  • 对于order_id2,只有一个匹配的客户:Carol。
  • 对于order_id3,没有匹配的客户,所以客户名为 NULL。

结果对比:

  • 使用窗口函数:你只得到每个订单的第一个客户,总共3条记录。
  • 不使用窗口函数:你得到所有匹配的订单和客户组合,总共4条记录。