当前位置: 首页 > news >正文

mysql join on where

left outer join 的含义
LEFT OUTER JOIN(通常简写为 LEFT JOIN)是 SQL/JPQL 中外连接的核心类型,也是关联多表查询时最常用的连接方式之一。其核心含义是:以左表为基准,返回左表的所有记录,同时匹配右表中满足连接条件的记录;若右表无匹配记录,则右表字段返回 NULL

一、核心概念拆解

1. 关键术语

  • 左表:LEFT JOIN 左侧的表(驱动表),查询结果会包含左表的全部行;
  • 右表:LEFT JOIN 右侧的表(被驱动表),仅返回与左表匹配的行,无匹配则补 NULL
  • 连接条件:通过 ON 子句指定的匹配规则(如 a.id = b.a_id),决定右表哪些行能与左表匹配。

2. 与其他连接的对比(快速区分)

连接类型核心行为结果集特点
LEFT JOIN 左表全量,右表匹配补 NULL 行数 = 左表总行数
INNER JOIN 仅返回左右表都匹配的行 行数 ≤ 左表 / 右表总行数
RIGHT JOIN 右表全量,左表匹配补 NULL(与 LEFT 相反) 行数 = 右表总行数
FULL JOIN 左右表全量,彼此无匹配则补 NULL 行数 = 左表 + 右表 - 交集行数

二、直观示例(SQL 层面)

假设存在两张表:
  • t_user(用户表,左表):存储所有用户,即使该用户无订单;
  • t_order(订单表,右表):存储用户的订单记录。

表数据示例

t_user (左表) t_order (右表)  
id name id order_no user_id
1 张三 101 O1001 1
2 李四 102 O1002 1
3 王五 103 O1003 2

执行 LEFT JOIN 查询

sql
 
 
SELECT u.id AS user_id, u.name, o.id AS order_id, o.order_no
FROM t_user u
LEFT OUTER JOIN t_order o ON u.id = o.user_id; -- 连接条件:用户ID匹配
 

查询结果

user_idnameorder_idorder_no
1 张三 101 O1001
1 张三 102 O1002
2 李四 103 O1003
3 王五 NULL NULL
核心特征:
  • 左表(t_user)的「王五」无订单(右表无匹配),但仍出现在结果中,右表字段补 NULL
  • 左表有匹配的行(张三、李四),会返回所有匹配的右表记录(张三对应 2 条订单)。

三、JPQL/QueryDSL 中的 LEFT JOIN 用法

结合你之前关注的 JPQLQueryFactory,以下是 LEFT JOIN 的典型场景示例:

1. 基础 LEFT JOIN(QueryDSL 写法)

需求:查询所有用户,以及其关联的订单(无订单则显示 NULL)。
java
 
运行
 
 
 
 
@Service
public class UserOrderService {@Resourceprivate JPAQueryFactory queryFactory;private final QUser qUser = QUser.user;private final QOrder qOrder = QOrder.order;// LEFT JOIN 查询所有用户+订单(无订单补NULL)public List<Tuple> listUserWithOrders() {return queryFactory.select(qUser.id, qUser.name, qOrder.id, qOrder.orderNo).from(qUser)// LEFT JOIN:左表=qUser,右表=qOrder,连接条件=用户ID匹配.leftJoin(qUser.orders, qOrder) .fetch();}
}
 

2. LEFT JOIN + 过滤(仅过滤右表,不影响左表)

需求:查询所有用户,以及其「金额 > 100」的订单(无符合条件订单则补 NULL)。
注意:过滤右表条件需写在 ON 子句(而非 WHERE),否则会退化为 INNER JOIN!
java
 
运行
 
 
 
 
// 正确写法:过滤条件写在 ON 子句
public List<Tuple> listUserWithHighAmountOrders() {return queryFactory.select(qUser.id, qUser.name, qOrder.id, qOrder.amount).from(qUser)// ON 子句添加右表过滤条件.leftJoin(qUser.orders, qOrder).on(qOrder.amount.gt(new BigDecimal("100"))) .fetch();
}
 

3. LEFT JOIN + DISTINCT(去重)

需求:查询所有用户(去重),以及其订单(避免左表因右表多条数据重复)。
java
 
运行
 
 
 
 
// LEFT JOIN + DISTINCT 去重左表记录
public List<User> listDistinctUsersWithOrders() {return queryFactory.select(distinct(qUser)) // 对左表用户去重.from(qUser).leftJoin(qUser.orders, qOrder).fetch();
}
 

