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 替代)
相关推荐
l1t1 天前
DeepSeek总结的修复pgrust v0.2 #83:generate_series 聚合及其底层的两个成本 - #84
数据库·postgresql
2601_962181961 天前
数据库之PostgreSQL详解
数据库·postgresql
pnoker1 天前
IoT DC3 时序存储选型:四款数据库可插拔
数据库·物联网·postgresql·时序数据库·influxdb·tdengine·iotdb
xuefuhe2 天前
PostgreSQL源码1:select 1
postgresql
IvorySQL2 天前
PostgreSQL 日报|内核多项缺陷修复与数据校验补丁推进(8 月 27 日)
数据库·postgresql·区块链
Cloud云卷云舒3 天前
【运营商数据库】实战淬炼,规模落地 | 中国移动HaishanDB年中总结会圆满召开
数据库·postgresql·ai-native·haishandb·移动云海山数据库
xuefuhe3 天前
PostgreSQL Page Layout
postgresql
云贝贝贝3 天前
PostgreSQL 生产环境备份恢复与高可用实战
数据库·postgresql
wangchunyu1143 天前
PostgreSQL 入门与实践:从零搭建一个 CRUD 应用
数据库·postgresql
m0_579146653 天前
PostgreSQL+Milvus分层存储架构:双写一致性与CAP理论取舍分析
postgresql·架构·milvus·cap