ITPub博客

首页 > 数据库 > MySQL > sql风暴来袭

sql风暴来袭

原创 MySQL 作者:bestpaydata 时间:2015-12-10 15:16:21 0 删除 编辑


written by : 小旋风

1:过滤database中所有table的重复的索引

    前奏:记得小编刚入职时,就摊上事了,审核sql,其中有审核库中重复的索引及索引命名规范,公司没有自动化审核工具,n张表让小编情何以堪,虽说男人男人哭吧哭吧不是罪。
          果不其然,审核不多会,两眼果断直了,烦躁的一米。转念一想,小编也是DBA开发出身的,多大点事,自动化sql搞起...... 经过炼狱般的大脑风暴,耳边想起悦耳的节奏,搞定

   过滤重复的索引思路:
       1: 找到一个索引的所有子索引。           比如一个索引的索引字段为(a,b,c)对应的子索引(a,b)(a)
       2: 确定当下索引是直接索引还是间接索引。  比如 上面 (a,b,c)是直接索引 ,(a,b)(a) 是间接索引
      3:分组过滤,组成员个数大于等于2且组中必须有一个直接索引。

具体的sql:
  

点击(此处)折叠或打开

  1.     select table_name 表名,group_concat(index_name separator ' ; ') 重复索引列表 ,count(*)-1 重复次数,idx_name 索引列 from
  2.         (
  3.             select a.table_name ,a.index_name,a.column_name ,a.seq_in_index,a.idx_name,if(b.max_seq_index=a.seq_in_index,1,0)max_seq_index from
  4.                 (
  5.                     select table_name ,index_name,column_name ,seq_in_index
  6.                                 ,if(index_name=@befo_index_name,@_column_names:=concat(@_column_names ,',',column_name),@_column_names:=column_name)idx_name
  7.                                 ,@befo_index_name:=index_name
  8.                     from information_schema.statistics ,(select @befo_index_name:='',@_column_names:='')b
  9.                     where table_schema=database_name
  10.                     order by table_name ,index_name ,seq_in_index
  11.                 )a ,
  12.                 (
  13.                     select table_name ,index_name,max(seq_in_index) max_seq_index
  14.                     from information_schema.statistics
  15.                     where table_schema=database_name
  16.                     group by table_name ,index_name
  17.                 ) b
  18.             where a.table_name =b.table_name and a.index_name=b.index_name
  19.         ) c
  20.     group by table_name,idx_name
  21.     having count(*)>1 and sum(max_seq_index)>=1;




      说明:database_name :指定你要检查的database
   执行结果:

       
   
   2:在线清除大表数据
            前奏:小编自从上次自动化搞定安逸了好久,然有一天zabbix 报zabbix server 磁盘空间不足,经过排查是zabbix 的history_uint 20G 导致的,当然了磁盘存储也不给力。
               只见BOSS 雷厉风行 干掉上过月的数据,那就动起来呗。

             需求:在线批量删除大表大量数据
             分析:删除大量数据会走全表扫描,会长时间占用 数据库主机大量的 IO ,CUP ,内存。  事必会长时间影响线上的应用的响应时间。甚至是灾难性的。

            结论:采用的策略: “化整为零,各个击破” ,
            具体实现: 每间1秒隔删除固定行数比如1W 条记录,往复循环,直至把需要数据都清除掉
            优点:      
                     1: 每隔一秒删除给予线上应用的喘息的机会,对前端应用影响较小。
                     2: 要是一条删除sql搞定,执行到一半 失败了,光回滚的时间及给系统再次负载够你喝一壶的,最最不爽的是
                          执行这么长时间,数据还是没有被删除 想想就都心碎了。
                     3:可以查看删除记录数的进度。
        类比:同样在线批量修改同理
        存储过程实现:

点击(此处)折叠或打开

  1. CREATE  PROCEDURE `p_batch_delete`()
  2. BEGIN
  3.   set @start_date=now() ;
  4.   set @num=0;
  5.   -- 往复干掉满足条件的1W行数据
  6.     lab:while true do
  7.        delete from history_uint where clock <UNIX_TIMESTAMP('2015-10-26') limit 10000;
  8.        -- 记录删除的影响的行数时间复杂度为1
  9.        set @count=row_count();
  10.        -- 数据已被全部干掉,则退出 循环体
  11.        if @count=0 then
  12.            leave lab;
  13.        else
  14.            -- 累加删除记录数
  15.             set @num=@num+@count;
  16.            -- 每干掉200000记录,记录一条数据,便于后续查看删除进度
  17.                         if mod(@num,200000)=0 then
  18.                insert into delete_progresse (records,time)values( @num,round((unix_timestamp(now())-unix_timestamp(@start_date))/1));
  19.                         end if;
  20.             -- 每干掉1W条记录停顿1S 给予前端应用喘息的机会
  21.             set @s=sleep(1) ;
  22.       end if;
  23.   end while ;
  24.   -- 满足条件的数据被全部干掉后,统计 删除行数,开始时间,结束时间
  25.   select @num delete_row ,@start_date start_date, now() end_date ;

  26. END;
3:实现自动化排班功能
   前奏:小编有一段时间在搞php开发,终于找回了久违的兴奋感。有天一妹子在群里发值班信息,说实在的那个排班表格,设计的那个完美,完美到想让它实现自动化
            还有妹子每次排班各种复制黏贴,想想都醉了,为什么不能让繁琐枯燥的事情变成愉悦的事情呢?再着搞IT的妹子本来就少,必须悉心呵护,小编果断上演一次英雄救美,好吧              是我想多了。下面分享下数据逻辑加工部分

  排班表格样式:       
组别 星期一 星期二 星期三 星期四 星期五 星期六 星期天
oracle 1李二,李三 2李五 3李七 4李八 5李九 6李十
mysql 1王五 2王六 3王一 4王二 5王三 6王四
oracle 7李十一 8李十二,李十三 9李一 10李二 11李三 12李五 13李七
mysql 7王五 8王六 9王一 10王二 11王三 12王四 13王五
oracle 14李八 15李九,李十 16李十一 17李十二 18李十三 19李一 20李二
mysql 14王六 15王一 16王二 17王三 18王四 19王五 20王六
oracle 21李三 22李五,李七 23李八 24李九 25李十 26李十一 27李十二
mysql 21王一 22王二 23王三 24王四 25王五 26王六 27王一
oracle 28李十三 29李一,李二 30李三 31李五
mysql 28王二 29王三 30王四 31王五
    心得体会:
             1:一口吃不成胖子,怎么办?逆向思维拆分;正向思维验证
             2:寻找规律,怎么着?  观察 ;模仿人的行为,抽象出规律。
             3:不要为了解决一个问题而解决问题,试着总结出解决问题的统一方法论。
                  
   分析:通过观察其实这个是行列转换一个典型的例子。
             逆向思维拆分:
                1:每一行数据都有2个相同的属性:组别,一年多少个星期。【观察得出规律】
                2:展示结果是由值班信息表(组别,值班人,值班日期)根据组别,一年多少个星期 分组得来的
                3:怎样得到值班信息表?经分析是有 日期表 和 值班设置表(排班根据值班人值班顺序排的,当然也可以设置请假时间区间)
                4:怎样得到日期表?一年365天存储到表里?2年呢?n年呢?这样要更新日期表,使用存储0-30一共31个数字数值表 生成任意年月的日期,一劳永逸。
            正向思维验证:
               1: 数值表----》日期表 + 排班设置表----》值班信息表表----》结果
             结论:思路正确

过程主要代码;根据某月第一天生成当月排班数据
           