四、常见误区与注意事项

1. 过滤条件写在 WHERE 子句会导致 LEFT JOIN 失效

错误示例:
sql
 
 
-- 错误:WHERE 过滤右表会把左表无匹配的行(王五)过滤掉,退化为 INNER JOIN
SELECT u.id, u.name, o.order_no
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id
WHERE o.amount > 100;
 
正确示例(过滤条件写在 ON):
sql
 
 
SELECT u.id, u.name, o.order_no
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id AND o.amount > 100;
 

2. LEFT JOIN 后统计右表数据(需处理 NULL)

需求:统计每个用户的订单数(无订单则为 0)。
sql
 
 
SELECT u.id, u.name, COUNT(o.id) AS order_count -- COUNT(NULL)=0,适合统计
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id
GROUP BY u.id, u.name;
 

3. JPQL 中 LEFT JOIN 关联非关联字段

若实体未配置关联关系(如 @OneToMany),可通过 on 手动指定连接条件:
java
 
运行
 
 
 
 
// 无实体关联时的 LEFT JOIN(手动指定 ON 条件)
public List<Tuple> listUserWithOrdersNoRelation() {return queryFactory.select(qUser.id, qOrder.orderNo).from(qUser)// 手动指定左表、右表,以及连接条件.leftJoin(qOrder).on(qUser.id.eq(qOrder.userId)) .fetch();
}
 

五、核心使用场景

  1. 查询主表全量数据 + 关联表可选数据:如「查询所有商品,以及其评论(无评论则显示空)」;
  2. 统计主表记录的关联数据:如「统计每个用户的订单数(含无订单用户)」;
  3. 避免丢失左表数据:如「查询所有员工,以及其所属部门(含未分配部门的员工)」。

总结

LEFT OUTER JOIN 的核心是「左表全保留,右表匹配补,无匹配补 NULL」,是多表查询中保障左表数据不丢失的关键方式。使用时需注意:
  • 右表过滤条件写在 ON 子句,而非 WHERE
  • 结合 COUNT() 统计时,用右表非空字段(如主键)避免统计错误;
  • JPQL/QueryDSL 中,关联实体用 leftJoin(关联字段),非关联实体用 leftJoin(右表).on(条件)
 
mysql join on where
在 MySQL 中,JOIN ... ON 和 WHERE 是多表查询的核心语法,但二者的执行时机、作用范围、对结果集的影响 有本质区别,误用会导致查询结果不符合预期(比如 LEFT JOIN 被 “降级” 为 INNER JOIN)。以下从 核心区别、场景示例、执行原理、常见误区 四个维度,彻底讲清二者的用法和配合方式。

一、核心区别:ON vs WHERE

