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关键字的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python怎么切换中文键盘_pytho
- 下一篇: MySQL优化建议汇总~~~