You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Oracle Apex插入LAB_PARA_RESULTS时如何避免重复记录?

问题描述

我有一个Oracle Apex应用,插入数据时出现重复记录。PL/SQL代码如下:

DECLARE
      CURSOR p1 IS
        SELECT DISTINCT 
            sd.TEST_NO,
            b.TEST_NAME_ENG,
            a.PATIENT_NO,
            a.ORDER_ID,
            sd.SAMPLE_ID,
            a.SECTION_ID,
            b.SAMPLE_TYPE,
            b.TEST_CONTAINER,
            b.TEST_VOLUME,
            a.SAMPLE_STATUS,
            a.CUST_NO,
            DECODE(o.ORDER_PRIORITY, 1, 'URGENT', 2, 'ROUTINE') AS ORDER_PRIORITY , 
            p.TEST_NAME,p.REFERENCE_RANGE, p.TEST_UNIT , p.SERIAL
        FROM 
            LAB_SAMPLE_HEADER a,
            LAB_TESTS b,
            LAB_SAMPLE_DETAILS sd,
            LAB_ORDERS o , 
            LAB_TEMPLATE_DETAILS p 

        WHERE 
            sd.TEST_NO = b.TEST_NO
            AND a.ORDER_ID = o.ORDER_ID
            AND sd.ORDER_ID = o.ORDER_ID
            AND a.ORDER_ID = sd.ORDER_ID
            AND a.SAMPLE_ID = sd.SAMPLE_ID
            AND a.PATIENT_NO = :P60_MRN 
            AND sd.TEST_NO = p.TEST_NO 
            AND a.order_id = :P60_ORDER
            AND B.TEST_NO IN (
                SELECT REGEXP_SUBSTR(:P60_TEST_NO, '[^,]+', 1, LEVEL) AS TESTNO
                FROM DUAL
                CONNECT BY LEVEL <= REGEXP_COUNT(:P60_TEST_NO, ',') + 1
            );


    CURSOR para_tests(p_test_no IN NUMBER) IS
    select TEST_NO,TEST_NAME,REFERENCE_RANGE, TEST_UNIT , SERIAL
    from LAB_TEMPLATE_DETAILS para 
    where para.test_no = p_test_no;
  
   v_profile_exists number; -- Variable to check if the TEST_NO belongs to a profile
   v_para_exists number; -- Variable to check if the TEST_NO belongs to a parasitology
BEGIN
    
    FOR i IN p1 LOOP
             -- Check if the current TEST_NO is part of a template
        SELECT COUNT(*) INTO v_para_exists
        FROM LAB_TEMPLATE_DETAILS
        WHERE test_no = i.TEST_NO;
        
        IF v_para_exists > 0 THEN
            -- TEST_NO is part of a parasitology, insert only parasitology tests
            FOR p IN para_tests(i.TEST_NO) LOOP
                INSERT INTO LAB_PARA_RESULTS
                (
                    ORDER_ID, SAMPLE_ID, PATIENT_NO, TEST_NO, TEST_NAME, TEST_RESULT , REFERENCE_RANGE , 
                    TEST_UNIT , SERIAL , EXAMINED_BY, EXAMINED_DATE, APPROVED_BY, APPROVED_DATE, CUST_NO , SAMPLE_STATUS ) 
                    VALUES (
                    i.ORDER_ID, -- ORDER_ID from cursor
                    i.SAMPLE_ID, -- SAMPLE_ID from cursor
                    i.PATIENT_NO, -- PATIENT_NO from cursor
                    p.TEST_NO, -- TEST_NO from para 
                    i.TEST_NAME ,
                    NULL , 
                    i.REFERENCE_RANGE , 
                    i.TEST_UNIT , 
                    i.SERIAL , -- SERIAL
                    NULL , -- EXAMINED_BY
                    NULL, -- EXAMINED_DATE
                    NULL, -- APPROVED_BY
                    NULL , -- APPROVED_DATE 
                    i.CUST_NO, -- CUST_NO from cursor
                    3 -- SAMPLE_STATUS
                                       
                );
            END LOOP;
            end if;
         END LOOP;
         
    COMMIT; -- Commit the transaction
END;

游标p1返回行数正确且无重复,游标para_tests返回行数也正确且无重复,但插入到LAB_PARA_RESULTS表时出现重复:比如p1返回10行,插入后生成了100行(每行重复10次)。需要解决循环插入时的重复问题。