点击(此处)折叠或打开

  1. -- 动态获取 oracle 和 mysql 组更有多少个成员
  2.   select sum(if(category=1,1,0)) , sum(if(category=2,1,0)) into oracle_count,mysql_count from duty_order ;

  3.   -- 保存oracle组当前月的值班数据
  4.   insert into tmp_duty_info(category,date,member)
  5.   select 1,c.date,group_concat(d.member order by c.date) member from
  6.         (
  7.                 select a.date , if(@duty_order=oracle_count,@duty_order:=f_get_duty_order(date,1,1,oracle_count),@duty_order:=f_get_duty_order(date,@duty_order+1,1,oracle_count)) duty_order
  8.                 from
  9.                         ( select DATE_ADD( datetime,INTERVAL num day) date from number
  10.                             where num<datediff(DATE_ADD( datetime,INTERVAL 1 month),datetime)
  11.                             union all
  12.                             select date
  13.                             from (select DATE_ADD( datetime,INTERVAL num day) date from number
  14.                                         where num<datediff(DATE_ADD( datetime,INTERVAL 1 month),datetime) )a
  15.                             where dayofweek(date)=3
  16.                             order by     date )a,
  17.                      ( select @duty_order:=b.duty_order duty_order from duty_info a ,duty_order b
  18.                             where a.member=b.member and a.date=DATE_ADD(datetime,INTERVAL -1 day) and a.category=1) b
  19.         )c join duty_order d on c.duty_order=d.duty_order and d.category=1
  20.              group by date ;


  21.   -- 保存mysql组当前月的值班数据
  22.   insert into tmp_duty_info(category,date,member)
  23.     select 2,c.date,d.member from
  24.     (
  25.             select a.date , if(@duty_order=mysql_count,@duty_order:=f_get_duty_order(date,1,2,mysql_count),@duty_order:=f_get_duty_order(date,@duty_order+1,2,mysql_count)) duty_order
  26.             from
  27.                     ( select DATE_ADD( datetime,INTERVAL num day) date from number
  28.                         where num<datediff(DATE_ADD( datetime,INTERVAL 1 month),datetime)
  29.                         order by     date )a,
  30.                  ( select @duty_order:=b.duty_order duty_order from duty_info a ,duty_order b
  31.                         where a.member=b.member and a.date=DATE_ADD(datetime,INTERVAL -1 day) and a.category=2) b
  32.     )c join duty_order d on c.duty_order=d.duty_order and d.category=2;



  33.      
  34.   -- 根据2组的当前月排班数据,加工数据
  35.         select
  36.                      if(category=1,'oracle','mysql') cate ,
  37.                      max(if(dayofweek(date)=2,concat(dayofmonth(date),member),'')) 星期一,
  38.                      max(if(dayofweek(date)=3,concat(dayofmonth(date),member),'')) 星期二,
  39.                      max(if(dayofweek(date)=4,concat(dayofmonth(date),member),'')) 星期三,
  40.                      max(if(dayofweek(date)=5,concat(dayofmonth(date),member),'')) 星期四,
  41.                      max(if(dayofweek(date)=6,concat(dayofmonth(date),member),'')) 星期五,
  42.                      max(if(dayofweek(date)=7,concat(dayofmonth(date),member),'')) 星期六,
  43.                      max(if(dayofweek(date)=1,concat(dayofmonth(date),member),'')) 星期天
  44.         from tmp_duty_info
  45.     group by weekofyear(date), category
  46.     order by date,weekofyear(date), category ;



处理值班人员请假的函数:模仿人为排班得出规律

点击(此处)折叠或打开

  1. BEGIN
  2.   declare duty_num int ;
  3.   
  4.   if cate=1 then
  5.             select duty_order into duty_num
  6.             from ( select a.*, if(duty_order>=duty,duty_order,duty_order+cate_count) dx from duty_order a
  7.              where category=1 order by duty_order
  8.                  )b
  9.             where (if(start_date!=''and end_date='','',start_date) = '' and if(end_date!=''and start_date='','',end_date) = '' )
  10.              or date>end_date or date<start_date order by dx limit 1 ;
  11.   else
  12.             select duty_order into duty_num
  13.             from ( select a.*, if(duty_order>=duty,duty_order,duty_order+cate_count) dx from duty_order a
  14.              where category=2 order by duty_order
  15.                  )b
  16.             where (if(start_date!=''and end_date='','',start_date) = '' and if(end_date!=''and start_date='','',end_date) = '' )
  17.             or date>end_date or date<start_date order by dx limit 1 ;
  18.  end if;

  19.   RETURN duty_num;
  20. END

