zoukankan      html  css  js  c++  java
  • 【原】对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, ...)
     
    最后看起来大概是这个样子的:
     
    至少,以我目前有限的认识,这样做是最佳方案,希望有更好方法的同学不吝赐教。
     
     
     
     

  • 相关阅读:
    Container Attached Storage: A primer
    1.3-Air302(NB-IOT)-新建lua文件和lua文件的变量,函数调用
    STM32+ESP8266+AIR202/302基本控制篇-114-功能测试-APP扫码绑定Air302(NB-IOT),并通过MQTT和Air302(NB-IOT)实现远程通信控制
    1.2-Air302(NB-IOT)-刷固件,下载和运行第一个lua程序
    1.1-Air302(NB-IOT)-硬件使用说明
    006-STM32+ESP8266+AIR202/302基本控制方案(阿里云物联网平台)-阿里云物联网平台加入规则引擎(云产品流转),让两个MQTT设备之间实现通信
    004-在阿里云物联网平台上动态注册设备(基于Android)
    30-STM32+W5500+AIR202基本控制篇-功能优化-MQTT服务器配置SSL单向认证(Linux系统)
    30-STM32+W5500+AIR202基本控制篇-功能优化-MQTT服务器配置SSL单向认证(.Windows系统)
    数据处理思想和程序架构: 单片机stm32的flash保存数据优化方案(让擦写次数达到上百万至上千万次)
  • 原文地址:https://www.cnblogs.com/wangjuns8/p/7865174.html
Copyright © 2011-2022 走看看