MySql 关联查询时 on条件过滤和where条件过滤的区别

在学习mysql的连接查询时容易忽略的细节,当左连接时就以左表为主,有连接就以由表为主,但是过滤条件为on和where时时常被我们忽略。

首先建两张表并导入数据:

-- ----------------------------
-- Table structure for sys_dept
-- ----------------------------
DROP TABLE IF EXISTS `sys_dept`;
CREATE TABLE `sys_dept`  (
  `dept_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id,自增',
  `dept_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '部门名称',
  PRIMARY KEY (`dept_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_dept
-- ----------------------------
INSERT INTO `sys_dept` VALUES (1, '研发部');
INSERT INTO `sys_dept` VALUES (2, '测试部');
INSERT INTO `sys_dept` VALUES (3, '财务部');
INSERT INTO `sys_dept` VALUES (4, '人事部');

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id,自增',
  `user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',
  `dept_id` int(11) NULL DEFAULT NULL COMMENT '部门id',
  `is_disable` int(11) NULL DEFAULT NULL COMMENT '是否禁用:1-启用,0-禁用',
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, '张三', 1, 0);
INSERT INTO `sys_user` VALUES (2, '李四', 4, 1);
INSERT INTO `sys_user` VALUES (3, '王五', 1, 1);
INSERT INTO `sys_user` VALUES (4, '赵六', 3, 1);
INSERT INTO `sys_user` VALUES (5, '田七', 2, 1);
INSERT INTO `sys_user` VALUES (6, '刘八', 4, 0);

SET FOREIGN_KEY_CHECKS = 1;

一共两张表,用户表和部门表,用户表中的dept_id和部门表的dept_id关联,下面的对两表进行关联查询,查出用户名和用户对应的部门名称,分别采用 where 和 on 进行条件过滤。

where 过滤

SELECT
	u.user_name,
	d.dept_name 
FROM
	sys_user u
	LEFT JOIN sys_dept d ON u.dept_id = d.dept_id 
WHERE
	u.is_disable = 1

条件在where后加过滤条件输出结果

on 过滤

SELECT
	u.user_name,
	d.dept_name 
FROM
	sys_user u
	LEFT JOIN sys_dept d ON u.dept_id = d.dept_id 
AND
	u.is_disable = 1

在on后加过滤条件输出结果

上述两个查询中都是加了is_disable = 1这个条件,本来左连接要以左表为主,但是where后加了is_disable = 1后过滤掉了左表中的两条数据,而 and 后加is_disable = 1却仍然以坐标为主,并没有过滤掉左表中的数据,这是为什么呢?

mysql在进行关联查询时会生成一个临时表,当过滤条件为on时mysql会不管on后的条件是否为真,都会以左表为主(这里指左连接);而过滤条件为where时,mysql会在临时表生成之后再对临时表进行条件过滤,所以第二条sql中仍然以左表为主,并没有将数据过滤掉。

### MySQL 中 LEFT JOIN 与 FILTERED LEFT JOIN 的性能结果差异 #### 结果差异 当直接使用 `LEFT JOIN` ,所有的左数据都会被保留下来,即使右没有匹配的数据也会返回 NULL 值。然而,在 `WHERE` 子句中加入额外的过滤条件可能会改变这种行为[^1]。如果 `WHERE` 条件包含了针对右列的非空约束(如 `IS NOT NULL`),MySQL 可能会将外连接转换为内连接,从而导致结果类似于 `INNER JOIN` 而不再是完整的 `LEFT JOIN` 行为[^2]。 对于经过预过滤后的 `LEFT JOIN` (即先对参与联结的格应用某些筛选标准再做联合运算),由于已经减少了待处理的数据量,因此其最终输出可能更接近于仅包含满足特定业务逻辑需求的那一部分记录集合,而不是单纯依赖后续阶段施加的进一步限制来裁剪整个关联产物。 #### 性能对比分析 从理论上讲,“Filtered Left Join”的方式通常能够带来更好的查询现,原因在于它可以提前削减不必要的计算负担——通过尽早剔除不符合要求的部分,减少实际参与到昂贵笛卡尔乘积操作里的元素数量,进而降低整体复杂度并加快响应速度[^4]。 另一方面,尽管单纯的 `LEFT JOIN` 实现起来更为简洁直观,但在存在严格的选择性或者涉及大量无意义组合的情况下,则可能导致资源浪费以及延长执行周期等问题;尤其是在面对大规模数据集尤为明显。不过值得注意的是,具体到每一个实例上的相对优劣还需要考虑多种因素共同作用的结果,比如索引的存在与否及其质量、统计信息的新鲜程度等都可能显著影响两者的实际效能差距[^5]。 ```sql -- Example of Filtered Left Join Approach SELECT l.*, r.column_name FROM left_table AS l LEFT JOIN ( SELECT * FROM right_table WHERE some_condition = 'value' ) AS r ON l.key_column = r.foreign_key; ``` 以上代码片段展示了如何实现一个带有前置过滤步骤的Left Join过程。这里我们首先限定右侧子查询只获取符合条件的数据项,然后再将其作为临结果集与左侧主表进行链接动作。 综上所述,虽然两种方法都能达到相似的目的,但从效率角度出发推荐优先尝试采用“Filtered Left Join”模式除非特殊情况下证明后者具备绝对优势。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值