a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 299|回复: 3

[考试辅导] 怎样删除Oracle数据库中的冗余数据(一)

[复制链接]
发表于 2012-8-4 14:06:19 | 显示全部楼层 |阅读模式
作为数据库的开发者,我们经常面临着要找出及删除数据库中冗余数据的任务,如果数据库中有大量的冗余数据(占总数的百分比太多),数据的精确性和可靠性将受到影响,同时也影响着数据库的性能,那么如何解决这个问题呢?下面我将探讨关于这个问题的这个解决方案,oracle也为我们提供了一个解决方案,但是Oracle提供的解决方案不够完美,遇到大批量数据那个解决方案工作起来很慢应该怎么删除冗余数据呢?   在这里我们应用一个PL/SQl方案(一个自定义的存储过程)或者一个SQL语句的解决方案(使用一个分析的函数RANK()和一个嵌套的子查询)来消除冗余数据然后控制应该保留的记录
% e; k) \: n( p3 x  [1 Y( {& }/ z  什么是冗余数据?
9 |5 Z( i* t! f) `, F( n1 l# [# n  冗余数据就是一个数据表中,这个表中的行包含了一些相同的值,这些值理论上来说应该是唯一的(这些值一般来说能确定一条记录)例如,像社会保险号,姓与名的集合.那么我们把这么含有相同信息的行中包含的数据叫做冗余数据,现在所有的数据库表中都有主键约束,主键中记录了一行记录中的唯一值,从数据库的角度来看,每一行都是唯一的,但是从我们用户角度看来,这些记录都是相同的记录,因为它们都包含相同的键值(First Name + Last Name),即使他们有不同的主键' w# j. L' r% u
  ID Last Name First Name City Phone0 X6 R2 D" |! f" L5 E5 O
  ---- --------------- ---------- --------------- ----------" Z& [7 x# b% x, a3 P0 F
  1005 Krieger Jeff San Ramon 9252997100
$ u' U* ]; x" w/ c" i; s  1012 Krieger Jeff San Ramon 9252997100
. X7 }+ c8 c+ G: ^0 ^% M" P  1017 Krieger Jeff San Ramon 9252997100  H2 k- l7 e0 i2 ]
  那么这些冗余数据是怎么出现的那?通常有两种情况:1.从不同的表中加载或者合并数据通过图形化的用户接口来输入数据,然后由计算机来生成一个唯一的键,并做为这一条记录的主键那么怎样找到冗余数据呢?让我们来创建一个叫作Customer 的表并向其中加入冗余数据,看表1,正如你所看到的,我们并没有在这个表上做什么限制来防止冗余数据,下面这么代码创建了一个唯一约束,来防止冗余数据的生成
1 ~$ H: M- g* O$ O* I- _2 e  SQL
5 h% s2 H' n8 U  Listing 1. 创建Customer表
  R" u1 B% i: [# I  这个表中我们故意加入了冗余数据
7 D0 }% P8 v5 q9 L/ X  DROP TABLE Customers CASCADE CONSTRAINTS;
6 n8 B. |1 `& c4 y. b  CREATE TABLE Customers(. v0 J% \8 R# G3 U* j8 W
  Id INTEGER NOT NULL, LastName VARCHAR2(15) NOT NULL, FirstName VARCHAR2(10), Address VARCHAR2(20), City VARCHAR2(15), State CHAR(2), Zip VARCHAR2(10),
( i& P. }% Q! {  Phone VARCHAR2(10)5 i3 ?* n0 a$ y7 c7 J
  CONSTRAINT Customers_PK) Q- N; b. s& L- s
  PRIMARY KEY (ID))! u+ d, X1 v" J" i- [1 `: R
  TABLESPACE TALLYDATA;9 Y5 u9 D! \& f3 w) _4 \3 t/ T
  COMMIT;8 d* A" @1 _' W; B1 q
  看下面的代码我在姓,和名这两个字段上加上唯一约束,(当然你可以在创建表的时候加上这一约束,来防止冗余数据)
8 V& P0 T& B  T& O  ALTER TABLE Customers' u- X& E& y, [- K* _' k. z
  ADD CONSTRAINT Customers_LastFirst
5 |& l  e4 ~) t( q; @# l  UNIQUE (LastName, FirstName);
, f) N7 K( m5 h2 n8 T5 L  Customer表中的冗余键是LastName和FirstName的集合,我们把含* `/ q& `' ^" |$ I$ D" q1 T6 g
  有冗余键的数据进行分组并进行统计.
4 `! Y0 g8 Z+ n' R! y2 ^: {' m  SELECT LastName, FirstName, COUNT(*)   FROM Customers GROUP BY LastName, FirstName ORDER BY LastName, FirstName;
! g' _4 n5 Y0 D* T; f5 ^9 v7 `  Listing 2显示了这条语句的输出,我们可以看到有三行的输出大于1,这也就意味& h2 o% b8 y' F8 ^! n9 g
  着表中含有3组冗余数据.
/ v3 Z. T% Z) g1 Z* Y1 O  Listing 2. 找出冗余
% @/ ?+ i9 F* B4 e- p6 w  LASTNAME FIRSTNAME COUNT(*)
4 i% k6 P+ l) \% U- G  J/ [  --------------- ---------- ----------
/ t  b9 D+ n0 Q* V  Blake Becky 1! ]. P7 s$ Q. A  a& C
  Blue Don 1' D& W# z+ C; N- Z: f6 I2 y1 r
  Bradley Tom 1
) y8 D  c& ~2 B# _' ~* }5 |  Chang Jim 12 h% n4 M6 c/ Q5 E3 P! F
  Griffith David 1
, U- |4 v+ L) J! \4 _  Hill Larry 1  p$ P  l+ O6 e- j: z6 a6 h1 Q
  King Chuck 1; Z# b- ^& u$ `1 B$ X; N$ t: |
  Krieger Jeff 3
