美文网首页mysql技巧与性能优化
excel数据导出sql优化(一)

excel数据导出sql优化(一)

作者: 灰袍甘道夫 | 来源:发表于2017-11-20 18:25 被阅读0次

    excel数据导出sql优化分2篇,本文有sql优化篇

    假设有A,B,C,D,E5张表,需要导出这5张表的部分信息,查询条件只涉及到A,B,C的相关字段,约定A,B,C,D,E对应关系都为1对1.
    A表字段:ID,TYPEA,NAMEA....;
    B表字段:ID,AID,TYPEB,NAMEB...;(AID为A表主键)
    C表字段: ID,AID,BID,TYPEC,NAMEC...;(AID为A表主键,BID为B表主键)
    D表字段:ID,AID,BID,TYPED,NAMED...;(AID为A表主键,BID为B表主键)
    E表字段:ID,AID,BID,TYPEE,NAMEE...;(AID为A表主键,BID为B表主键)
    如何导出?
    1 简单的实现步骤如下:
    1>left join A.B.C,获得ABC的相关信息.

    <select id="getABC" ......>
        select  *  from A left join B on A.ID  = B.AID
        left join C on C.BID = B.AID and C.AID = B.AID
        <where>
          <if test="TYPEA != NULL  and TYPEA!=''">
            and A.TYPEA = #{TYPEA}
          </if>
         <if test="TYPEB != NULL and TYPEB!='' ">
            and B.TYPEB = #{TYPEB}
          </if>
         <if test="NAMEC!= NULL and NAMEC !='' ">
            and C.NAMEC   CONCAT('%','${NAMEC}','%' )
          </if>
        </where>
    </select>
    

    2>循环获得DE数据,然后在组装excel信息

    /** 伪代码 **/
    public void getABCDEList(ABC abc) {
        List<ABCDE> resultList = new ArrayList<>();
        List<ABC> listABC = ABCMapper.getABCList(ABC abc);
        for(ABC abc:listABC){
            D d = DMappger.getDByID(abc.AID, abc.BID);
            E e = EMappger.getDByID(abc.AID, abc.BID);
            ABCDE abcde = new ABCDE();
            //TODO 根据abc,d,e组装abcde数据
            resultList.add(abcde);
        }
        //TODO excel一次性写入skip,resultList
    }
    

    初看起来,也没什么问题,但是当数据量快速增涨的时候,发现excel导出的时间会比较慢,而且当数据条数过W的时候,甚至会出现导出空页的情况。
    其实这种简单的写法有3个问题:
    1>一次性导出数据可能会OOM,换成分批写入excel
    2>left join效率低下,尤其是2张表以上,select * 的写法应该具体到对应的字段
    3>for循环获取数据会循环调用sql,执行时间长
    针对上述3个问题我们做进一步的优化,称为优化版本:
    1> 针对sql条件我们做优化,拆分为2个查询方法,并且加上分页,去掉*用法,left join去掉
    方法一: getAB

    <select id="getAB" ......>
       select  A.ID as AID,A.TYPEA,A.NAMEA,
       B.ID as BID, B.TYPEB,B.NAMEB from 
      A , B  
       <where>
         AND A.ID  = B.AID
         <if test="TYPEA != NULL  and TYPEA!=''">
           and A.TYPEA = #{TYPEA}
         </if>
        <if test="TYPEB != NULL and TYPEB!='' ">
           and B.TYPEB = #{TYPEB}
         </if>
       </where>
    order by A.ID desc
    limit #{skip},#{limit}
    </select>
    

    方法二:getABC

    <select id="getABC" ......>
        select  A.ID as AID,A.TYPEA,A.NAMEA,
       B.ID as BID, B.TYPEB,B.NAMEB ,
      C.ID as CID, C.TYPEC,C.NAMEC ,
      from A,B,C
      from A left join B on 
      left join C on 
        <where>
           and A.ID  = B.AID
           and C.BID = B.AID 
          and C.AID = B.AID
          <if test="TYPEA != NULL  and TYPEA!=''">
            and A.TYPEA = #{TYPEA}
          </if>
         <if test="TYPEB != NULL and TYPEB!='' ">
            and B.TYPEB = #{TYPEB}
          </if>
         <if test="NAMEC!= NULL and NAMEC !='' ">
            and C.NAMEC   CONCAT('%','${NAMEC}','%' )
          </if>
        </where>
      order by A.ID desc
      limit #{skip},#{limit}
    </select>
    

    2>循环自分页获取数据

    /** 伪代码 **/
    public void  getABCDEList(ABC abc) {
        List<ABCDE> resultList = new ArrayList<>();
        List<ABC> listABC = new ArrayList<>();
       //是否需要获取C信息
        boolean needC =true;
        //对比测试50,100,200,300,400,500,1000条,发现200效果更佳执行
        //总时间最短
        int skip = 0;
        int limit = 200; 
        int pageNum = 1;
        boolean hasNext = true;
        while(hasNext) {
            //NAMEC存在
            skip = (pageNum - 1) * limit;
            //根据查询条件调用对应的方法
            if(null != abc.NAMEC  && "".equls(abc.NAMEC)) {
             listABC = ABCMapper.getABCList(abc,skip,limit);
             needC = false;
           } else {
             listABC = ABCMapper.getABList(abc, skip,limit);
           }
          //调用批量获取list方法
          resultList = getABCDEList(listABC);
         //TODO excel分批次写入文件
        if(listABC.size()  <  limit)
           hasNext = false;
       } else {
         //分页自动加1
          pageNum += 1;
      }
        return resultList;
    }
    

    方法二:根据listABC获得List<ABCDE>

    private List<ABCDE>  getABCDEList(List<ABC> listABC){
      //循环获取A.B 对应ID信息
          List<Integer> AIDS = new ArrayList<>();
          List<Integer> BIDS = new ArrayList<>();
         for(ABC abc:listABC) {
            AIDS.add(abc.AID);
            BIDS.add(abc.BID);
         }
        // 批量获取C.D.E数据
         List<C> listC = new ArrayList<>();
         List<D> listD = new ArrayList<>();
         List<E> listE = new ArrayList<>();
         //按照AID+BID放置到对应的map结构里
         Map<String,C> mapC = new HashMap<>();
         Map<String,D> mapD = new HashMap<>();
         Map<String,E> mapE = new HashMap<>();
         //数据获取和组装
          if(needC){
             listC = CMapper.getListCByIDS(AIDS,BIDS);
             for(C c:listC) {
                mapC.put(String.valueOf(c.AID) + "-" + String.valueOf(c.AID),c);
              }
         }
        listD = DMapper.getListCByIDS(AIDS,BIDS);
        listE = EMapper.getListCByIDS(AIDS,BIDS);
        for(D d:listD) {
                mapD.put(String.valueOf(d.AID) + "-" + String.valueOf(d.AID),d);
         }
        for(E e:listE) {
                mapE.put(String.valueOf(e.AID) + "-" + String.valueOf(e.AID),e);
        }
         //TODO 根据CDE 对应ID
        for(ABC abc:listABC){
            ABCDE abcde = new ABCDE();
            //TODO 根据abc,(mapC),mapD,mapE装abcde数据
            resultList.add(abcde);
        }
       return resultList;
    }
    

    后记:关于excel写入分批写入的优化,会开专门的篇幅概述,敬请期待!

    相关文章

      网友评论

        本文标题:excel数据导出sql优化(一)

        本文链接:https://www.haomeiwen.com/subject/zswvvxtx.html