mysql 索引详解

本博客主要内容有:BTree与B+Tree 对比,mysql聚簇索引与非聚簇索引 ,索引的离散性,联合索引,常用 Mysql 语句案例,mysql 存储过程案例

B-TREE

在这里插入图片描述
一个B-tree 是一棵多路绝对平衡查找树。所谓绝对平衡是指所有叶子节点都在同一层级。一颗B-TREE,相对于AVL树而言,减少了树高,减少了 IO次数。但是在mysql 中使用的是优化过的BTree,即B+TREE。

B+TREE

在这里插入图片描述
1.在B+tree 中,每个索引节点只存储关键字和指向子节点的索引,并没有存储数据。而B-tree,每个索引节点存储关键字,数据和指向子节点的索引。B+Tree 相比B-Tree而言,B+Tree中每个节点的关键字和子节点引用的比值为1:1,而B-Tree是2:3,B+TREE 每个节点可以存储更多的关键字
2.对于B+Tree 而言,只在叶子节点中存储所有记录,想要查找一个记录,必须要进行三次IO操作,而B-Tree的IO操作可能是一次,二次,三次。相对于B-Tree而言,B+Tree 更加稳定,方便后续调优
3.B+Tree中,叶子节点天然有序,当进行基于索引的排序会更加方便和基于索引的扫表能力会更强

mysql 索引

两种索引: InnoDB (聚簇索引)和MyISAM(非聚簇索引)
InnoDB 聚簇索引:与数据存放的位置一致,一张表只能存在一个聚簇索引。InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
一般来说不建议使用隐含字段作为主键.,当我们自定义主键时,字段长度只有4个字节,隐含主键不仅占据更多空间,而且会使行锁升级为表锁,降低 了并发性。
Innodb 创建的索引会生成两个文件,一个是以.frm结尾的表结构文件,另一个是.ibd结尾的索引树
主键索引:

在这里插入图片描述
** 辅助索引(二级索引,非聚簇索引):**
在这里插入图片描述
辅助索引(Secondary Index, 也就是非主键索引):
在叶子节点中会存储辅助索引列值+主键,并指向对应的行。
好处:1.innodb在移动行时无须更新行指针。
缺点:使用主键值会让辅助索引占据更多的空间。
所以,如果主键定义的比较大,其他索引也将很大。如果想在表上定义 、很多索引,则争取尽量把主键定义得小一些。同时InnoDB 不会压缩索引。
因此,在使用Innodb引擎时如果索引和sql编写不当往往会触发回表操作。
MyISAM:
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。
在这里插入图片描述

辅助索引
在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。创建MyIsam索引会生成三个数据文件,.frm的表结构文件,.myi的索引树文件,.myd中存储的实际数据
在这里插入图片描述

索引的离散性

无论是创建Innodb索引还是MyIsam索引,都会要求索引列的离散性要尽可能的好。其中离散性比值=count(distinct(col)):count(col)
如果离散性比值大于1:10,说明这个索引的离散性不错。
为什么要求索引的离散性尽可能的好呢?
因为索引离散性的好坏会影响优化器是否走索引。如果离散性太差,优化器会认为走索引的效果和全表扫描差不多,那么就不会走索引而去进行全表扫描了。

这边来记录一个问题,面试的时候也常考。
为啥Innodb的主键要设置为自增主键?

1.不设置主键采用默认主键时占据更多内存,降低并发度
2.在Innodb的叶子结点中是有序的,如果不采用自增主键的时候,会导致页的分离与合并,产生IO,降低性能。(页是Innodb的磁盘存储单位)这条是主要原因
3.离散性

讲解的很不错的博客:
mysql 聚簇索引原理
innodb 与MyISAM索引实现原理
回看:https://www.cnblogs.com/shijingxiang/articles/4743324.html

联合索引