9 u, U" h4 I3 |/ i# C  Loney Julie 1( R  y$ N5 J; ]0 k' _9 Z
  Lord Don 1
+ a* q. M# P* Z  e  Mason Paul 14 S$ [; p! w6 n( ]! y- j
  Monroe John 1
7 `5 `+ L5 X7 K+ p( F  Simon Michael 2
2 y6 q& ?. a+ W7 g! ~: _  Stone Tony 5' g' @" u8 F; f" C
  14 rows selected.
5 |( k$ {4 E7 o$ b5 s3 W/ X; x- ]+ ?# U! M, V$ p; _
  
回复

使用道具 举报

 楼主| 发表于 2012-8-4 14:06:20 | 显示全部楼层

怎样删除Oracle数据库中的冗余数据(一)

我们在语句中加入Having()语句来过滤出非冗余数据.</p>  SELECT LastName, FirstName, COUNT(*)" ?. m* U+ r+ p1 P- }
  FROM Customers9 g) v/ m+ M, t  Y$ ^
  GROUP BY LastName, FirstName% z9 U' P( {8 O7 {) \- j
  HAVING COUNT(*) > 1;) R/ Q$ S6 |; c* h' a0 U
  SQL
" X+ E: r! z$ {. R, w  Listing 3. 过滤冗余* U. ^- D9 M7 X8 n
  加入Having()语句来过滤出非冗余数据.% H+ n% ~2 o8 i- N  T9 ^; ?
  LASTNAME FIRSTNAME COUNT(*)
0 e& _' l9 M& |2 t; O' T  --------------- ---------- ----------
; Q8 i8 p4 l  z! N! H% g* _0 U  Krieger Jeff 37 k. {6 @$ w, ?6 a) b
  Simon Michael 27 d  b) x! y' A2 v4 b
  Stone Tony 5
