日期:2014-05-17  浏览次数:20905 次

插入数据时去重,怎样做的效率高一些?
向表A(field1,field2)中插入数据,其中字段field1为自动增长的(我使用了序列自增的方式,如insert into A values(seq.nextval,'XXX')),而字段field2的值是不能重复的(其实是多个字段,这里做了简化)。一次性要插入上亿条数据。
我使用的方法:
1)select count(*) from A where field2=‘XXX’;如果count(*) >=1,就中不再执行插入任务;
2)将field2字段(其实是多个字段)设为主键,插入数据时由Oracle自行进行主键约束判断;

问题:
a)请问各位还有更好的方法吗?
b)另外,我使用方法2的时候,由于字段field1做了序列自增,导致即使主键约束使得无法插入重复数据,但是field1字段的值还是自增了,表现为下一条数据成功插入时,其field1字段的值与上一个值不连续。有没有一个方法能够使得field1字段的值是自动增长的(多线程插入),同时在遇到重复值不进行插入时该字段不自增?

------解决方案--------------------
楼主是想做以下的事吗?
1.  向一张表插入海量数据,而且,依楼主的要求分析,这张表本身的数据量就很大。
2.  想一条一条插入,判断一条插入一条。

如果上述表达成立的话,不赞成楼主的方案,插入的同时进行判断。 
因为,这样的效率太慢,别说1亿,100W的数据插入速度,就已经会让人抓狂了。

建议考虑以下的方案,也是插入数据常用的方法
1. 先插入数据
2. 再进行滤重
    2.1 如果目标表里数据量非常大,滤重时进行特殊考虑,如,按分区一个一个进行滤重。
    2.2 另一种能想到的方式,先插入一张缓存表,缓存表 minus 目标表 (只在缓存表存在的记录) =>  插入目标表 即可。 也就是插入要分2步,当然,数据量巨大的情况下,也需要特殊考量(分区?)。

总之,方法是有的,但重要的,还是实践,希望帮到楼主。

------解决方案--------------------
如果能够设置足够的回滚段、临时表空间的话,可以使用下面的SQL来一次性插入,能够实现FIELD2除重和FIELD1连续的目的。
如果亿条数据量过大,则把FIELD2排序后,自己分段放入到TEST_TAB中即可。
如果目标表A中已经包含了TEST_TAB中部分数据,则还需要使用not exists来过滤,记得加索引。

INSERT INTO A
  (FIELD1,FIELD2)
  SELECT SEQ.NEXTVAL, FIELD2
    FROM (SELECT /*+ PARALLEL(A,4)*/
           A.FIELD2,
           ROW_NUMBER() OVER(PARTITION BY A.FIELD2 ORDER BY 1 DESC NULLS LAST) AS RN
            FROM TEST_TAB A) B
   WHERE B.RN = 1;

------解决方案--------------------
MERGE INTO table a
      USING (SELECT cols1,cols2,cols3 ...
               FROM tableb) b
      ON (a.cols1 = b.cos1)
      WHEN NOT MATCHED THEN -不存在,就插入
         INSERT (cols1,cols2,cols3 ...)
         VALUES (b.cols1,cols2,cols3 ...)
      WHEN MATCHED THEN --存在就UPDATE,这个你可以不要的
         UPDATE
            SET a.cols1 = b.cols1, a.cols2 = b.cols2,
                a.cols3 = b.cols3 ...
                where a.cols1 = b.cols1