注:本文使用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, ...)
 
最后看起来大概是这个样子的:

 
至少,以我目前有限的认识,这样做是最佳方案,希望有更好方法的同学不吝赐教。
 
 
 
 

最新文章

  1. 详解tintColor属性
  2. 2014 39th ACM-ICPC 西安赛区 总结
  3. Juery Ajax语法
  4. 使用Template控制Editor显示方式
  5. Linux shell misc
  6. Matlab手册翻译
  7. 非常实用的10个PHP高级应用技巧
  8. 非常全面的java基础笔试题
  9. docker4dotnet
  10. SetConsoleTitle 函数--设置控制台窗口标题
  11. [Micropython]TPYBoardV102 Dfu固件烧写教程
  12. HDU 6348 序列计数 (树状数组 + DP)
  13. lua模块demo(redis,http,mysql,cjson,本地缓存)
  14. shell中的dd命令使用详解
  15. Itellij Idea全局搜索
  16. Python学习笔记第十八周
  17. Zabbix实战-简易教程--大型分布式监控系统实现Agent批量快速接入
  18. Python Django性能测试与优化指南
  19. 最长公共子序列(POJ1458)
  20. Redis实战(五)

热门文章

  1. UVA 10340 - All in All 水~
  2. P2P系统哪家强,功能其实都一样
  3. 【例题 6-7 UVA - 122 】Trees on the level
  4. POJ 1745 Divisibility DP
  5. 1、第一课 register_chrdev和register_chrdev_region 创建知识
  6. windows关闭进程 批处理端口占用
  7. Android 获取签名证书的具体信息(Eclipse和Android studio通用)
  8. php thinkphp uploadify
  9. Thinking in UML 学习笔记(二)——UML核心视图之用例图
  10. 安装alien,DEB与RPM互换