问题原因

核心问题是**p1游标中存在重复的TEST_NO值**——你所说的"行无重复"是指整行数据唯一,但TEST_NO字段在多行行中可能重复。当p1中有多个行对应同一个TEST_NO时,外层循环会多次处理同一个TEST_NO,每次都触发内层para_tests循环插入相同的一组记录,最终导致重复。

另外,代码中的v_para_exists查询完全多余:因为p1游标已经关联了LAB_TEMPLATE_DETAILS p,能进入p1的行必然满足sd.TEST_NO = p.TEST_NO,也就是TEST_NO一定存在于LAB_TEMPLATE_DETAILS中,这个判断可以直接删除。


解决方案

方案1:去重处理TEST_NO,避免重复遍历

方式A:修改p1游标,确保TEST_NO唯一

由于插入时用到的i.ORDER_ID、i.SAMPLE_ID等字段对于同一个TEST_NO应该是相同的(否则业务逻辑本身存在问题),可以修改p1游标,通过DISTINCT确保TEST_NO唯一:

CURSOR p1 IS
  SELECT DISTINCT 
      sd.TEST_NO,
      b.TEST_NAME_ENG,
      a.PATIENT_NO,
      a.ORDER_ID,
      sd.SAMPLE_ID,
      a.CUST_NO,
      p.REFERENCE_RANGE, 
      p.TEST_UNIT, 
      p.SERIAL
  FROM 
      LAB_SAMPLE_HEADER a,
      LAB_TESTS b,
      LAB_SAMPLE_DETAILS sd,
      LAB_ORDERS o , 
      LAB_TEMPLATE_DETAILS p 
  WHERE 
      sd.TEST_NO = b.TEST_NO
      AND a.ORDER_ID = o.ORDER_ID
      AND sd.ORDER_ID = o.ORDER_ID
      AND a.SAMPLE_ID = sd.SAMPLE_ID
      AND a.PATIENT_NO = :P60_MRN 
      AND sd.TEST_NO = p.TEST_NO 
      AND a.order_id = :P60_ORDER
      AND B.TEST_NO IN (
          SELECT REGEXP_SUBSTR(:P60_TEST_NO, '[^,]+', 1, LEVEL) AS TESTNO
          FROM DUAL
          CONNECT BY LEVEL <= REGEXP_COUNT(:P60_TEST_NO, ',') + 1
      );

方式B:记录已处理的TEST_NO,跳过重复项

声明集合变量存储已处理的TEST_NO,每次循环前检查是否已处理,避免重复执行内层插入:

DECLARE
  -- 定义存储已处理TEST_NO的集合
  TYPE test_no_tab IS TABLE OF NUMBER;
  v_processed_tests test_no_tab := test_no_tab();
  
  CURSOR p1 IS
    -- 保持原游标定义不变
    SELECT DISTINCT 
        sd.TEST_NO,
        b.TEST_NAME_ENG,
        a.PATIENT_NO,
        a.ORDER_ID,
        sd.SAMPLE_ID,
        a.SECTION_ID,
        b.SAMPLE_TYPE,
        b.TEST_CONTAINER,
        b.TEST_VOLUME,
        a.SAMPLE_STATUS,
        a.CUST_NO,
        DECODE(o.ORDER_PRIORITY, 1, 'URGENT', 2, 'ROUTINE') AS ORDER_PRIORITY , 
        p.TEST_NAME,p.REFERENCE_RANGE, p.TEST_UNIT , p.SERIAL
    FROM 
        LAB_SAMPLE_HEADER a,
        LAB_TESTS b,
        LAB_SAMPLE_DETAILS sd,
        LAB_ORDERS o , 
        LAB_TEMPLATE_DETAILS p 
    WHERE 
        sd.TEST_NO = b.TEST_NO
        AND a.ORDER_ID = o.ORDER_ID
        AND sd.ORDER_ID = o.ORDER_ID
        AND a.ORDER_ID = sd.ORDER_ID
        AND a.SAMPLE_ID = sd.SAMPLE_ID
        AND a.PATIENT_NO = :P60_MRN 
        AND sd.TEST_NO = p.TEST_NO 
        AND a.order_id = :P60_ORDER
        AND B.TEST_NO IN (
            SELECT REGEXP_SUBSTR(:P60_TEST_NO, '[^,]+', 1, LEVEL) AS TESTNO
            FROM DUAL
            CONNECT BY LEVEL <= REGEXP_COUNT(:P60_TEST_NO, ',') + 1
        );

  CURSOR para_tests(p_test_no IN NUMBER) IS
    select TEST_NO,TEST_NAME,REFERENCE_RANGE, TEST_UNIT , SERIAL
    from LAB_TEMPLATE_DETAILS para 
    where para.test_no = p_test_no;
  