联合索引:用多个列共同创建索引
联合索引与单列索引相比较,相当于把多棵单列索引数变成一棵,节省空间和提升效率。
在联合索引中,假设对(a,b,c)三列建立联合索引,利用索引查找时会先比较a,如果a相等然后再去比较b。
所以在联合索引中会有最左匹配原则。即如果where条件中没有a列,只有b,c两列的判断条件,那么联合索引会失效。
除此之外,在联合索引中如果出现了范围查询,那么后续索引也会失效。
但是在单列索引中,如果出现范围查询,是可以使用索引的。
举个例子吧,看下图:
在这里插入图片描述
在第一个中只会使用name索引,其他索引失效。
在第二个中也会使用name索引,其他索引失效。
在第三个中不使用索引。
在第四个中会使用两个索引。原因:mysql中的优化器会把数据进行调换,将离散性好的数据放在最左边。所以我们在写sql的时候,应该尽量按照索引顺序来,避免优化器的调换。

覆盖索引

所谓覆盖索引是指当我们执行一个sql的时候可以不用去主键索引树中查询行 (不执行回表操作)即能拿到所需要的数据。
使用覆盖索引可以加快查询速度。
在这里插入图片描述
1.用到了,没有去主键索引树中 查询
2.没有,因为select* 是查找所有列。所以为了执行效果,尽量不要写select *。
3.用到了。在username的索引树中会存储列信息和主键索引值
4,没用到
5.用到了。

mysql易错点

when 与group by,having
1 when,group by ,having 可以一起使用,when作用于group by 之前,having作用group by 之后,且必须和group by 同时出现。此时group by 会对when 中经过限制条件选择之后结果进行分组,而having 则是对分组之后的结果添加限制条件,从分组之后的结果中选择出满足条件的记录。
2 having其后所接一般为聚集函数,除having后聚集函数中出现的属性字段外,select语句后的属性字段必须全部出现在group by 子句中。而when后面不能跟 聚集函数作为条件。
**case when **

update empc set sal=case when sal>2000 and  sal<=4600 then sal*1.15
                         when sal>4600 then sal*0.9
                         else sal end; 

  此种用法类似 if ...else 当第一个条件满足时便不会执行 第二个,如果前面条件都不满足时执行else条件
   select cname,case when cource='数学'  then score else 0 end 数学,
        case when cource='语文'  then score else 0 end 语文,
        case when cource='英语'  then score else 0 end 英语
       from tb ;

行变列

select cname,case when cource='数学'  then score else 0 end 数学,
      case when cource='语文'  then score else 0 end 语文,
      case when cource='英语'  then score else 0 end 英语
     from tb ;
``
对每个记录都进行判断,满足任何一个casewhen的条件的都会作为结果放在名为该casewhen 属性字段下。

**top n**

关键词 limit,limit 开始位,选取个数。最好用于无并列情况。当选取最大者(最小者)时,limit 0,1。可以放在子查询中,主查询中。

select deptno,dname from dept where deptno =(select deptno from emp group by deptno order by count(deptno) desc limit 0,1);

子查询几乎可以出现在查询语句的任何位置,但是,当子查询作为查询源和属性字段出现时必须起别名。
mysql 在子查询方面性能比较糟糕.
例如 select * from a where a.id in(select id from b where b.id in (1,2,3))
mysql 会将外层的结果先查询出来然后逐条执行子查询,如果外层结果集很大会导致很慢。

存储过程案例及使用及调试:

CREATE OR REPLACE PROCEDURE P_ETL_F_DFNSBQ_TOP1000  (V_MONTH   IN VARCHAR2,
                                                     V_RETCODE OUT VARCHAR2,
                                                     V_RETINFO OUT VARCHAR2) IS
  /**********************************************************
   *名    称 --%@NAME:P_ETL_F_DFZFPM_TOP1000
   *功能描述 --%@COMMENT:抽取地方级一般公共预算排名前一千的企业
   *执行周期 --%@PERIOD:全量
   *参    数 --%@PARAM:V_BBQ       帐期 YYYYMMDD
   *参    数 --%@PARAM:V_RETCODE    过程执行结果代码(成功:SUCCESS/失败:FAIL/等待:WAIT),输出参数
   *参    数 --%@PARAM:V_RETINFO    过程执行结果描述,输出参数
   *创 建 人 --%@CREATOR: WKS
   *创建时间 --%@CREATED_TIME: 2020-04-20
   *备    注 --%@REMARK:
   *修改记录 --%@MODIFY: 20200505 名称取时间段内最新的 WKS
   *来 源 表 --%@FROM:
   *目 标 表 --%@TO:
   *--字段映射
  ***********************************************************/
  V_USER VARCHAR2(50); --过程当前用户
  V_BBQ  VARCHAR2(100);
  V_ROWCOUNT   NUMBER; --用于保存记录数
  V_PROCNAME   VARCHAR2(1000); --过程名
  V_TABLE_NAME VARCHAR2(1000); --表名
  V_START      DATE;
  --V_YEAR       VARCHAR2(8);
  S_DAY        VARCHAR2(10):=TO_CHAR(TRUNC(TO_DATE(V_MONTH,'YYYY/MM'),'YY'),'YYYYMMDD');
  S_LAST_DAY   VARCHAR2(10):=TO_CHAR(LAST_DAY(TO_DATE(V_MONTH,'YYYY/MM')),'YYYYMMDD');
  S_DAY1       VARCHAR2(10):=TO_CHAR(ADD_MONTHS(TRUNC(TO_DATE(V_MONTH,'YYYY/MM'),'YY'),-12),'YYYYMMDD');
  S_LAST_DAY1  VARCHAR2(10):=TO_CHAR(LAST_DAY(ADD_MONTHS(TRUNC(TO_DATE(V_MONTH,'YYYY/MM'),'MM'),-12)),'YYYYMMDD');--20200507 wks 先加月再求月底日期,防止2月28 29日


