跳转至

第5章 左半开连接

Hive 里的“左半开连接”通常指 LEFT SEMI JOIN,也叫 左半连接。它的作用是:

只返回左表中“在右表存在匹配”的记录,并且只输出左表字段;每个左表记录最多出现一次。

可以理解为 Hive 中实现 IN / EXISTS 语义的一种高效写法。

语法

SELECT t1.*
FROM table1 t1
LEFT SEMI JOIN table2 t2
ON t1.id = t2.id;

示例

找“下过订单的客户”:

SELECT c.*
FROM customers c
LEFT SEMI JOIN orders o
ON c.cust_id = o.cust_id;

含义:只要某个客户在 orders 中有至少一条订单,就返回这个客户。
即使该客户有 100 条订单,客户信息也只返回一次。

大致等价于:

SELECT c.*
FROM customers c
WHERE EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.cust_id = c.cust_id
);

或:

SELECT c.*
FROM customers c
WHERE c.cust_id IN (
    SELECT o.cust_id
    FROM orders o
);

和普通 JOIN 的区别

普通 INNER JOIN:

SELECT c.*, o.order_id
FROM customers c
JOIN orders o
ON c.cust_id = o.cust_id;

如果客户有多个订单,客户信息会重复出现,而且可以查询右表字段。

LEFT SEMI JOIN:

SELECT c.*
FROM customers c
LEFT SEMI JOIN orders o
ON c.cust_id = o.cust_id;

特点:

  • 只返回左表字段;
  • 右表只用来判断“是否存在匹配”;
  • 左表记录不会因为右表多条匹配而重复;
  • 不能 SELECT o.* 或 SELECT o.order_id;
  • 右表字段一般只能出现在 ON 条件中,不能出现在 SELECT、WHERE 等地方。

和 LEFT OUTER JOIN 的区别

LEFT OUTER JOIN 会保留左表所有记录,右表没匹配时补 NULL:

SELECT c.*, o.order_id
FROM customers c
LEFT OUTER JOIN orders o
ON c.cust_id = o.cust_id;

LEFT SEMI JOIN 只保留左表中“有匹配”的记录:

SELECT c.*
FROM customers c
LEFT SEMI JOIN orders o
ON c.cust_id = o.cust_id;

所以:

  • LEFT OUTER JOIN:左表全保留;
  • LEFT SEMI JOIN:只保留右表存在的左表记录。

Hive 使用注意点

  1. 只支持左半连接
    Hive 常用的是 LEFT SEMI JOIN,没有直接的 RIGHT SEMI JOIN。如果需要右半,可以交换左右表。

  2. 只输出左表列
    例如下面会报错或不符合语义:

    SELECT c.*, o.order_id
    FROM customers c
    LEFT SEMI JOIN orders o
    ON c.cust_id = o.cust_id;
    

  3. 右表不能随便出现在 WHERE 中
    例如:

    SELECT c.*
    FROM customers c
    LEFT SEMI JOIN orders o
    ON c.cust_id = o.cust_id
    WHERE o.amount > 100;
    
    这种通常不行,需要把条件放到 ON 里,或先用子查询过滤右表。

  4. 适合存在性判断
    比如“哪些用户有订单”“哪些商品被购买过”“哪些记录在另一张表中有匹配”。

  5. 可配合 MapJoin 优化小表

    SELECT /*+ MAPJOIN(o) */ c.*
    FROM customers c
    LEFT SEMI JOIN orders o
    ON c.cust_id = o.cust_id;
    

总结

LEFT SEMI JOIN 就是 Hive 中的左半连接:

  • 只返回左表字段;
  • 只保留右表存在匹配的左表记录;
  • 不会因为右表多条匹配而重复左表记录;
  • 常用于替代 IN / EXISTS 子查询;
  • 右表只能用于 ON 匹配,不能输出右表字段。