PostgreSQL JOIN 大白话指南

bp_master_data × bp_level_lock 为真实案例讲解


先建立一个直觉模型

把两张表想象成两摞扑克牌:

  • 左牌堆 = bp_master_data(全量 BP 信息,5000 张)
  • 右牌堆 = bp_level_lock_fy2627_q1(某财季的 BP 等级,只有 300 张)

不同 JOIN 就是"用不同规则把两摞牌拼在一起"。


1. INNER JOIN(普通 JOIN)--- 只要两边都有的

规则: 左边有、右边也有,才出现在结果里。两边任意一边没有 → 这行直接消失。

sql 复制代码
SELECT bmd.*, lck.bp_level
FROM bp_master_data bmd
INNER JOIN bp_level_lock lck
    ON lck.incentive_id = bmd.incentive_id
   AND lck.fiscal_year  = 'FY2627'
   AND lck.fiscal_quarter = 'Q1'

结果: 只有在 bp_level_lock_fy2627_q1 里有记录的 BP 才会出现。

没有等级记录的 BP → 直接丢掉,不出现在结果里

复制代码
bp_master_data   bp_level_lock_fy2627_q1   结果
PA001            ✅ 有记录                 → 出现(bp_level = Gold)
PA002            ❌ 没记录                 → 消失
PA003            ✅ 有记录                 → 出现(bp_level = Silver)

适用场景: 只想看"有等级"的 BP。


2. LEFT JOIN(左连接)--- 左边全要,右边没有就填 NULL

规则: 左边的每一行必须出现在结果里。右边有就拼进来,右边没有就用 NULL 补位。

sql 复制代码
SELECT bmd.*, lck.bp_level
FROM bp_master_data bmd
LEFT JOIN bp_level_lock lck
    ON lck.incentive_id = bmd.incentive_id
   AND lck.fiscal_year  = 'FY2627'
   AND lck.fiscal_quarter = 'Q1'

结果: 全量 5000 条 BP 都出现,有等级的填 bp_level,没等级的 bp_level = NULL。

复制代码
bp_master_data   bp_level_lock_fy2627_q1   结果
PA001            ✅ 有记录                 → bp_level = Gold
PA002            ❌ 没记录                 → bp_level = NULL
PA003            ✅ 有记录                 → bp_level = Silver

适用场景: 想看全量 BP,顺便附上等级信息(没有等级也要显示这个 BP)。

这就是本次 queryMasterWithLevel 用的方式。


3. RIGHT JOIN(右连接)--- 右边全要,左边没有就填 NULL

LEFT JOIN 的镜像版。右边每一行必须出现,左边没有就 NULL 补位。

实际工作中很少用,通常把表顺序换一下改成 LEFT JOIN 更直观。


4. FULL JOIN(全外连接)--- 两边都全要,缺哪边就 NULL 补

规则: 不管左边还是右边,所有行都必须出现在结果里。对应不上的位置用 NULL 填充。

sql 复制代码
SELECT bmd.*, lck.bp_level
FROM bp_master_data bmd
FULL JOIN bp_level_lock lck
    ON lck.incentive_id = bmd.incentive_id

结果:

复制代码
bp_master_data   bp_level_lock   结果
PA001            Gold            → 两边都有,正常拼
PA002            NULL            → bp_master_data 有,lock 没有 → bp_level = NULL
NULL             Silver          → lock 有,但 bp_master_data 没有这个 incentive_id → bmd 字段全 NULL

适用场景: 数据对账、找"孤儿数据"(在 lock 表里有但 master 里找不到的 BP)。

实际业务查询用得很少。


5. CROSS JOIN --- 笛卡尔积,两边每行互相组合一遍

规则: 没有 ON 条件,左边每一行和右边每一行两两配对。

左边 M 行 × 右边 N 行 = 结果 M×N 行。

sql 复制代码
-- 如果 bp_master_data 有 5000 行,右边有 2 个财季
SELECT bmd.*, periods.fiscal_year, periods.fiscal_quarter
FROM bp_master_data bmd
CROSS JOIN (VALUES ('FY2627', 'Q1'), ('FY2526', 'Q4')) AS periods(fiscal_year, fiscal_quarter)

结果: 5000 × 2 = 10000 行,每个 BP 和每个财季都组合了一次。