BEGIN

  V_USER       := 'DC_MID';
  V_PROCNAME   := 'P_ETL_F_DFNSBQ_TOP1000';
  V_TABLE_NAME := 'F_BI_IN_DFNSBQ_TOP1000';
  V_START      := SYSDATE;
  --V_YEAR       := SUBSTR(V_MONTH, 1, 4);
  V_BBQ        :=V_MONTH;
  
  P_INSERT_ETL_LOG(V_BBQ,
                   V_USER,
                   V_PROCNAME,
                   V_TABLE_NAME,
                   V_START,
                   '',
                   V_ROWCOUNT,
                   V_RETCODE,
                   V_RETINFO,
                   '');

  IF 1 = 1 THEN
   DELETE FROM M_BI_IN_DFNSBQ_TOP1000 WHERE ACCT_YEAR=V_BBQ;
   COMMIT;

   INSERT INTO M_BI_IN_DFNSBQ_TOP1000
    SELECT V_BBQ ACCT_YEAR, PRIMARY_ID, XZQH, HY_DM, RN
      FROM (SELECT PRIMARY_ID,
                   XZQH,
                   HY_DM,
                   ROW_NUMBER() OVER(PARTITION BY 1 ORDER BY AMT DESC) RN
              FROM (SELECT PRIMARY_ID,
                           --MAX(DECODE(XZQH_RN, 1, XZQH, NULL)) XZQH,
                           MAX(CASE
                                 WHEN SDS_AMT = 0 THEN
                                  DECODE(XZQH_RN, 1, XZQH, NULL)
                                 ELSE
                                  DECODE(SDS_RN, 1, XZQH, NULL)
                               END) XZQH,
                           MAX(DECODE(HY_RN, 1, HY_DM, NULL)) HY_DM,
                           MAX(AMT) AMT
                      FROM (SELECT PRIMARY_ID,
                                   XZQH,
                                   HY_DM,
                                   ROW_NUMBER() OVER(PARTITION BY PRIMARY_ID ORDER BY AMT_XZQH DESC) XZQH_RN,
                                   ROW_NUMBER() OVER(PARTITION BY PRIMARY_ID ORDER BY AMT_HY DESC) HY_RN,
                                   ROW_NUMBER() OVER(PARTITION BY PRIMARY_ID ORDER BY SDS_XZQH DESC) SDS_RN,
                                   SDS_AMT,
                                   AMT
                              FROM (SELECT /*+ PARALLEL(A,20)*/
                                     A.PRIMARY_ID,
                                     A.DIVISIONS XZQH,
                                     A.IND_PHY HY_DM,
                                     SUM(NVL(CITY_DIV_TRAAMT, 0) +
                                         NVL(COUNTY_DIV_TRAAMT, 0)) OVER(PARTITION BY A.PRIMARY_ID, A.DIVISIONS) AMT_XZQH,
                                     SUM(NVL(CITY_DIV_TRAAMT, 0) +
                                         NVL(COUNTY_DIV_TRAAMT, 0)) OVER(PARTITION BY A.PRIMARY_ID, A.IND_PHY) AMT_HY,
                                     SUM(DECODE(SUBSTR(BUDGETSUBJECTCODE, 1, 7),
                                                'AA10104',
                                                NVL(CITY_DIV_TRAAMT, 0) +
                                                NVL(COUNTY_DIV_TRAAMT, 0),
                                                0)) OVER(PARTITION BY A.PRIMARY_ID, A.DIVISIONS) SDS_XZQH,
                                     SUM(NVL(CITY_DIV_TRAAMT, 0) +
                                         NVL(COUNTY_DIV_TRAAMT, 0)) OVER(PARTITION BY A.PRIMARY_ID) AMT,
                                     SUM(DECODE(SUBSTR(BUDGETSUBJECTCODE, 1, 7),
                                                'AA10104',
                                                NVL(CITY_DIV_TRAAMT, 0) +
                                                NVL(COUNTY_DIV_TRAAMT, 0),
                                                0)) OVER(PARTITION BY A.PRIMARY_ID) SDS_AMT
                                      FROM F_BI_IN_RHDZSP_CX_DAY_HZ A
                                     WHERE A.APPLYDATE BETWEEN S_DAY AND S_LAST_DAY
                                       AND A.PRIMARY_ID NOT LIKE '%11111111'
                                       /*AND ASCIISTR(A.TAXPAYNAME) LIKE '%\%'*/
                                       AND NOT REGEXP_LIKE(A.TAXPAYNAME, '(^[A-Za-z]+$)')--20200506 wks 修改为只剔除全英文企业
                                       AND A.BUDGETSUBJECTCODE LIKE 'A%'))
                     GROUP BY PRIMARY_ID))
     WHERE RN <= 1000;

    --名称取时间段内最新的--WKS-20200505
    DELETE FROM M_BI_IN_DFNSBQ_TOP1000_QYXX;
    COMMIT;
    INSERT INTO M_BI_IN_DFNSBQ_TOP1000_QYXX
      SELECT TTT.PRIMARY_ID, TTT.TAXPAYNAME, TTT.TAXPAYCODE
        FROM (SELECT DISTINCT HZ.PRIMARY_ID,
                              HZ.TAXPAYNAME,
                              HZ.TAXPAYCODE,
                              HZ.APPLYDATE,
                              ROW_NUMBER() OVER(PARTITION BY HZ.PRIMARY_ID ORDER BY HZ.APPLYDATE DESC) RN
                FROM M_BI_IN_DFNSBQ_TOP1000 TT, F_BI_IN_RHDZSP_CX_DAY_HZ HZ
               WHERE TT.PRIMARY_ID = HZ.PRIMARY_ID
                 AND HZ.APPLYDATE BETWEEN S_DAY AND S_LAST_DAY
                 AND HZ.BUDGETSUBJECTCODE LIKE 'A%'
                 AND TT.ACCT_YEAR = V_MONTH) TTT
       WHERE RN = 1;
    COMMIT;

    DELETE FROM DC_APP.F_BI_IN_DFNSBQ_TOP1000@BJCZ161 A WHERE A.ACCT_MONTH=V_BBQ;
    COMMIT;

    INSERT INTO DC_APP.F_BI_IN_DFNSBQ_TOP1000@BJCZ161
      SELECT V_MONTH ACCT_MONTH,
             A1.RN,
             A1.PRIMARY_ID,
             QYBC.TAXPAYNAME NSRMC,--名称取时间段内最新的--WKS-20200505
             B1.SK_GKCODE XZQH,
             B1.ORGNAME XZQH_NAME,
             NVL(C1.CODE1, 'VVVV') HY_DM,
             NVL(C1.NAME1, '第三产业其他') HY_NAME,
             A1.B_AMT,
             A1.S_AMT,
             A1.CENT_AMT,
             A1.CITY_AMT,
             A1.COUNTY_AMT,
             A1.S_CENT_AMT,
             A1.S_CITY_AMT,
             A1.S_COUNTY_AMT
        FROM (SELECT /*+ PARALLEL(A,20) USE_HASH(A,B)*/
               RN,
               PRIMARY_ID,
               MAX(NSRMC) NSRMC,
               XZQH,
               HY_DM,
               SUM(B_AMT) B_AMT,
               SUM(S_AMT) S_AMT,
               SUM(CENT_AMT) CENT_AMT,
               SUM(CITY_AMT) CITY_AMT,
               SUM(COUNTY_AMT) COUNTY_AMT,
               SUM(S_CENT_AMT) S_CENT_AMT,
               SUM(S_CITY_AMT) S_CITY_AMT,
               SUM(S_COUNTY_AMT) S_COUNTY_AMT
                FROM (SELECT B.RN,
                             A.PRIMARY_ID,
                             MAX(A.TAXPAYNAME) NSRMC,
                             B.XZQH,
                             B.HY_DM,
                             SUM(TRAAMT) B_AMT,
                             0 S_AMT,
                             SUM(A.CENT_DIV_TRAAMT) CENT_AMT,
                             SUM(A.CITY_DIV_TRAAMT) CITY_AMT,
                             SUM(A.COUNTY_DIV_TRAAMT) COUNTY_AMT,
                             0 S_CENT_AMT,
                             0 S_CITY_AMT,
                             0 S_COUNTY_AMT
                        FROM F_BI_IN_RHDZSP_CX_DAY_HZ A,
                             M_BI_IN_DFNSBQ_TOP1000   B
                       WHERE A.APPLYDATE BETWEEN S_DAY AND S_LAST_DAY
                         AND A.PRIMARY_ID = B.PRIMARY_ID
                         AND B.ACCT_YEAR = V_MONTH
                         AND A.BUDGETSUBJECTCODE LIKE 'A%'
                       GROUP BY B.RN, A.PRIMARY_ID, B.XZQH, B.HY_DM
                      UNION ALL
                      SELECT /*+ PARALLEL(A,20) USE_HASH(A,B)*/
                       B.RN,
                       A.PRIMARY_ID,
                       MAX(A.TAXPAYNAME),
                       B.XZQH,
                       B.HY_DM,
                       0 B_AMT,
                       SUM(TRAAMT) S_AMT,
                       0,
                       0,
                       0,
                       SUM(A.CENT_DIV_TRAAMT) S_CENT_AMT,
                       SUM(A.CITY_DIV_TRAAMT) S_CITY_AMT,
                       SUM(A.COUNTY_DIV_TRAAMT) S_COUNTY_AMT
                        FROM F_BI_IN_RHDZSP_CX_DAY_HZ A,
                             M_BI_IN_DFNSBQ_TOP1000   B
                       WHERE A.APPLYDATE BETWEEN S_DAY1 AND S_LAST_DAY1
                         AND A.PRIMARY_ID = B.PRIMARY_ID
                         AND B.ACCT_YEAR = V_MONTH
                         AND A.BUDGETSUBJECTCODE LIKE 'A%'
                       GROUP BY B.RN, A.PRIMARY_ID, B.XZQH, B.HY_DM)
               GROUP BY RN, PRIMARY_ID, XZQH, HY_DM) A1,
             D_IN_ORGCODE_17 B1,
             (SELECT * FROM D_HY_QM T WHERE T.DHY_HYZT = '01') C1,
             M_BI_IN_DFNSBQ_TOP1000_QYXX QYBC--名称取时间段内最新的--WKS-20200505
       WHERE A1.XZQH = B1.SK_GKCODE(+)
         AND A1.HY_DM = C1.CODE1(+)
         AND A1.PRIMARY_ID=QYBC.PRIMARY_ID;
    COMMIT;


    V_RETCODE := 'SUCCESS';
    V_RETINFO := '执行成功!';

    P_UPDATE_ETL_LOG(V_BBQ,
                     V_USER,
                     V_PROCNAME,
                     V_TABLE_NAME,
                     V_START,
                     SYSDATE,
                     V_ROWCOUNT,
                     V_RETCODE,
                     V_RETINFO,
                     '');
  END IF;

EXCEPTION
  WHEN OTHERS THEN
    V_RETCODE := 'FAIL';
    V_RETINFO := SQLERRM;
    P_UPDATE_ETL_LOG(V_BBQ,
                     V_USER,
                     V_PROCNAME,
                     V_TABLE_NAME,
                     V_START,
                     SYSDATE,
                     V_ROWCOUNT,
                     V_RETCODE,
                     V_RETINFO,
                     '');

END;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值