Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如何合并行并检索新键和现有键_Sql_Oracle_Merge_Duplicates - Fatal编程技术网

Sql 如何合并行并检索新键和现有键

Sql 如何合并行并检索新键和现有键,sql,oracle,merge,duplicates,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.p

在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

为什么不能对此使用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遍历游标更好的解释路径。

如果键是从序列生成的,则获取该insert生成的键的正常方法是使用returning子句:

declare
  v_insert_seq integer;
begin
  insert into t1 (pk, c1)
  values (myseq.nextval, 'value') returning pk into v_insert_seq;
end;
/
然而,正如我所知,merge语句不支持返回特性

根据新行的来源,您可以使用不同的方法来执行此操作。如果一次插入一行,那么上面的方法将非常有效

要检测重复记录,只需在插入when dup_val_on_索引时捕获异常,然后用更新处理它们

如果您的行源是另一个表,您可能希望查看批量插入,并允许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;
/
下一个最好的方法是将行选择到数组中,然后使用带有returning子句的批量绑定来捕获新的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;
/

将“修改日期”列添加到表中

抓取并保存sysdate

合并时,请同时更新/插入sysdate的值

合并完成后,选择修改日期=SYSDATE且您
拥有您感兴趣的集合。

如果您运行的是Oracle 10或更高版本,您可能可以免费完成大致相同的工作,方法是在合并之前发出提交命令以更新SCN,然后在合并之后,
使用ORA_ROWSCN来检测哪些行已更改。

相关:有趣:太糟糕的MERGE不支持RETURNING子句我自然地说,我应该尝试使用MERGE,但我还需要检索插入时创建的和更新主键时存在的所有行。。合并似乎不允许这样。我不确定我是否理解。你可能需要在问题中给出一个更完整的例子。在合并中,如果源中不存在该键,它将使用INSERT语句创建该键。否则,如果它找到匹配的键,它将使用UPDATE语句。我认为解决问题的关键是正确定义源查询。我想我应该问,您的密钥的源是什么?它是一个不同的表还是基于过程中的逻辑或其他东西?插入时键的来源是一个序列,或者当具有该PK的行已经存在时主键值本身。正如您在插入后所看到的那样,我确实从DUAL中选择了SEQ_MYTABLE.CURRVAL。新行是来自另一个表还是来自一个应用程序?如果它来自一个应用程序,您将无法使用合并,除非您将新插入缓存到一个全局临时表中,然后从该临时表合并—这将非常快。否则,您将不得不使用存储过程方法。谢谢。有趣的是,在处理异常时,似乎无法检索现有行的PK。如果使用序列生成PK,则无论如何都不会有重复的PK。如果基于表/新行中未生成的列违反了另一个唯一约束,那么错误消息应该会告诉您违反了哪个约束,因此您知道键,因为它已经在失败的行中了。这个想法很聪明,但我认为可以稍微改进一下,例如,如果两组合并完全同时进行,则两个合并中的键将过多。也许某种随机散列整数应该可以做到这一点。。。我可能会遇到与使用sysdate类似的问题:一次合并会影响某些行;犯罪影响相同行的另一个合并;犯罪然后,如果选择检索第一个合并主键,某些键将不会返回第二个合并更新的行…让每个合并获取下一个序列号。每个合并都会向上插入该值。选择序列号匹配的行。。。。如果合并更新同一行,它可能会变得混乱:另一种方法是延迟提交,直到您选择出键
你需要。在你提交之前,另一个会话不会看到数据。我目前没有足够的时间来测试。。。我目前正试图让我的快速和肮脏的逻辑工作,当它将是好的,我会尝试提高性能。我将随时通知您:ORA_ROWSCN似乎不是100%可靠的,正如本文所述:它不是绝对准确的,因为Oracle通过为行所在的块提交的事务来跟踪SCN。无论如何,谢谢你的建议。