维度JOIN ... ONWHERE
执行时机 多表连接阶段(先匹配行,再生成临时表) 连接完成后(对临时表过滤行)
作用对象 仅作用于参与连接的两张表,控制 “哪些行能匹配” 作用于连接后的完整临时表,控制 “哪些行保留”
对 LEFT JOIN 影响 不过滤左表,仅过滤右表匹配行(无匹配则右表补 NULL) 过滤整个结果集(包括左表无匹配的行,可能丢失左表数据)
核心用途 指定表之间的连接条件(如 a.id = b.a_id 过滤最终结果集(如 a.status = 1

二、基础场景示例(先建测试表)

先创建两张测试表,方便后续演示:
sql
 
 
-- 左表:用户表
CREATE TABLE t_user (id INT PRIMARY KEY,name VARCHAR(20),status TINYINT -- 1=正常,0=禁用
);
INSERT INTO t_user VALUES (1, '张三', 1), (2, '李四', 1), (3, '王五', 0);-- 右表:订单表
CREATE TABLE t_order (id INT PRIMARY KEY,order_no VARCHAR(20),user_id INT,amount DECIMAL(10,2)
);
INSERT INTO t_order VALUES (101, 'O1001', 1, 99.00), (102, 'O1002', 1, 200.00), (103, 'O1003', 2, 150.00);
 

场景 1:仅用 ON 指定连接条件(LEFT JOIN 保留左表全量)

需求:查询所有用户,以及其订单(无订单则补 NULL)。
sql
 
 
SELECT u.id, u.name, o.order_no, o.amount
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id; -- 仅指定连接条件,不过滤
 
结果:左表(t_user)的 3 条记录全部保留,右表无匹配则补 NULL:
idnameorder_noamount
1 张三 O1001 99.00
1 张三 O1002 200.00
2 李四 O1003 150.00
3 王五 NULL NULL

场景 2:ON + WHERE 过滤左表全局条件

需求:查询正常状态的所有用户,以及其订单(先过滤左表,再连接)。
sql
 
 
SELECT u.id, u.name, o.order_no, o.amount
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id
WHERE u.status = 1; -- 过滤左表全局条件(禁用用户王五被排除)
 
结果:左表仅保留 status=1 的用户,右表匹配规则不变:
idnameorder_noamount
1 张三 O1001 99.00
1 张三 O1002 200.00
2 李四 O1003 150.00

场景 3:ON 中过滤右表(保留左表,仅过滤右表匹配行)

需求:查询所有用户,以及其金额 > 100 的订单(无符合条件订单则补 NULL)。
sql
 
 
SELECT u.id, u.name, o.order_no, o.amount
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id AND o.amount > 100; -- ON 中过滤右表
 
结果:左表全保留,右表仅匹配金额 > 100 的订单(张三的 O1001 被过滤,王五仍为 NULL):
idnameorder_noamount
1 张三 O1002 200.00
2 李四 O1003 150.00
3 王五 NULL NULL

场景 4:WHERE 中过滤右表(LEFT JOIN 降级为 INNER JOIN)

需求:错误示例 —— 想查询所有用户 + 金额 > 100 的订单,但误用 WHERE 过滤右表:
sql
 
 
SELECT u.id, u.name, o.order_no, o.amount
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id
WHERE o.amount > 100; -- WHERE 过滤右表,导致左表无匹配行被删除
 
结果:左表王五因 o.amount 为 NULL,不满足 o.amount > 100 被过滤,LEFT JOIN 变为 INNER JOIN:
idnameorder_noamount
1 张三 O1002 200.00
2 李四 O1003 150.00

三、执行原理(为什么会有这种区别?)

MySQL 执行多表 JOIN 查询的核心步骤:
  1. 笛卡尔积:先将左表和右表生成笛卡尔积(所有行的组合);
  2. ON 过滤:根据 ON 条件过滤笛卡尔积,保留匹配的行(LEFT JOIN 会保留左表未匹配的行,右表补 NULL);
  3. WHERE 过滤:对步骤 2 生成的临时表,按 WHERE 条件过滤,不满足的行直接删除;
  4. 分组 / 排序 / 限制:最后执行 GROUP BY、ORDER BY、LIMIT 等操作。
关键结论:ON 是 “筛选匹配的行”,WHERE 是 “筛选最终的行”;对 LEFT JOIN 来说,ON 不会删除左表行,而 WHERE 会。

四、常见误区与解决方案

误区 1:LEFT JOIN 中用 WHERE 过滤右表,导致左表数据丢失

问题:如场景 4,WHERE 过滤右表字段会删除左表无匹配的行(NULL 不满足任何比较条件)。解决方案:将右表过滤条件移到 ON 子句,或在 WHERE 中兼容 NULL:
sql
 
 
-- 方案 1:移到 ON 子句(推荐)
SELECT u.id, u.name, o.order_no, o.amount
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id AND o.amount > 100;-- 方案 2:WHERE 中兼容 NULL(适合需同时过滤其他条件)
SELECT u.id, u.name, o.order_no, o.amount
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id
WHERE (o.amount > 100 OR o.amount IS NULL) AND u.status = 1;
 

误区 2:INNER JOIN 中混淆 ON 和 WHERE(结果正确但性能差)

问题:INNER JOIN 中 ON 和 WHERE 过滤结果一致,但 ON 先过滤再连接,性能更高。示例:
sql
 
 
-- 低效:先连接所有行,再过滤
SELECT u.id, o.order_no
FROM t_user u
INNER JOIN t_order o ON u.id = o.user_id
WHERE u.status = 1;-- 高效:先过滤左表,再连接(减少笛卡尔积行数)
SELECT u.id, o.order_no
FROM t_user u
INNER JOIN t_order o ON u.id = o.user_id AND u.status = 1;
 

误区 3:多表 JOIN 时,ON 条件遗漏关联字段

问题:多表 JOIN 时,仅写部分关联条件,导致笛卡尔积膨胀。示例(错误 vs 正确):
sql
 
 
-- 错误:t_order 和 t_user 的关联条件写在 WHERE,导致先笛卡尔积再过滤
SELECT u.id, o.order_no, p.product_name
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id
LEFT JOIN t_order_product p ON o.id = p.order_id
WHERE u.id = o.user_id; -- 冗余且低效-- 正确:所有关联条件写在 ON 子句
SELECT u.id, o.order_no, p.product_name
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id
LEFT JOIN t_order_product p ON o.id = p.order_id;
 

五、最佳实践

  1. 关联条件必写在 ON:表之间的连接关系(如 u.id = o.user_id)必须写在 ON 子句;
  2. 左表全局过滤写在 WHERE:如 u.status = 1(过滤所有左表行);
  3. 右表选择性过滤写在 ON:如 o.amount > 100(保留左表,仅过滤右表匹配行);
  4. INNER JOIN 优先在 ON 过滤:减少笛卡尔积行数,提升性能;
  5. LEFT JOIN 过滤右表兼容 NULL:若必须用 WHERE 过滤右表,需加 OR 字段 IS NULL

六、总结

需求场景正确写法
指定表之间的关联关系 JOIN ... ON 关联条件
过滤左表所有行(全局条件) WHERE 左表字段 = 值
仅过滤右表匹配行(保留左表) JOIN ... ON 关联条件 AND 右表过滤条件
过滤右表且保留左表无匹配行 WHERE (右表字段 = 值 OR 右表字段 IS NULL)
INNER JOIN 过滤(性能优先) JOIN ... ON 关联条件 AND 过滤条件
记住核心原则:ON 管 “连接匹配”,WHERE 管 “最终过滤”;LEFT JOIN 中,ON 不丢左表行,WHERE 会丢。
http://www.rkmt.cn/news/70078.html

相关文章:

  • 高级程序语言设计第八次
  • 实验四 综合数据流处理-Storm案例构建
  • 不只是“外包”,而是共创:兰亭妙微解读UI设计合作的升维之道
  • 2025年房梁榫卯产品定制厂家权威推荐榜单:房梁榫卯产品介绍图‌/房梁结构‌/房梁榫卯产品介绍‌源头厂家精选
  • day4敏捷冲刺
  • 小红书种草公司哪家专业,哪家更推荐?
  • 国产全自动咖啡机排名:主流品牌实力解析
  • 从0开始重新学python+neo4j之文件读写、函数、异常
  • 实用指南:Nestjs框架: 微服务事件驱动通信与超时处理机制优化基于Event-Based 通信及异常捕获实践
  • 2025 年 12 月纳米砂磨机,卧式砂磨机,工业砂磨机厂家最新推荐,产能、专利、环保三维数据透视!
  • 迅为RK3588开发板Android系统烧写及注意事项
  • Pytorch随笔:gather函数
  • 2025年精选烤兰打包带实力源头厂家推荐,打包带钢带/冷镀锌打包带/烤漆打包带/带钢/镀锌打包钢带烤兰打包带品牌哪家好
  • 博士留学导师资源榜单:谁的学术人脉覆盖全球名校?
  • 使用NVIDIA Isaac Lab和Newton训练机器人策略与布料模拟
  • 2025年度中央空调品牌推荐排行榜,新测评精选中央空调品牌
  • 实测花灯厂家,技术强的原来是这家!避坑指南
  • verl grpo 快速开始
  • 2025年中国十大超声波吐司面包切割机服务商推荐:哪家售后服
  • 2025年打工人代餐清单:上班族便携即食的低热量代餐品牌推荐
  • sql server 导出excel表
  • 2025年市场热销雷达干扰模拟器品牌实力排行,无线信号测量仪表/以太网测试仪/光通信测量仪表雷达干扰模拟器企业推荐排行榜单
  • docker runc逃逸漏洞修复的大坑
  • 2025年柔性夹爪品牌怎么选?苏州柔触机器人核心技术
  • 2025年医疗用品搬运技术革新:柔性夹爪解决方案全景解析
  • 易基因:山东大学基础医学院李雷教授团队微量WGBS揭示DNA甲基化调控斑马鱼造血干细胞发育的表观遗传机制|项目文章
  • 2025年中国机床钣金加工企业综合竞争力TOP5排行榜
  • 2025年五大实验室耗材品牌排行榜,芯硅谷实力出众
  • 2025年中国十工业脚轮厂家推荐:推荐聚氨酯工业脚轮厂家哪家