朋友作一個項目,遇到一個多表聯合查詢的需求。mysql
須要一個sql,當A表中SYSTEM_ID值爲123時,找到B表和C表的關聯,當B表知足SYSTEM_ID值爲123時,包含其中的ROLE_ID數據,顯示C表中NAME數據。例如查詢結果爲:james、lucy.sql
T_TABLE_A表函數
ID | SYSTEM_ID |
---|---|
1 | 123 |
2 | 234 |
T_TABLE_B表code
ID | SYSTEM_ID | ROLE_ID |
---|---|---|
1 | 222 | 667 |
2 | 123 |
555 |
3 | 123 |
777 |
4 | 234 | 567 |
5 | 234 | 231 |
T_TABLE_C表資源
ID | NAME | ROLE_LIST |
---|---|---|
1 | james |
667,777 |
2 | lucy |
223,555 ,823 |
3 | tom | 253,231 |
4 | max | 123,712 |
5 | min | 123,567 |
最終提供的sql以下:字符串
select name from t_table_c c where exists ( select 1 from t_table_a a inner join t_table_b b on a.system_id = b.system_id where a.system_id = 123 and c.role_list like '%' || b.role_id || '%' )
上面的查詢sql採用的exists子句的方式,採用鏈接的方式也能完成相同的功能,具體實現見文末附錄Sql.table
從給的脫敏數據能夠推測出各個表的功能。class
通常使用單獨的表來存儲用戶和角色的關聯信息,這裏很巧妙的採用以逗號分隔的方式存儲多個角色編號。採用這種方式能夠減小錶鏈接,但處理存儲和查詢的複雜度更高。select
單純從數據上來看,因爲採用的模糊查詢,有可能出現錯誤的查詢結果。權限
假設C表中存在如下數據。
ID | NAME | ROLE_LIST |
---|---|---|
6 | kim | 1777 ,2211 |
7 | kenv | 220,1555 ,800 |
從新執行sql,你會發現查詢結果裏面包含:kim和kenv. 形成這個結果的緣由是,模糊查詢能夠匹配ROLE_LIST列中部分數據,1777和1555分別包含777和555.
經過這個簡單示例,能夠發現存儲多個值時確實包含一些侷限性。咱們既想一列存儲多個數值,又想消除歧義,那麼怎麼解決這個問題呢?
給存儲的值添加一個前綴,也許是一個好辦法,好比:r555,r777,r1777,r1555,前綴採用表明特定含義的單詞或字母,加上實際的數值,能夠構造出一個特殊的字符串。條件子語句:like '%r555'. 能夠避免示例數據中的錯誤查詢結果問題。
等等,好像仍是有點問題。若是字符串爲:r55,r555,r1555,何解。
固然若是存在這種狀況,咱們還有一個終極解決辦法(適用於任何狀況):前綴+值+後綴
前綴+值+後綴 列存儲示例:[55],[555],[1555] 條件子語句:like '%[55]%',這裏前綴='['、值='55'、後綴=']'
通常狀況下,使用特定單詞或字母做爲特殊前綴加取值能夠滿級絕大多數狀況,這種方式可讀信更高。而須要嚴格意義上的避免誤查詢,能夠採用:前綴+值+後綴。
--附錄Sql create table t_table_a ( id int primary key, system_id int ); create table t_table_b ( id int primary key, system_id int, role_id int ); create table t_table_c ( id int primary key, name varchar2(20), role_list varchar2(200) ); insert into t_table_a values(1, 123); insert into t_table_a values(2, 234); insert into t_table_a values(3, 100); insert into t_table_b values(1, 222, 667); insert into t_table_b values(2, 123, 555); insert into t_table_b values(3, 123, 777); insert into t_table_b values(4, 234, 567); insert into t_table_b values(5, 234, 231); insert into t_table_c values(1, 'james', '667,777'); insert into t_table_c values(2, 'lucy', '223,555,823'); insert into t_table_c values(3, 'tom', '253,231'); insert into t_table_c values(4, 'max', '123,712'); insert into t_table_c values(5, 'min', '123,567'); insert into t_table_c values(6, 'kim', '1777,2211'); insert into t_table_c values(7, 'kenv', '220,1555,800'); select name from t_table_c c where exists ( select 1 from t_table_a a inner join t_table_b b on a.system_id = b.system_id where a.system_id = 123 and c.role_list like '%' || b.role_id || '%' ); select c.name, b.id from t_table_a a inner join t_table_b b on a.system_id = b.system_id inner join t_table_c c on c.role_list like '%' || b.role_id || '%' where a.system_id = 123;
On more thing.
這sql在Oracle中跑起來,沒有什麼問題。可是朋友拿去改造後在mysql中運行,出現的查詢結果不正常。排查了很長一段時間,才找到緣由:在mysql中 '%' || 'S001' || '%' 顯示打印爲:1. 正確的方式是使用concat函數。因此若是你在mysql中運行,須要這樣構造sql.
select c.name, b.id from t_table_a a inner join t_table_b b on a.system_id = b.system_id inner join t_table_c c on c.role_list like concat('%', b.role_id, '%') where a.system_id = 123;