设为首页 收藏本站
查看: 510|回复: 0

[经验分享] oracle处理插入重复记录的技巧

[复制链接]

尚未签到

发表于 2016-7-31 13:41:34 | 显示全部楼层 |阅读模式
  http://book.csdn.net/bookfiles/669/10066921093.shtml
精明地使用异常(Exceptions)
Discerning Use of Exceptions
勇敢与鲁莽的界线很模糊,我建议进攻式编程,但并不是要你模仿轻步兵旅在Balaclava的自杀性冲锋(注7)。针对异常编程,最终可能落得虚张声势的愚蠢结果,但自负的开发者还是对它“推崇备至(go for it)”,并坚信检查和处理异常能使他们完成任务。
正如其名字所暗示的,异常应该是那些例外情况。对数据库编程的具体情况而言,不是所有异常都要求同样的处理方式——这是理解异常的使用是否明智的关键点。有些是“好”异常,应预先抛出;有些是“坏”异常,仅当真正的灾害发生时才抛出。
例如,以主键为条件进行查询时,如果没有结果返回则开销极少,因为只需检查索引即可判断。然而,如果查询无法使用索引,就必须搜索整个表——当此表数据量很大,所在机器又正在接近满负荷工作时,可能造成灾难。
有些异常的处理代价高昂,即使是在最佳情况下也不例外,例如重复键(duplicate key)的探测。“唯一性(uniqueness)”如何保证呢?我们几乎总是建立一个唯一性索引,每次向该索引增加一个键时,都要检查是否违反了该唯一性索引的约束。然而,建立索引项需要记录物理地址,于是就要求先将记录插入表,后将索引项插入索引。如果违反此约束,数据库会取消不完全的插入,并返回违反约束的错误信息。上述这些操作开销巨大。但最大的问题是,整个处理必须围绕个别异常展开,于是我们必须“从个别记录的角度进行思考”,而不是“从数据集出发进行思考”,这与关系数据库理论完全背道而驰。多次违反此约束会导致性能严重下降。
来看一个 Oracle 的例子。假设在两家公司合并后,电子邮件地址定为<Initial><Name>的标准格式,最多 12 个字符,所有空格或引号以下划线代替。
如果新的employee表已经建好,并包含3 000 条从employee_old表中提取并进行标准化处理的电子邮件地址。我们希望每个员工的电子邮件地址具有唯一性,于是Fernando Lopez的地址为flopez,而Francisco Lopez的地址为flopez2。实际上,我们实际测试的数据中有33 个潜在的重复项,所以我们需要做如下测试:
SQL> insert into employees(emp_num, emp_name,
emp_firstname, emp_email)
2 select emp_num,
3 emp_name,
4 emp_firstname,
5 substr(substr(EMP_FIRSTNAME, 1, 1)
6 ||translate(EMP_NAME, ' ''', '_ _'), 1, 12)
7 from employees_old;
insert into employees(emp_num, emp_name, emp_firstname, emp_email)
*
ERROR at line 1:
ORA-00001: unique constraint (EMP_EMAIL_UQ) violated
Elapsed: 00:00:00.85
3 000 条数据中重复 33 条,比率大约是 1%,所以,或许可以心安理得地处理符合标准的 99%,并用异常来处理其余部分。毕竟,1% 的不符标准数据带来的异常处理开销应该不大。以下是采用该“乐观方法”的代码:
SQL> declare
2 v_counter varchar2(12);
3 b_ok boolean;
4 n_counter number;
5 cursor c is select emp_num,
6 emp_name,
7 emp_firstname
8 from employees_old;
9 begin
10 for rec in c
11 loop
12 begin
13 insert into employees(emp_num, emp_name,
14 emp_firstname, emp_email)
15 values (rec.emp_num,
16 rec.emp_name,
17 rec.emp_firstname,
18 substr(substr(rec.emp_firstname, 1, 1)
19 ||translate(rec.emp_name, ' ''', '_ _'), 1, 12));
20 exception
21 when dup_val_on_index then
22 b_ok := FALSE;
23 n_counter := 1;
24 begin
25 v_counter := ltrim(to_char(n_counter));
26 insert into employees(emp_num, emp_name,
27 emp_firstname, emp_email)
28 values (rec.emp_num,
29 rec.emp_name,
30 rec.emp_firstname,
31 substr(substr(rec.emp_firstname, 1, 1)
32 ||translate(rec.emp_name, ' ''', '_ _'), 1,
33 12 - length(v_counter)) || v_counter);
34 b_ok := TRUE;
35 exception
36 when dup_val_on_index then
37 n_counter := n_counter + 1;
38 end;
39 end;
40 end loop;
41 end;
40 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:18.41
但这个异常处理的开销到底在哪里呢?让我们先从测试数据中剔除“问题记录”,然后再执行相同的测试,比较发现:这次测试的总运行时间,与上次几乎相同,都是18 秒。然而,从测试数据中剔除“问题记录”之后再执行前面第一段 insert...select 语句时,速度明显比循环快:最终发现采用“一次处理一行”的方式导致耗时增加了近 50%。那么,在此例中可以不用“一次处理一行”的方式吗?可以,但要首先避免使用异常。正是这个通过异常处理解决“问题记录”问题决定,迫使我们采用循序方式的。
另外,由于发生冲突的电子邮件地址可能不止一个,可以为它们指定某个数字获得唯一性。
很容易判断有多少个数据记录发生了冲突,增加一个group by子句就可以了。但在分配数字时,如果不使用主数据库系统提供的分析功能,恐怕比较困难。(Oracle 称为分析功能(analytical function),DB2 则称在线分析处理(online analytical processing,OLAP),SQL Server 称之为排名功能(ranking function)。)纯粹从SQL角度来看,探索此问题的解决方案很有意义。
重复的电子邮件地址都可以被赋予一个具唯一性的数字:1赋给年纪最大的员工,2 赋给年纪次之的的员工……依次类推。为此,可以编写一个子查询,如果是group中的第一个电子邮件地址就不作操作,而该group中的后续电子邮件地址则加上序号。代码如下:
SQL> insert into employees(emp_num, emp_firstname,
2 emp_name, emp_email)
3 select emp_num,
4 emp_firstname,
5 emp_name,
6 decode(rn, 1, emp_email,
7 substr(emp_email,
8 1, 12 - length(ltrim(to_char(rn))))
9 || ltrim(to_char(rn)))
10 from (select emp_num,
11 emp_firstname,
12 emp_name,
13 substr(substr(emp_firstname, 1, 1)
14 ||translate(emp_name, ' ''', '_ _'), 1, 12)
15 emp_email,
16 row_number()
17 over (partition by
18 substr(substr(emp_firstname, 1, 1)
19 ||translate(emp_name,' ''','_ _'),1,12)
20 order by emp_num) rn
21 from employees_old)
22 /
3000 rows created.
Elapsed: 00:00:11.68
上面的代码避免了一次一行的处理,而且该解决方案的执行时间仅是先前方案的 60%。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-251502-1-1.html 上篇帖子: Oracle SQL 内置函数大全(二) 下篇帖子: Oracle学习:分区表和索引
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表