复制代码
PA001  ×  FY2627 Q1  →  一行
PA001  ×  FY2526 Q4  →  一行
PA002  ×  FY2627 Q1  →  一行
PA002  ×  FY2526 Q4  →  一行
...

适用场景: 把一个小的"维度列表"(财季、大区、产品线)扩展到每一条主数据上,

然后再 LEFT JOIN 找对应的事实数据。


本次 SQL 的组合拳解析

sql 复制代码
FROM bp_master_data bmd                          -- ① 5000 条 BP 全量
CROSS JOIN (
    VALUES ('FY2627', 'Q1'), ('FY2526', 'Q4')    -- ② 2 个财季
) AS periods(fiscal_year, fiscal_quarter)
LEFT JOIN bp_level_lock lck                      -- ③ 找等级
    ON  lck.incentive_id   = bmd.incentive_id
    AND lck.fiscal_year    = periods.fiscal_year
    AND lck.fiscal_quarter = periods.fiscal_quarter

执行逻辑三步走:

复制代码
第①步  CROSS JOIN
       bp_master_data(5000) × periods(2) = 10000 行
       每个 BP 被复制了 2 份,每份对应一个财季

第②步  LEFT JOIN bp_level_lock
       对每一行,去 bp_level_lock 找匹配的 (incentive_id, fiscal_year, fiscal_quarter)
       找到 → 填上 bp_level
       没找到 → bp_level = NULL

第③步  输出 10000 行结果
       每个 BP × 每个请求财季 = 一行

为什么要先 CROSS JOIN 再 LEFT JOIN,而不是直接 LEFT JOIN?

因为需求是"给我 FY2627Q1 和 FY2526Q4 两个财季的数据"------如果只用 LEFT JOIN,

你没有办法在一条 SQL 里同时为每个 BP 生成两行(一行对应 Q1,一行对应 Q4)。

CROSS JOIN 先把财季维度"展开",LEFT JOIN 再把等级"附上去",两步合一。


一张图总结

复制代码
数据集 A = {1, 2, 3}
数据集 B = {2, 3, 4}

INNER JOIN     → {2, 3}           只保留两边都有的
LEFT JOIN      → {1, 2, 3}        A 全要,B 没有的填 NULL
RIGHT JOIN     → {2, 3, 4}        B 全要,A 没有的填 NULL
FULL JOIN      → {1, 2, 3, 4}     两边都全要,缺的填 NULL
CROSS JOIN     → {1×2, 1×3, 1×4,  两两组合,无条件,结果 = 3×3 = 9 行
                  2×2, 2×3, 2×4,
                  3×2, 3×3, 3×4}

实际选择建议

场景 用哪个
只要两边都有的数据 INNER JOIN
左表全量 + 右表补充信息(可为空) LEFT JOIN ✅ 最常用
数据对账,找两边的差异 FULL JOIN
把一个小维度列表展开到大表每一行 CROSS JOIN
右表全量 + 左表补充 RIGHT JOIN(可以改成 LEFT JOIN 替代)
相关推荐
花酒锄作田1 天前
DeepAgents - 使用Postgres作为Checkpoint
postgresql·deepagents
瀚高PG实验室2 天前
pgsql-ogr-fdw
数据库·postgresql·瀚高数据库·highgo
IvorySQL2 天前
PostgreSQL 技术日报 (6月5日)|PG19 Beta1 上线,PGConf.PL 2026开启征稿
数据库·postgresql·区块链
IvorySQL2 天前
【HOW 2026 分论坛演讲】PG/IvorySQL私有云中实践
数据库·人工智能·sql·postgresql
倒流时光三十年2 天前
PostgreSQL ON CONFLICT DO UPDATE 增加 WHERE 条件优化性能
数据库·postgresql
IvorySQL2 天前
PostgreSQL 技术日报 (6月1日)|逻辑复制问题修复,AI 行业动态速览
数据库·人工智能·postgresql
*neverGiveUp*2 天前
PostgreSql常用SQL大全
数据库·sql·postgresql
倒流时光三十年2 天前
PostgreSQL HOT 优化 - 大白话解释
数据库·postgresql·hot
有想法的py工程师2 天前
PostgreSQL分区表父索引INVALID排查实战:缺少某个分区索引导致父索引INVALID
数据库·postgresql