【原】對MYSQL下視圖的一些總結


注:本文使用mysql5.5版本為例。
 
做過數據庫開發的同學,對視圖(VIEW)應該不會陌生。
 
我接觸視圖最多的應用場景有兩個:
1)出於權限問題,為了限制訪問者看到過多的表字段(或內容),就建立一個視圖供TA使用。
2)也可以為了查詢方便,將多張表合並到一個視圖里使用。
 
比如,我最近遇到一個這樣的需求:
 
有四種用戶類型的表,有部分相同的信息,當然也會有很多不相同的字段:
T_USER_PERSON(ID, PERSON_NAME, CREATE_TIME, …)
T_USER_TEAM(ID, TEAM_NAME, CREATE_TIME, …)
T_USER_COMPANY(ID, COMPANY_NAME, CREATE_TIME, ...)
T_USER_ORGANIZATION(ID, ORGANIZATION_NAME, CREATE_TIME, ...)
 
還有一張業務表,匯集了這四種用戶分別創建的業務數據:
T_BUSSINESS(ID, USER_ID, USER_TYPE, CONTENT, ...)
USER_TYPE 是枚舉類型:PERSON, TEAM, COMPANY, ORGANIZATION
由USER_ID和USER_TYPE來關聯哪類的哪個用戶,創建了這條業務數據。
 
關系圖大概是這樣的:
 

 

 
我第一個就想到了使用視圖➡建立一個視圖統一管理所有用戶,再跟業務表關聯查詢,業務邏輯多么清晰!
於是,我建立這張視圖,如下:
CREATE OR REPLACE VIEW VIEW_ALL_TYPE_USER as
select id id, PERSON_NAME user_name, 'PERSON' user_type, create_time create_time from T_USER_PERSON
UNION ALL
select id as id, COMPANY_NAME as user_name, 'COMPANY' as user_type, create_time create_time from T_USER_COMPANY
UNION ALL
select id as id, ORGANIZATION_NAME as user_name, 'ORGANIZATION' as user_type, create_time create_time from T_USER_ORGANIZATION
UNION ALL
select id as id, TEAM_NAME as user_name, 'TEAM' as user_type, create_time create_time from T_USER_TEAM;
 
改變之后的關系圖大概是這樣的:

 

 
聯合查詢的sql語句如下:
select b.id, b.user_id, v.user_name, b.content
from T_BUSSINESS b
left join VIEW_ALL_TYPE_USER v ON b.user_id=v.user_id and b.user_type=v.user_type
order by b.id desc
 
看起來很簡潔,但是運行起來就 慢得要死。其實數據量並不大,用戶總共大概有12萬,業務數據大概1400條。可是,經過視圖和表的關聯查詢,速度是不可以忍受的,我想,這是多重笛卡爾積的結果吧。
 
除了聯合查詢效率低的問題, 視圖還不能建立索引,沒辦法有效提高查詢效率。
 
經過再三權衡,我決定放棄視圖,寧可使用稍微復雜一些的case-then語句來實現。效果還是可以的。
select b.id,
b.user_id,
b.user_type,
case b.user_type
when 'PERSON' then (select USER_NAME from T_USER_PERSON u where u.id=b.user_id)
when 'ENTERPRISE' then (select COMPANY_NAME from T_USER_COMPANY o where o.id=b.user_id)
when 'CHARITY' then (select ORGANIZATION_NAME from T_USER_ORGANIZATION c where c.id=b.user_id)
when 'TEAM' then (select TEAM_NAME from T_USER_TEAM t where t.id=b.user_id)
end as user_name,
b.content
from T_BUSSINESS b
order by b.id desc
 
但是,這樣的話,存在一個問題:
如果用別名作為查詢條件,會出現錯誤:Unknown column 'user_name' in 'where clause'
 
1)把別名的查詢條件放在table后面:
這種辦法對我不起作用,不知道是否是版本的問題……
 
2)用一個select包起來:
注意:每個table,包括最終()起來的虛表都要起個別名
 
3)在查詢條件里使用完整的case-then:
在本文中的sql語句應該是:
select b.id,
b.user_id,
b.user_type,
case b.user_type
when 'PERSON' then (select USER_NAME from T_USER_PERSON u where u.id=b.user_id)
when 'ENTERPRISE' then (select COMPANY_NAME from T_USER_COMPANY o where o.id=b.user_id)
when 'CHARITY' then (select ORGANIZATION_NAME from T_USER_ORGANIZATION c where c.id=b.user_id)
when 'TEAM' then (select TEAM_NAME from T_USER_TEAM t where t.id=b.user_id)
end as user_name,
b.content
from T_BUSSINESS b
where
case b.user_type
when 'PERSON' then (select USER_NAME from T_USER_PERSON u where u.id=b.user_id)
when 'ENTERPRISE' then (select COMPANY_NAME from T_USER_COMPANY o where o.id=b.user_id)
when 'CHARITY' then (select ORGANIZATION_NAME from T_USER_ORGANIZATION c where c.id=b.user_id)
when 'TEAM' then (select TEAM_NAME from T_USER_TEAM t where t.id=b.user_id)
end like '測試%'
order by b.id desc
 
-------------------
 
但是,好景不長,又遇到了問題……
 
新的需求來了,在外面又多了一層查詢:
有多種相似的業務,想要放在一起進行排序展示。(這回我學“聰明”了,不考慮視圖了)這樣,就需要嵌套兩層case-then語句……,先不考慮效率問題,這樣冗長的SQL的可讀性和維護性也太差了吧?
 
又經過再三權衡,我還是決定在業務表里增加一個user_name字段,雖然這是個冗余字段,但會使邏輯更簡單,更易於維護。至於user_name數據一致性的問題,解決方法有很多:觸發器/異步通知/同步更新等等,都可以實現。畢竟user_name並不是經常修改的。
最終的業務表是這樣的(當然,其他的業務表也都增加了USER_NAME字段):
T_BUSSINESS(ID, USER_ID, USER_TYPE, USER_NAME, CONTENT, ...)
 
最后看起來大概是這個樣子的:

 

 
至少,以我目前有限的認識,這樣做是最佳方案,希望有更好方法的同學不吝賜教。
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM