0

我在 Oracle 中有一个 PL/SQL 包,它的重要功能是:

function checkDuplicate(in_id in varchar2) return boolean is
  cnt number;
  begin
      select count(*)
        into cnt
        from tbl_Log t
       where t.id = in_id

      if (cnt > 0) then
        // It means the request is duplicate on in_id
        return false;
      end if;

    insert into tbl_log (id,date) values(in_id , sysdate);
    return true;
  end;

当两个请求同时调用这个函数时,它们都传递了这个函数,并且两个相同的in_id 被插入到tbl_log中。注意:tbl_log没有针对性能问题的 PK。有什么解决办法吗?

4

3 回答 3

4

“他们都通过了这个函数,并且在 tbl_log 中插入了两个相同的 in_id”

Oracle 在 READ COMMITTED 隔离级别上运行,因此select只能找到已提交的记录。如果一个线程为给定值插入了一条记录,但尚未提交事务,则另一个寻找相同值的线程将变为空。

“注意:tbl_log 没有针对性能问题的 PK。”

历史的教训很清楚:没有完整性约束的表不可避免地会陷入数据损坏。

“我想用这个功能识别重复……有什么解决办法吗?”

您的意思是除了添加主键约束之外?没有比主键更有效的方法来捕获重复。也许你应该看看性能问题。许多应用程序可以处理数百万次插入,并且仍然强制执行完整性约束。您还应该查看 Java 层:为什么会有多个线程提交相同的 ID?

于 2017-08-19T12:19:19.493 回答
0

根据您的情况,由于您不愿意使用主键数据完整性强制(无论如何都会导致数据损坏),我建议您可以使用MERGEstatment 并保留审核日志以latest thread更新表。这样,您将能够消除重复记录的条目,并跟踪 id 更新的时间和来源(最新信息)。希望下面的代码片段有所帮助。

---Create dummy table for data with duplicates

DROP TABLE dummy_hist;

CREATE TABLE dummy_hist AS
SELECT LEVEL COL1,
  'AVRAJIT'
  ||LEVEL COL2,
  SYSTIMESTAMP ACTUAL_INSERTION_DT,
  SYSTIMESTAMP UPD_DT,
  1 thread_val
FROM DUAL
  CONNECT BY LEVEL < 100;


--Update upd_dt
UPDATE dummy_hist SET upd_dt = NULL,thread_val = NULL;


SELECT * FROM dummy_hist;

--Create function

CREATE OR REPLACE
  FUNCTION checkDuplicate(
      in_id        IN VARCHAR2,
      p_thread_val IN NUMBER)
    RETURN BOOLEAN
  IS
    cnt NUMBER;
  BEGIN
    MERGE INTO dummy_hist A USING
    (SELECT in_id VAL FROM dual
    )B ON (A.COL1 = B.VAL)
  WHEN MATCHED THEN
    UPDATE
    SET a.upd_dt   = systimestamp,
      a.thread_val = p_thread_val
    WHERE a.col1   = b.val WHEN NOT MATCHED THEN
    INSERT
      (
        a.col1,
        a.col2,
        a.actual_insertion_dt,
        a.UPD_DT,
        a.thread_val
      )
      VALUES
      (
        b.val,
        'AVRAJIT',
        SYSTIMESTAMP,
        NULL,
        p_thread_val
      );
    COMMIT;
    RETURN true;
  END;
  /

--Execute the fucntion
DECLARE
  rc BOOLEAN;
BEGIN
  FOR I IN
  (SELECT LEVEL LVL FROM DUAL CONNECT BY LEVEL BETWEEN 8 AND 50
  )
  LOOP
    rc:=checkduplicate(I.LVL,3);
  END LOOP;
END;
/
于 2017-08-21T04:00:43.900 回答
0

注意: tbl_log 没有针对性能问题的 PK。

为了“避免性能问题”,此列上没有 PK 或唯一索引,但是有成百上千的查询,例如SELECT ... WHERE t.id = ..针对此表运行。由于该列缺少索引,这些查询必须使用全表扫描!!!!
在我看来,这可能会导致更大的性能问题。


由于此列的值是 UUID,因此值冲突的可能性很小。在这种情况下,我宁愿不使用任何锁。
只需在此列上使用唯一约束(索引)即可防止插入两个重复值。

ALTER TABLE tbl_log ADD CONSTRAINT tbl_log_id_must_be_unique UNIQUE( id );

然后使用你的函数的这个实现:

create or replace function checkDuplicate(in_id in varchar2) return boolean is
  begin
    insert into tbl_log (id,"DATE") values(in_id , sysdate);
    return true;
  exception when dup_val_on_index then
    return false;
  end;
  /

在绝大多数情况下,该函数只是将新记录插入表中而没有任何延迟,因为值是 UUID。
在极少数重复值的情况下,当该值已经提交到表中时,插入将立即失败,没有任何延迟。
在非常罕见的情况下(几乎不可能),当两个线程试图同时插入相同的 UUID 时,第二个线程将在 INSERT 命令上暂停并等待一段时间,直到第一个线程提交或回滚。

于 2017-08-19T07:12:46.743 回答