仅当记录尚不存在时,才使用过程将其插入表中

时间:2018-10-19 22:27:18

标签: oracle plsql

我有一个要通过plsql脚本填充的表(在plsql开发人员上运行)。实际的DML语句 包含在包内的过程中。该过程仅在记录不存在时才插入。

它不起作用。即使在表中实际上不存在,检查存在性的部分在脚本循环的第一次迭代后也会返回true。

如果我将提交置于循环之外,则根本不会插入任何内容,即使表为空,存在性检查也会对所有迭代返回true。

当我尝试通过存在检查将插入简化为仅包含一条语句而不进行异常处理时,我得到的结果相同。

请告诉我我在做什么错了。

CREATE OR REPLACE PACKAGE BODY some_package
IS
  PROCEDURE add_to_queue(id IN NUMBER)
  IS
    pending_record VARCHAR2(1);
  BEGIN
    -- this part succeeds even if nothing matches the criteria
    -- during the loop in the outside script
    SELECT 'Y'
    INTO pending_record
    FROM dual
    WHERE EXISTS (SELECT 'x' FROM some_queue smq
                  WHERE smq.id = id AND smq.status IS NULL);
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      INSERT INTO some_queue (seqno, id, activity_date)
      VALUES (some_sequence.nextval, id, SYSDATE);
    WHEN OTHERS THEN
      NULL;
  END;
END some_package;

CREATE TABLE some_queue
(
  seqno             VARCHAR2(500) NOT NULL,
  id                NUMBER NOT NULL,
  activity_date     DATE NOT NULL,
  status            VARCHAR2(25),
  CONSTRAINT some_queue_pk PRIMARY KEY (seqno)
);

-- script to randomly fill in the table with ids from another table
declare
  type ids_coll_tt is table of number index by pls_integer;

  ids_coll_table ids_coll_tt;

  cursor ids_coll_cur is
    select tab.id
    from (select *
          from ids_source_table
          order by dbms_random.value ) tab
    where rownum < 10;
begin
  open ids_coll_cur;
  fetch ids_coll_cur bulk collect into ids_coll_table;
  close ids_coll_cur;

  for x in 1..ids_coll_table.count
  loop
    some_package.add_to_queue(ids_coll_table(x));
    commit; -- if this is here, the first iteration gets inserted
  end loop;
  -- commit; -- if the commit is done here, nothing gets inserted
end;

注意:我将此代码翻译为更通用。如果有错别字,请原谅我。

更新:即使我将所有内容放到脚本中而不使用包,我也无法正确检查其存在,并且得到相同的结果。

2 个答案:

答案 0 :(得分:0)

我想出了解决方案:

CREATE OR REPLACE PACKAGE BODY some_package
IS
  PROCEDURE add_to_queue(p_id IN NUMBER)
  IS
    pending_record VARCHAR2(1);
  BEGIN
    -- this part succeeds even if nothing matches the criteria
    -- during the loop in the outside script
    SELECT 'Y'
    INTO pending_record
    FROM dual
    WHERE EXISTS (SELECT 'x' FROM some_queue smq
                  WHERE smq.id = p_id AND smq.status IS NULL);
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      INSERT INTO some_queue (seqno, id, activity_date)
      VALUES (some_sequence.nextval, p_id, SYSDATE);
    WHEN OTHERS THEN
      NULL;
  END;
END some_package;

更改参数名称将其修复。我猜编译器如果和表字段同名会感到困惑。

答案 1 :(得分:0)

不要使用与列相同的名称(使用像p_in_这样的前缀),如果使用MERGE语句,则可以在一个语句中完成在ROWID伪列上自动加入:

CREATE OR REPLACE PACKAGE BODY some_package
IS
  PROCEDURE add_to_queue(
    in_id IN NUMBER
  )
  IS
  BEGIN
    MERGE INTO some_queue dst
    USING ( SELECT ROWID AS rid
            FROM   some_queue
            WHERE  id = in_id
            AND    status IS NULL ) src
    ON ( src.rid = dst.ROWID )
    WHEN NOT MATCHED THEN
      INSERT (seqno,                 id,    activity_date)
      VALUES (some_sequence.nextval, in_id, SYSDATE);
  END;
END some_package;