SQL关联查询————LEFT JOIN关键字的使用
引言
关联查询一直是非常重要的SQL使用技巧。
在一次查询操作中,使用mybatis进行条件查询,在没有使用 LEFT JOIN 关键字的情况下是这样写的:
<!-- 查找成员 --><select id="selectUsers" resultMap="selectUsers_ResultMap">SELECT *FROMsys_user u, sys_user_role ur, sys_role r, sys_dept d<where><if test="roleId != null">AND ur.user_id = u.user_idAND ur.role_id = r.role_idAND r.role_id = #{roleId}</if><if test="deptId != null">AND u.department_id = d.dept_idAND u.department_id = #{deptId}</if><if test="username != null">AND u.username = #{username}</if></where></select>
这个查询操作的需求是:根据roleId(角色id)、deptId(部门id)、username(账号)查找用户列表。
虽然可以正常执行,但是查询的结果并不完全正确。
BUG重现
上述查询SQL看似逻辑比较严谨,该关联的都画了等号,但是查询结果是错误的:
首先,进行接口调用,查询所有的 roleId = 12 的用户
真实数据
- role表:role_id = 12
- user_role表:role_id = 12 ——>user_id = 31
- user表:user_id = 31 ——> department_id = 1
- dept表:dept_id = 1 ——> 研发总监部
数据库user_id = 31的用户department_id = 1 ,即“研发总监部”。
查询结果
上图,是通过swagger API 间接调用的接口,执行的就是引言中的SQL语句,可以看到,虽然 roleId = 12 查询正常,如果不细心可能不会发现这个问题,部门信息为什么会是 deptId = 9 的 “炼丹部” ?
错误原因分析
经过仔细思考,得出结论:
在错误的SQL中,我们的 ID关联条件 都写在了WHERE子句中,且通过动态SQL进行分支执行。
这就导致了:由于只传入了roleId = 12 的条件,而deptId未作为查询条件传入,此时 user.department_id = dept.dept_id 也不会对查询进行外键约束,换言之,这个约束条件可能会被WHERE动态拼接后的SQL所舍弃。因此,导致了查询结果中用户与部门的对应关系与数据库实际的对应关系不一致的情况。
引入LEFT JOIN
解决方案
清晰了问题的症结所在,那么如何解决问题呢?
我们的要求是不论WHERE子句中的查询条件有或没有,都要将 id 进行关联。不论是 user.role_id = role.role_id ,还是user.department_id = dept.dept_id 都要在任何查询情况下进行关联,这样就可以得出与数据库对应关系相符的数据。
最终加入LEFT JOIN后的SQL是这样的
<!-- 查找成员 此SQL必须用left join,因为如果将关联条件写在where中,分支将会忽略未执行的关联条件,导致查询结果出错--><select id="selectUsers" resultMap="selectUsers_ResultMap">SELECT *FROM (SELECT u.*, ur.role_idFROM sys_user uLEFT JOIN sys_user_role urON u.user_id = ur.user_id) u_roleLEFT JOIN sys_role r ON u_role.role_id = r.role_idLEFT JOIN sys_dept d ON u_role.department_id = d.dept_id<where><if test="roleId != null">AND r.role_id = #{roleId}</if><if test="deptId != null">AND u_role.department_id = #{deptId}</if><if test="username != null">AND u_role.username = #{username}</if></where></select>
声明一个问题:为什么会有子查询?
子查询主要是解决 user 表、user_role 表、role 表之间的关联关系,其中user_role 表是一个只存储 user_id 和 role_id 的中间表,这条子查询仅仅适用于 一个用户只拥有一个角色的情况(角色是用户一个分组,本来可以完全不用中间表,但是为了后期扩展为用户-角色 呈多对多的关系,故加入user_role 中间表)。
针对于上述实际的SQL语句来说,这条子查询,仅仅是将 用户所对应的唯一的 role_id 拼接到了user表的末尾,并连同user表的所有数据一同查出。这里其实也可以使用一个LEFT JOIN ,但是由于子查询中的WHERE 子句一定会执行,因此这样写也是可以的。
另外注意:
子查询一定要记得加别名,否则SQL执行会报错!!
子查询一定要记得加别名,否则SQL执行会报错!!
子查询一定要记得加别名,否则SQL执行会报错!!
修改后测试
同样只传入 roleId = 12 查询全部用户:
可以看到,查出的用户已经与数据库的关联信息保持一致了,其他的成员也都是如此。说明,我们的SQL执行结果符合我们的期望。
复习LEFT JOIN
定义
LEFT JOIN 关键字会从左表 (table_name1) 那里返回所有的行,即使在右表 (table_name2) 中没有匹配的行。
语法
SELECT column_name(s) FROM table_name1 LEFT JOIN table_name2 ON table_name1.column_name=table_name2.column_name
综上,就是对于SQL关联查询的爬坑随笔,比较 隐蔽的一个错误。希望能够对大家有所帮助。欢迎文末留言。
SQL关联查询————LEFT JOIN关键字的使用相关推荐
- SQL关联查询详解,SQL JOIN详解
关联查询,也称为多表查询,指两个或更多个表一起完成查询操作. 前提条件:这些一起查询的表之间是有关系的(一对一.一对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,也可能没有建立外键. 一 ...
- You can't specify target table 'dt_task_apply' for update in FROM clause sql 关联查询 并删除
两个表相互关联,结果 只删除了第一个表 A(task)中数据,导致 B (task_apoly)表数据依旧在, 这个就不好了!由于数据量比较大,还不能直接删除B中所有数据,只能关联查询删除了 dele ...
- mybatis02映射动态sql关联查询spring整合mybatis
2019独角兽企业重金招聘Python工程师标准>>> 输入映射和输出映射: 动态sql: 关联查询_一对一: 关联查询_一对多: 一对一,一对多操作的区别: 一对一,resultM ...
- MyBatis 实践 -动态SQL/关联查询
MyBatis 实践 标签: Java与存储 动态SQL 动态SQL提供了对SQL语句的灵活操作,通过表达式进行判断,对SQL进行拼接/组装. if 对查询条件进行判断,如果输入参数不为空才进行查询条 ...
- mybatis collection标签_MyBatis第二天(结果映射+动态sql+关联查询)
笑不出莱:MyBatis第一天(介绍+文件配置+Mapper动态代理)zhuanlan.zhihu.com 一.导包+配置文件+pojo类 1.jar包:mybatis的核心包和依赖包+连接数据库的 ...
- SQL语法之 FULL JOIN 关键字
SQL学习 学习SQL语法 SQL语法 SQL学习 FULL JOIN 关键字 全连接(FULL JOIN)实例 FULL JOIN 关键字 SQL FULL JOIN 关键字 只要其中某个表存在匹配 ...
- sql三个表关联查询LEFT JOIN
SELECT 别名1.字段1,别名1.字段2,别名2.字段1,别名2.字段2,别名3.字段1,别名3.字段2 FROM 表名1AS 别名1 LEFT JOIN 表名2AS 别名2 ON 别名1.字段1 ...
- sql关联查询子表的第一条_SQLAlchemy(8)惰性查询
10204 15.relationship 中惰性查询 1.试理解relationship(自己) MySQL 是一个关系型数据库,关系型数据库最关键的就是关系.SQLAlchemy 作为一层ORM ...
- mysql多表关联left join_Mysql多表表关联查询 inner Join left join right join
一.Join语法概述 join 用于多表中字段之间的联系,语法如下:... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona table1: ...
最新文章
- Java中的值传递和引用传递
- python中lambda使用
- 纯Shading Language绘制飞机火焰效果
- (0030) iOS 开发之跳转之转场动画
- php判断字符串是否为IP,php 判断IP为有效IP地址的方法
- 三、NoSQL数据库的四大分类的分析
- Python二叉树的三种深度优先遍历
- 搭建golang+vscode开发环境
- HDU 1281:棋盘游戏
- loadRunner函数之lr_set_debug_message
- Unicode编码和解码(3种)
- tf.expand_dims函数用法详解(搭配代码理解)
- 亚信安全发现勒索软件新变种 Word文档成为导火索
- Java烤地瓜简单小案例
- excel填充序列_猴哥讲述:excel的自动填充功能——自动填充单元格
- 三维模型是什么?3D建模都有什么用处?
- 解决打开Chrome网页被2345篡改
- 海康威视真是太恶心了
- Blender动画笔记:Basic Jump Cycle(二)调整曲线和节奏
- Fedora32 防火墙设置常用命令
热门文章
- Java Integer类highestOneBit()方法与示例
- C# Winform 窗体美化(二、LayeredSkin 界面库)
- JQuery Datatables 实现对某一列的数据合计汇总
- 矩阵分析理论在实际工程中的应用_【顶管技术在市政给排水工程中的应用分析】...
- java 小数处理_java 小数点处理
- qt android程序联网死机,Qt for Android(九) ——APP 崩溃卡死拉起保活实战
- 织梦自定义图片字段和缩略图一样_织梦图片集模型自定义图片字段调用
- mysql数据库特征_如何掌握MySQL数据库中动态表的特征
- UC浏览器怎么清除缓存
- win7电脑共享硬盘分区的方法