关键时刻,第一时间送达

最近看到一个有趣的题目,拿出来和大家分享一下,我的解决方法可能比较笨,就此抛砖引玉希望能有更优的办法。

题目内容

有一批Oracle数据库中的表(超过1000张),表中的一些字段的指定字符需要修改成其他指定字符。表名是类似的,例如TABLE0001,TABLE0002,TABLE0003等等,但是表名并不是连续的,有可能没有表TABLE0100,TABLE0200,TABLE0300等等。字段名称也是类似的,例如FIELD001,FIELD002,FIELD003以此类推。表的字段名也不固定,有的可能只有1个字段,有的可能有100多个字段,而且有的表有可能没有数据。现在就是要把所有这些表里的字段中包含“张三”字符的内容全部修改成“李四”,字段中的其他字符不变,例如:“张三是个好学生”改成“李四是个好学生”。

看到这样的题目,脑海中想到的第一个办法就是写个游标让它重复执行。但是随着我仔细的深入,发现并没那么简单。(想看答案的可以直接跳到文末代码部分)问题分解如下:

表名不连续的问题
表名不连续就要判断这个表是否存在数据库当中,这时候想到的是从Oracle的系统表USER_TABLES(用户表)中找到这些表,然后再插入到一张临时表中,并且将表中的记录数也一起插入,方便过滤记录为0的表,减少后期的内容更新。

查询语句如下:

DECLARE CURSOR CURS ISSELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME LIKE 'TABLE%';v_sql VARCHAR2(4000);BEGIN    FOR CUR IN CURS LOOP    v_sql :=' SELECT  '||''CUR.TABLE_NAME''||',COUNT(1) T_CNT    INTO TEMP_TABLE FROM '||CUR.TABLE_NAME;    EXECUTE IMMEDIATE v_sql;    END LOOP;END;END;

很遗憾的是这里一直报错,错误原因是我想把变量的表名CUR.TABLE_NAME作为字符插入到临时表中,这样我查询临时表TEMP_TABLE就可以直接知道每个表的具体记录数,但是在动态SQL语句中就是不成功,只能另辟蹊径。

将遍历出来的表放在EXCEL里面自动生成我需要的查询语句,如下图:

图 EXCEL完成查询语句拼接

然后将这些拼接好的查询语句开始执行,即可将每张表的数据量查询出来并插入到临时表TEMP_TABLE中。第一步算顺利完成啦~

字段不固定
这个问题和表名不连续有点类似,但是也有不同的地方,我开始的做法是把所有列都给列出来,说做就做。

具体代码如下:

DECLARECURSOR mycur ISSELECT T_NAME FROM TEMP_TABLE WHERE T_CNT<>0 ;myrecord mycur%ROWTYPE;v_sql1 VARCHAR2(4000);v_sql2 VARCHAR2(4000);v_sql3 VARCHAR2(4000);v_sql4 VARCHAR2(4000);v_sql5 VARCHAR2(4000);BEGINOPEN mycur;LOOPFETCH mycur INTO myrecord;EXIT WHEN mycur%NOTFOUND;v_sql1 :='UPDATE '||myrecord.T_NAME||' SETFIELD001=REPLACE(FIELD001,''张三'',''李四'')';v_sql2 :='UPDATE '||myrecord.T_NAME||' SET FIELD002=REPLACE(FIELD002,''张三'',''李四'')';v_sql3 :='UPDATE '||myrecord.T_NAME||' SET FIELD003=REPLACE(FIELD003,''张三'',''李四'')';v_sql4 :='UPDATE '||myrecord.T_NAME||' SET FIELD004=REPLACE(FIELD004,''张三'',''李四'')';v_sql5 :='UPDATE '||myrecord.T_NAME||' SET FIELD005=REPLACE(FIELD005,''张三'',''李四'')';EXECUTE IMMEDIATE v_sql1;EXECUTE IMMEDIATE v_sql2;EXECUTE IMMEDIATE v_sql3;EXECUTE IMMEDIATE v_sql4;EXECUTE IMMEDIATE v_sql5;END LOOP;CLOSE mycur;END;

