朋友做乙個專案,遇到乙個多表聯合查詢的需求。
需要乙個sql,當a表中system_id值為123時,找到b表和c表的關聯,當b表滿足system_id值為123時,包含其中的role_id資料,顯示c表中name資料。例如查詢結果為:james、lucy.
t_table_a表
idsystem_id
1123
2234
t_table_b表
idsystem_id
role_id
1222
6672
123
555
3123
777
4234
5675
234231
t_table_c表
idname
role_list
1james
667,777
2lucy
223,555
,823
3tom
253,231
4max
123,712
5min
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.
從給的脫敏資料可以推測出各個表的功能。
一般使用單獨的表來儲存使用者和角色的關聯資訊,這裡很巧妙的採用以逗號分隔的方式儲存多個角色編號。採用這種方式可以減少表連線,但處理儲存和查詢的複雜度更高。
單純從資料上來看,由於採用的模糊查詢,有可能出現錯誤的查詢結果。
假設c表中存在以下資料。
idname
role_list
6kim
1777
,2211
7kenv
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;
乙個業務邏輯引發的對多表連線的思考
批量插入語句 insert into 保養表 value.select value.from 裝備表 實現上面業務邏輯主要難點是在查的部分,實現 在滿足現在時間大於保養時間的情況下,有兩種情況會被插入到保養表裡.1 該裝備在保養表裡的state 0的,2 該裝備在保養表裡沒有的 實現這個需求可以有兩...
乙個事故引發的思考
今天線上服務出現了乙個事故,思考下這個事故,覺得有好幾個地方需要思考。1 對於前端而言,回滾的功能是必須的。前端介面出現了問題,第乙個應該想到的是將 回滾到乙個穩定版本。2 快取和資料庫的使用,需要注意乙個問題,當快取失效的時候,可能會有大併發的請求去訪問資料庫,這個時候資料庫會不會崩潰?如果這個時...
乙個案例引發的思考
今天下午,團隊開了乙個簡短的版本總結會。會上測試經理分析了乙個案例 某子程式在轉測試後發現不能被平台排程,原因是子程式的排程入口跟不符合平台規範。很明顯開發在轉測試前沒有充分自驗證,測試經理提出,後續對跟平台對接的子程式轉測試必須要有將子程式接入平台跑通後的驗證報告和相關checklist,否則不予...