4:n天之前,一个开发的哥们,向小编请教个问题,说是苦思良久,没有好的思路。暗自窃喜,嘿嘿,又要干一票了,不废话了,上干货,莱迪  GO  

  需要: 统计 当前月份 未签到的人员名单。 
  已知: user (user_name) 用户表
            sign 签到表 (user_name,date):只存储每天签到人员信息

 心得体会 
          1、模仿人的行为,抽象出设计思路
          2、基础知识和重要,可以灵光乍现出代码具体实现

 解析:有时拿到一个需求时,没有好的思路,就不要天马行空的想了,换个角度,如果这个需求是人来做,又该如何?
          当人的事情想明白了,思路也就水到渠成了。比如这道题,要是人工来判断11月份每天没签到的人,咋整?首先拿1号所有签到的人员名单和
          所有用户名单比对 得出1号未签到的人员名单,2号...以此类推得出结果。此时想到通过外连接再过滤可以得到匹配不到的记录。瞬间思路有了 ,代码具体实现也有了。

思路:1:当前月份所有人员的全勤 表a 和 签到表 b  外连接 过滤 -----》当前月没签到的人员
        2:
表 a 怎么获得 ? 当前月所有日期表c和用户表 笛卡尔积 
        3:表c 又如何获得? 数值表搞起

下面是个测试案例:

用户表:      
 mysql>  select * from user;
+----+-----------+
| id | user_name |
+----+-----------+
|  1 | 张三      |
|  2 | 李四      |
|  3 | 王五      |
+----+-----------+
3 rows in set

签到表:
mysql> select * from qiandao;
+-----------+---------------------+
| user_name | date                |
+-----------+---------------------+
| 张三      | 2015-10-14 00:00:00 |
| 王五      | 2015-10-15 00:00:00 |
| 李四      | 2015-10-14 00:00:00 |
+-----------+---------------------+
3 rows in set

数值表获取日期:

 mysql> select date_add( date_format('20151014','%Y%m%d') , INTERVAL id day) date,id from num ;
+------------+----+
| date       | id |
+------------+----+
| 2015-10-14 |  0 |
| 2015-10-15 |  1 |
| 2015-10-16 |  2 |
| 2015-10-17 |  3 |
| 2015-10-18 |  4 |
| 2015-10-19 |  5 |
| 2015-10-20 |  6 |
+------------+----+
7 rows in set


获取14、15 号没有签单的人员名单:

点击(此处)折叠或打开

  1. select b.date 日期 ,group_concat(b.user_name) 未签到人员
  2.  from qiandao a right join
  3.      (select a.user_name,b.date
  4.       from user a ,(select date_add( date_format('20151014','%Y%m%d') , INTERVAL id day) date from num limit 2)b)b
  5.  on a.date=b.date and a.user_name=b.user_name
  6.  where a.user_name is null
  7.  group by b.date;
结果展示:
+------------+------------+
| 日期       | 未签到人员 |
+------------+------------+
| 2015-10-14 | 王五       |
| 2015-10-15 | 张三,李四  |
+------------+------------+
2 rows in set




总结:小编说了这么多,其实只想揭示一个论点:授人以鱼不如授人以渔。拿恒定不变的渔去完胜变化莫测的鱼,以静制动,以不变应万变,
        御魔斩魔,遇佛成佛。


             

        

  
    



 


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/30109892/viewspace-1869762/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-01-19

  • 博文量
    126
  • 访问量
    1054947