2012-07-25 168 views
0

我有一个表,其中存在1220200个重复记录。Oracle:批量删除重复记录

我正在使用下面的查询来删除重复的记录。

DELETE /*+ NO_CPU_COSTING */ 
    FROM FCST f1 
    WHERE 
     ROWID > 
      (SELECT MIN (ROWID) 
       FROM FCST f2 
      WHERE 
        f1.DMDUNIT = f2.DMDUNIT 
        AND f1.DMDGROUP = f2.DMDGROUP 
        AND f1.LOC = f2.LOC 
        AND f1.STARTDATE = f2.STARTDATE 
        AND f1.TYPE = f2.TYPE 
        AND UPPER (f1.FCSTID) = UPPER (f2.FCSTID)); 

删除这些记录需要将近2分钟。我尝试了批量删除的方法,通过将重复数据加载到光标并批量删除它,但它需要更多时间。

什么是更好的方法来优化此代码?

+4

真的很重要吗?你一定会这样做,并且2分钟去除120万个副本似乎是相当合理的。 – 2012-07-25 13:33:18

+0

嗨托尼,感谢您的快速响应。这是迁移的一部分,它将在每次从样本转储设置新数据库时执行。 – Anand 2012-07-25 15:42:45

+0

除了解决方案,我更有兴趣了解是否可以使用类型,批量收集或可能并行pl/sql优化它? – Anand 2012-07-25 16:00:32

回答

0

一件简单的事情做的就是这样的事情

delete /*+RULE*/ from t 
where rowid in (select rid 
        from (select rowid rid, 
           row_number() over 
            (partition by cust_seg_nbr order by rowid) rn 
          from t 
         ) 
       where rn <> 1); 

不过,如果你有大量的数据,然后

检查此链接http://www.rampant-books.com/t_stoever_delete_duplicates.htm或者使用下面

DECLARE  -- Code ©2004 by Edward Stoever 
    CURSOR c_get_duplicates 
    IS 
     SELECT ssrfees_term_code, ssrfees_crn, ssrfees_detl_code, 
       ssrfees_ftyp_code, ssrfees_levl_code, COUNT (*) 
      FROM ssrfees 
     HAVING COUNT (*) > 1 
     GROUP BY ssrfees_term_code, 
       ssrfees_crn, 
       ssrfees_detl_code, 
       ssrfees_ftyp_code, 
       ssrfees_levl_code; 

    var_get_duplicates c_get_duplicates%ROWTYPE; 

    CURSOR c_del_only_one 
    IS 
     SELECT ROWID 
     FROM ssrfees 
     WHERE ssrfees_term_code = var_get_duplicates.ssrfees_term_code 
     AND ssrfees_crn = var_get_duplicates.ssrfees_crn 
     AND ssrfees_detl_code = var_get_duplicates.ssrfees_detl_code 
     AND NVL(ssrfees_ftyp_code,'1') = NVL(var_get_duplicates.ssrfees_ftyp_code,'1') 
     AND NVL(ssrfees_levl_code,'1') = NVL(var_get_duplicates.ssrfees_levl_code,'1'); 

    var_del_only_one ROWID; 
BEGIN 
    OPEN c_get_duplicates; 

    LOOP 
     FETCH c_get_duplicates 
     INTO var_get_duplicates; 

     EXIT WHEN c_get_duplicates%NOTFOUND; 

     OPEN c_del_only_one; 

     FETCH c_del_only_one 
     INTO var_del_only_one; 

     DELETE FROM ssrfees 
      WHERE ROWID = var_del_only_one; 

     COMMIT; 

     CLOSE c_del_only_one; 
    END LOOP; 

    CLOSE c_get_duplicates; 
END; 
/
+0

如果你有大量的数据,那么我会怀疑你想要的最后一种方法是使用嵌套游标,特别是当包含提交时。这看起来像一个糟糕的表现配方。 – 2013-06-17 10:03:38

0

查询代码包含“ROWID> ...”基本上是可疑的。

我认为你正在寻找的是这样的:

DELETE FROM 
    FCST f1 
WHERE 
    ROWID NOT IN (
    SELECT MIN(ROWID) 
    FROM  FCST f2 
    GROUP BY f2.DMDUNIT, 
      f2.DMDGROUP, 
      f2.LOC, 
      f2.STARTDATE, 
      f2.TYPE, 
      UPPER(f2.FCSTID)); 

子查询标识一组ROWID的,涵盖所有在该组的列BY子句中的唯一值,且其他所有删除。

更快的替代方案可能是创建一个只包含要保留的行的新表,但如果这足够高性能,请坚持这一点。