% H$ r- F8 w9 }& N0 i* e! Z1 V7 X  3 rows selected.Listing 3显示了以上代码的输入,尽管如此,这些查询结果并没有显示出能标识每一行的字段,我们将上一语句做为一个嵌套查询来显示标识这些记录的IDSELECT ID, LastName, FirstName
- L& M5 W" D  N3 c9 r+ f  FROM Customers
- n% _! U1 ?. H, c+ ~  WHERE (LastName, FirstName) IN (SELECT LastName, FirstName
  F- }& P4 s1 U+ W  G8 F: ]2 k4 `  FROM Customers+ _1 ^! e' {- b- [0 |9 \4 ^# y
  GROUP BY LastName, FirstName
2 H, t' A$ r! V4 d' n! z  HAVING COUNT(*) > 1)
$ s( S9 R4 l, c% A% D: F$ N1 @  ORDER BY LastName, FirstName;2 b* \8 P6 B& w
  Listing 4显示出了以上代码的结果,这些查询显示了有三组冗余,共有十行,我们应该保留这些组中的1005,1009,1001这些记录然后删除1012,1017,1010,1011,1016,1019,1014这些冗余的条目.
; l" a" g  R8 }5 v8 m  SQL
7 @; c% N6 |, T( T- I6 j  W6 w  Listing 4. 找出唯一的键& p' e+ R! U; K% _. t
  语句的输出
  }& M% k, `# D  ID LASTNAME FIRSTNAME! E- I" Q% f( }! W7 d; l) c
  ----- --------------- ----------  q4 T6 F7 {* |  N
  1005 Krieger Jeff7 `8 x) f# E# Q- k
  1012 Krieger Jeff
# \4 K  q" d$ f% P  1017 Krieger Jeff/ s1 y/ f8 ?1 N) P7 ?
  1009 Simon Michael
2 q: n% c2 P# `1 H% D: [  1010 Simon Michael/ B" H$ v2 C! M! A- k1 @
  1001 Stone Tony
6 ?+ m1 `" a/ Y' {8 l+ O: k  1011 Stone Tony$ @8 l$ q; L1 c8 I
  1016 Stone Tony
8 ^! }6 ]' d* `" l( `  1019 Stone Tony
% E9 O" J* w9 w4 H  1014 Stone Tony
3 g3 B! D: X9 {' F3 ?  ^  10 rows selected.
- w9 @- h) N& X1 d4 {  Oracle公司给出的一个解决方案
: H, G& b; u1 G7 N: I% w% C  Oracle 公司给我们提供一个见删除冗余数据的一个方案,这个方案使用了Oracl
/ s- g. G% W7 n. l, {2 N& \% p  e公司自己的一个集合函数MIN()或者MAX()来解决这一问题MIN()函数可以得
& c4 c1 U- I$ {. O9 L3 ^3 M4 ?  到每一组中(冗余的非冗余的),应保留的所有值.(正如我们所见,输入出不包含那些大I
" ?1 [: _+ _" H" M) L+ b  D的冗余值
4 E" E$ p5 H5 S% D  SELECT MIN(ID) AS ID, LastName, FirstName! i  r: t, |* G7 b  W4 {
  FROM Customers
7 j' w- T! o4 D6 n! L* \  GROUP BY LastName, FirstName;& L; m: p3 ~5 o3 X
  这一条命令的输出! G1 p6 V1 M* K- R' \. S

# t4 D2 I( T5 }2 N+ f+ O/ K 
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 14:06:21 | 显示全部楼层

怎样删除Oracle数据库中的冗余数据(一)

 Listing 5. Output of MIN() query这一条命令显示了所有的非冗余的数据,其它的行则应该被删除</p>  ID LASTNAME FIRSTNAME3 B5 c7 O9 R) h. M1 \
  ----- --------------- ----------( R) _+ ~" g- p2 ~, S. h2 N" e
  1018 Blake Becky
) r$ q; @) [. h" }* F  1013 Blue Don( v- ^  e5 _( t
  1000 Bradley Tom3 P+ [9 X! n+ I% g
  1002 Chang Jim8 Z6 b/ ^7 U( g; L& {& T
  1008 Griffith David+ A' m! ~4 a) ~3 f% |* _5 F" p
  1020 Hill Larry1 R' [! H1 u; B; Y8 r
  1004 King Chuck' f. a9 k4 q3 F5 _$ P* p9 A5 J
  1005 Krieger Jeff
* G# ^: s; Y. y! ^, y& H1 K  1003 Loney Julie
& D7 Y) L% \3 _- X( _# W  1007 Lord Don
! ?4 G: b% s. B, Z  1015 Mason Paul. H4 T$ ?# O9 ~5 o' u
  1006 Monroe John! k+ Q. Y( [/ F" Z$ s4 v
  1009 Simon Michael/ M7 v/ ~7 M, h9 }5 X7 m+ Q
  1001 Stone Tony
1 J4 X. o: C) c3 j2 m  L  14 rows selected.
* W8 E) v) E0 I4 Q6 V2 G/ p  这样你就可以删除那些不在这个表中的所有的行,同样将上一条语句作为一个子查询,构造一
1 H% g& k% _/ S  个语句
. w! y% T; {: y) h# L1 f0 C) f1 M; Y  DELETE FROM Customers
, N6 w1 z  U/ b* m' |9 U( N( B4 A  WHERE ID NOT IN
. t& Q+ T* B3 p& V' Y  (SELECT MIN(ID)9 ]# E: k+ e1 ?; ^, w' L9 q- `
  FROM Customers
+ S% N# E, M5 k' s; U' f: i  GROUP BY LastName, FirstName);. F! g  g7 E  e, ^1 Q6 r* D
  尽管如此,理论是可行的,但是这个方案并不是那么有效,因为这样一来,DBMS要完成两个表的扫描来完成这项任务,对于大量的数据来说,这简直是不可行的,为了测试他的性能,我创建了Customer表,大约有5000,000行,45,000冗余行,(9%)以上这个命令运行了一个小时,没有输出结果,它耗尽了我的耐心,所以我杀死了这个进程这个方案的令外这个方案还有一个缺点,你不能控制每一个组中你要保留的行2 \" O) |# t$ g; S: i) _
  一种PL/SQl解决方案:使用存储过程删除冗余数据,叫做DeleDuplicate的存储过程,这个过程的结构很清晰的.; e) \! F% R8 z- h  i
  SQL
% v4 D/ b$ R" C" S6 e  C  Listing 6. The DeleteDuplicate stored procedure它将这些冗余行选择一到一个游标中,然后从表中取出每一个冗余行来进行与游标中的行进行比对,然后决定是否删除  e- K: q2 |# d" k
  CREATE OR REPLACE PROCEDURE DeleteDuplicates(
; a# P/ ?6 T  L. L/ X  pCommitBatchSize IN INTEGER := 5000) IS# N: T' e' ~6 ~9 `4 k
  CURSOR csr_Duplicates IS! u! `7 F9 Q* p
  SELECT ID, LastName, FirstName
. q: ^" L6 u. {) c% h8 Z  J  FROM Customers
  q" g4 b5 g* E9 d; M% c  WHERE (LastName, FirstName) IN (SELECT LastName, FirstName
, O* z/ y( c' }- P  D  FROM Customers
' _' U! [* K: \  GROUP BY LastName, FirstName0 t' }# e+ z! m9 m) C
  HAVING COUNT(*) > 1)1 P; W5 ^  A6 H, F4 x
  ORDER BY LastName, FirstName;
. I+ y5 k/ `# {/ }+ A8 [5 M+ Q3 A
 
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 14:06:22 | 显示全部楼层

怎样删除Oracle数据库中的冗余数据(一)

 /*保存上一次的姓和名*/</p>  vLastName Customers.LastName%TYPE := NULL;
' _. I: }- Z4 d* L3 H( @  vFirstName Customers.FirstName%TYPE := NULL;# P% h+ i9 |; _5 e3 {
  vCounter INTEGER := 0;
& m' [8 B" b5 X/ H7 F# Y  BEGIN, Y# U& d% s2 x- F
  FOR vDuplicates IN csr_Duplicates
# V* @+ a6 c0 S! h9 q1 c: e  U% S  LOOP: B$ ~2 V! q. @; u% u
  IF vLastName IS NULL OR
* }; k) `- Z0 ]$ _4 g  H  (vDuplicates.LastName != vLastName OR NVL(vDuplicates.FirstName, ' ') != NVL(vFirstName, ' '))2 I8 I7 f' U* C5 @3 F4 e/ w
  THEN
. v9 ~: i; l3 \! k( y6 F8 X  /*第一次取出行或者是一个新行
+ [+ [$ g% G, Z# G* r9 ~) e  保存它的姓和名的值*/
3 r. U& H& E$ g7 L  vLastName := vDuplicates.LastName;/ @- m+ L2 o7 W1 E0 P
  vFirstName := vDuplicates.FirstName;
1 W( m' U& `# S2 w8 h7 W7 V, Y# ~  ELSE- W. h) }/ d* U) S& I
  /*冗余数据,删除它*/. L2 ?, c1 K2 }& V( M5 u1 @. c$ j# \
  DELETE; {1 U% Z: L( M4 P+ Q9 I" `
  FROM Customers
4 [1 w* ^$ O& a( U  WHERE ID = vDuplicates.ID;
6 A; V! o6 n# C5 Y# a' @  vCounter := vCounter + 1;% r7 k2 h8 A" f( v% W
  /*提交结果*/
. Q7 C& X3 J) m! b1 S, u  /* Commit every pCommitBatchSize rows */9 z, M+ T5 f& t1 j1 J. T5 G( J' r
  IF MOD(vCounter, pCommitBatchSize) = 0/ |+ K4 A7 f; Z; C; d
  THEN
0 h- \6 `8 k4 L6 p4 ?. X  COMMIT;
4 W. E0 D3 J3 U  t5 w6 w  END IF;2 n% g* @6 K# G% p" J! A$ E
  END IF;2 ?4 J" @- H: S
  END LOOP;& B  V) s5 M# i- a* X2 v$ q
  IF vCounter > 0- m' p/ }& M- s& x
  THEN9 W; `- r" L  _; `# }% {
  COMMIT;
# [+ O4 j& \2 R/ H! j  END IF;2 H$ [1 J' E  C) p3 l
  DBMS_OUTPUT.PUT_LINE(TO_CHAR(vCounter) ' duplicates have been deleted.');
+ B# W+ N1 k9 k5 @- X5 l  EXCEPTION
) X# u8 D2 K5 s4 h, X  P  WHEN OTHERS- @1 v% C5 M" k
  THEN' l8 v# H& `* x" s
  DBMS_OUTPUT.PUT_LINE('Error '6 Z; v: w6 f- p5 N
  TO_CHAR(SQLCODE) ': ' SQLERRM);& o; |/ I' d1 q& o, A4 o
  ROLLBACK;
5 a  l: y( B4 v9 x) W6 s3 b  END DeleteDuplicates;
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-5 22:20 , Processed in 0.179094 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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