BEGIN
  FOR i IN p1 LOOP
    -- 检查当前TEST_NO是否已处理
    IF i.TEST_NO NOT MEMBER OF v_processed_tests THEN
      -- 添加到已处理集合
      v_processed_tests.EXTEND;
      v_processed_tests(v_processed_tests.LAST) := i.TEST_NO;
      
      -- 执行内层插入
      FOR p IN para_tests(i.TEST_NO) LOOP
          INSERT INTO LAB_PARA_RESULTS
          (
              ORDER_ID, SAMPLE_ID, PATIENT_NO, TEST_NO, TEST_NAME, TEST_RESULT , REFERENCE_RANGE , 
              TEST_UNIT , SERIAL , EXAMINED_BY, EXAMINED_DATE, APPROVED_BY, APPROVED_DATE, CUST_NO , SAMPLE_STATUS ) 
              VALUES (
              i.ORDER_ID,
              i.SAMPLE_ID,
              i.PATIENT_NO,
              p.TEST_NO,
              i.TEST_NAME_ENG,
              NULL , 
              p.REFERENCE_RANGE,
              p.TEST_UNIT,
              p.SERIAL,
              NULL ,
              NULL,
              NULL,
              NULL ,
              i.CUST_NO,
              3
          );
      END LOOP;
    END IF;
  END LOOP;
  COMMIT;
END;

方案2:用单条INSERT语句替代嵌套循环(推荐)

嵌套循环效率低且容易出错,直接通过关联查询生成要插入的数据,一次性插入,从根源避免重复:

BEGIN
  INSERT INTO LAB_PARA_RESULTS
  (
      ORDER_ID, SAMPLE_ID, PATIENT_NO, TEST_NO, TEST_NAME, TEST_RESULT, REFERENCE_RANGE, 
      TEST_UNIT, SERIAL, EXAMINED_BY, EXAMINED_DATE, APPROVED_BY, APPROVED_DATE, CUST_NO, SAMPLE_STATUS
  )
  SELECT DISTINCT
      a.ORDER_ID,
      sd.SAMPLE_ID,
      a.PATIENT_NO,
      para.TEST_NO,
      b.TEST_NAME_ENG,
      NULL,
      para.REFERENCE_RANGE,
      para.TEST_UNIT,
      para.SERIAL,
      NULL,
      NULL,
      NULL,
      NULL,
      a.CUST_NO,
      3
  FROM 
      LAB_SAMPLE_HEADER a
      JOIN LAB_SAMPLE_DETAILS sd ON a.ORDER_ID = sd.ORDER_ID AND a.SAMPLE_ID = sd.SAMPLE_ID
      JOIN LAB_TESTS b ON sd.TEST_NO = b.TEST_NO
      JOIN LAB_ORDERS o ON a.ORDER_ID = o.ORDER_ID
      JOIN LAB_TEMPLATE_DETAILS p ON sd.TEST_NO = p.TEST_NO
      JOIN LAB_TEMPLATE_DETAILS para ON sd.TEST_NO = para.TEST_NO
  WHERE 
      a.PATIENT_NO = :P60_MRN 
      AND a.order_id = :P60_ORDER
      AND B.TEST_NO IN (
          SELECT REGEXP_SUBSTR(:P60_TEST_NO, '[^,]+', 1, LEVEL) AS TESTNO
          FROM DUAL
          CONNECT BY LEVEL <= REGEXP_COUNT(:P60_TEST_NO, ',') + 1
      );
  
  COMMIT;
END;

这种方式用JOIN替代嵌套循环,通过DISTINCT确保插入的记录唯一,同时执行效率更高。


内容的提问来源于stack exchange,提问作者Abdullah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 11:22:03