如何合并行+检索新密钥和现有密钥

时间:2011-08-01 13:48:39

标签: sql oracle merge duplicates

在Oracle表中(例如MYTABLE,使用数字序列字段作为主键),我必须插入数千行,但其中一些应该已经存在于表中。

当然,我应该尝试使用MERGE,但我也需要检索所有创建的(插入时)和现有的(更新时)主键。

同样,它应该尽可能快。

以下尝试(伪代码)是唯一的方法吗?感谢。

keys_list = empty array
for each row to merge
    do query 'SELECT PK_MYTABLE FROM MYTABLE WHERE PK_MYTABLE = '+row.pk_mytable
        ==> retrieve key
    if found then:
        add key to keys_list
    else:
        do query 'INSERT INTO MYTABLE (PK_MYTABLE, ...) VALUES (SEQ_MYTABLE.NEXTVAL, ...)'
        do query 'SELECT SEQ_MYTABLE.CURRVAL FROM DUAL' ==> retrieve key
        add key to keys_list

4 个答案:

答案 0 :(得分:1)

将MODIFICATION_DATE列添加到表

抓取并保存sysdate。

当你合并更新/插入sysdate的值时。

合并完成后,选择MODIFICATION_DATE = SYSDATE和您的行 有你感兴趣的套装。

答案 1 :(得分:0)

为什么不能为此使用MERGE语句?这正是MERGE的用途。以下是对它的外观的粗略概念......

merge into mytable mt
using 
(
    select key_field, value_field from sourcetable
) st
on 
( mt.key_field = st.key_field )
when matched then update
    set mt.value_field = st.value_field
when not matched then insert
    ( key_field, value_field )
    values 
    ( st.key_field, st.value_field )
;

使用MERGE语句很快,因为它是单个语句,Oracle优化器可以使用索引并选择比使用PL / SQL迭代游标更好的解释路径。

答案 2 :(得分:0)

如果从序列生成密钥,那么获取该插入生成的密钥的正常方法是使用returns子句:

declare
  v_insert_seq integer;
begin
  insert into t1 (pk, c1)
  values (myseq.nextval, 'value') returning pk into v_insert_seq;
end;
/

但是,据我所知,merge语句不支持该返回功能。

根据新行的来源,您可以通过不同的方式执行此操作。如果你一次插入一行,那么上面的方法就可以了。

要检测重复记录,只需在插入时捕获异常(当dup_val_on_index时),然后使用更新处理它们。

如果您的行源是另一个表,您可能希望查看批量插入,并允许Oracle返回一组新的PK值。我尝试了这个,但是无法让它工作,所以也许它不受支持(或者我今天遗漏了一些东西 - 它给出了语法错误):

declare
  type t_type is table of t1.pk%type;
  v_insert_seqs t_type;
begin

   insert into t1 (pk, c1)
   select level newpk, 'value' c1value
   from dual
   connect by level <= 10 returning pk bulk collect into v_insert_seqs;

exception 
  when dup_val_on_index then
    raise;
end;
/

接下来最好的事情是将行选择到数组中,然后使用bulk绑定和returns子句捕获新的PK ID,并使用Save Exceptions来捕获所有未能插入的行。然后您可以处理之后插入的任何失败:

set serveroutput on
declare
  type t_pk is table of t1.pk%type;
  type t_c1 is table of t1.c1%type;
  v_pks t_pk;
  v_c1s  t_c1;
  v_new_pks t_pk;

  ex_dml_errors EXCEPTION;
  PRAGMA EXCEPTION_INIT(ex_dml_errors, -24381);

begin
  -- get the batch of rows you want to insert
  select level newpk, 'value' c1
  bulk collect into v_pks, v_c1s
  from dual connect by level <= 10;

  -- bulk bind insert, saving exceptions and capturing the newly inserted
  -- records
  forall i in v_pks.first .. v_pks.last save exceptions 
    insert into t1 (pk, c1)
    values (v_pks(i), v_c1s(i)) returning pk bulk collect into v_new_pks;

exception
 -- Process the exceptions 
  when ex_dml_errors then
    for i in 1..SQL%BULK_EXCEPTIONS.count loop
      DBMS_OUTPUT.put_line('Error: ' || i || 
          ' Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
          ' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
    end loop;
end;
/

答案 3 :(得分:0)

如果您运行的是Oracle 10或更高版本,您可以通过在合并之前发出提交来更新SCN,然后在合并之后,几乎可以做同样的事情。 使用ORA_ROWSCN来检测哪些行已更改。