ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

记一次Oracle数据更新经历

2021-03-11 21:53:50  阅读:116  来源: 互联网

标签:END NAME -- 经历 myrecord 更新 mycur Oracle TABLE


关键时刻,第一时间送达

记一次Oracle数据更新经历

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

题目内容

有一批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里面自动生成我需要的查询语句,如下图:

记一次Oracle数据更新经历

记一次Oracle数据更新经历

图 EXCEL完成查询语句拼接

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

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

具体代码如下:


DECLARE
CURSOR 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;
LOOP
FETCH mycur INTO myrecord;
EXIT WHEN mycur%NOTFOUND;
v_sql1 :='UPDATE '||myrecord.T_NAME||' SET
FIELD001=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;
LOOP
FETCH 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 THEN
v_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次列是否存在。

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

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

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

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

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

标签:END,NAME,--,经历,myrecord,更新,mycur,Oracle,TABLE
来源: https://blog.51cto.com/15057820/2656423

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有