代码可以顺利执行,但是里面发现了个问题,它这个列不一定存在,如果执行中没有这个列,肯定会报错,数据库报错是不会自动帮你跳过的,就直接停止了。这样达不到效果!

进一步修改
既然这个列有可能不存在,那我先给它来个判断如何?

顺着这个思路,对代码加以修改,从系统表USER_TAB_COLUMNS中查询被更新的表中是否存在定义的列名,如果存在则执行更新,不存在就跳过。

具体代码如下:

DECLARE--定义游标CURSOR mycur IS   --将临时表TEMP_TABLE中记录数不为0的表查询出来SELECT T_NAME FROM TEMP_TABLE WHERE T_CNT<>0 ;myrecord mycur%ROWTYPE;--定义变量v_sql1 VARCHAR2(4000);v_count INTEGER;--定义列变量,v_name VARCHAR2(20) := 'TABLE0001';BEGIN--打开游标OPEN mycur;LOOPFETCH mycur INTO myrecord;EXIT WHEN mycur%NOTFOUND;--从系统表USER_TAB_COLUMNS中查询被更新的表中是否存在定义的列名SELECT COUNT(*) INTO v_count FROM USER_TAB_COLUMNS WHERE table_name=myrecord.T_NAME and column_name=v_name;--如果存在执行更新IF v_count>0 THENv_sql1 :='UPDATE '||myrecord.T_NAME||' SET '||v_name||'=REPLACE('||v_name||',''张三'',''李四'')';EXECUTE IMMEDIATE v_sql1;END IF;END LOOP;--结束游标CLOSE mycur;END;

结果成功了,只需要修改变量v_name即可。执行一次挺快的,只需要7秒左右。

后续优化
还有最后一步可以完全自动化执行,就是把所有列都放到一个表,循环执行这个游标,前提是知道最大列有多少个,或者直接取最大值FIELD999,这样在执行的时候可能会比较耗时间,因为每一轮他都要判断999次列是否存在。

至此,整改批量修改部分字段内容的功能全部完成。

一点思考
从这个题目中引发我的一点思考,有好的也有坏的。

好的是遇到问题能用其他办法解决就不要一棵树上吊死,第一步出错的地方至今未找到。如果把时间全放在这一步肯定得不偿失。工作中更多讲究的是效率,能用其他方法解决问题就尽快换方法解决。此外将问题细化也能让思维更优逻辑性。

坏的是遇到问题就一股脑的写代码,也不考虑写的结果是怎么样,这样其实还是很费时间的,磨刀不误砍柴工,将问题思考清楚再处理会更好。

结语
工作和生活中难免会遇到一些难题,当你能把一道题或者一件事通过自己思考做出来,那种成就感不言而喻。可能这就是程序员的快乐吧!

©著作权归作者所有:来自51CTO博客作者mb5fd8698f60f87的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. SQL高级知识V2——动态SQL
  2. 你有一份经典SQL语句大全,请注意查收!!!
  3. ansible初入
  4. SQL存储过程的详细用法,不信你看不懂
  5. c语言自学打卡
  6. hive 本机测试使用local模式可以加快执行效率
  7. 一文读懂MySQL的执行计划EXPLAIN
  8. 项目中常用的19条MySQL优化,你知道几个?
  9. JAVA开发ORACLE的规范

随机推荐

  1. Android 利用drawable中的gradient属性实
  2. android的binder机制研究二
  3. Android、iOS系统架构
  4. Android(安卓)Build System ---- how to
  5. Android(安卓)强制下线功能 第一行代码
  6. Android韩国市场占有率超过95%
  7. 如何解决:Android中 Error generating fin
  8. Android DVM
  9. Android的NDK开发
  10. Android(安卓)动画整理(3.0以上)