Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/82.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_Plsql_Cursor - Fatal编程技术网

Sql 一种更好的方法来处理游标,以避免最大游标打开超过错误

Sql 一种更好的方法来处理游标,以避免最大游标打开超过错误,sql,oracle,plsql,cursor,Sql,Oracle,Plsql,Cursor,我有一个存储过程,我认为它是在一个无限循环中捕获的,但是在将它分解并单独运行各个循环之后,结果发现我的代码导致了一个错误 ORA-01000:超过最大打开游标数 问题似乎发生在我填充前两个游标的方式上。这似乎是一个愚蠢的问题,但我希望有人能给我一些建议,如何更好地处理这些游标的人口 在下面,您将看到我的完整程序,然后我将分解似乎导致问题的区域 完整程序: procedure SplitIntrvlsAtShiftBoundaries is vRecCount number; RC nu

我有一个存储过程,我认为它是在一个无限循环中捕获的,但是在将它分解并单独运行各个循环之后,结果发现我的代码导致了一个错误

ORA-01000:超过最大打开游标数

问题似乎发生在我填充前两个游标的方式上。这似乎是一个愚蠢的问题,但我希望有人能给我一些建议,如何更好地处理这些游标的人口

在下面,您将看到我的完整程序,然后我将分解似乎导致问题的区域

完整程序:

    procedure SplitIntrvlsAtShiftBoundaries is

vRecCount number;
RC number;
vShiftIDAtIntervalStart number;
vShiftIDAtIntervalEnd number;
vDummyRecSecs number(6,4) := 0.0;

vStartTimestamp timestamp with time zone;
vEndTimestamp timestamp with time zone;

cCurs Sys_refcursor;

vCurrentRec TMP_SHIFT_LIST_TBL%rowtype;
vCurrentInterval MACHINE_INTERVAL%rowtype;
vNewInterval MACHINE_INTERVAL%rowtype;

lProcName    CONST.PBString;


vIntervalDOW smallint; -- intervals never span a midnite boundary 
                       -- are split over midnite in prior steps.

CURSOR C1 IS SELECT * FROM TMP_SHIFT_MI_TBL ORDER BY START_DATE_TIME ASC, MACHINE_ID;
CURSOR C2 IS SELECT * FROM TMP_SHIFT_LIST_TBL ORDER BY SHIFT_START_DAY ASC, SHIFT_START_TIME; 

BEGIN

  lProcName := 'TRANSFORM_PROCESS_2.SplitIntrvlsAtShiftBoundaries';
  pb_util.logdata(3, lProcName, 'Process Started ' );

  DELETE FROM TMP_SHIFT_MI_TBL;
  DELETE FROM TMP_SHIFT_LIST_TBL;

  INSERT INTO TMP_SHIFT_MI_TBL
  SELECT *
  FROM MACHINE_INTERVAL
  WHERE  interval_state = 0
  AND start_date_time <> CONST.STARTDATE
  AND  trunc(calc_end_time) < trunc( CONST.ENDDATE) - 144;

  BEGIN
    FOR R1 IN C1
      LOOP  

        -- gets the current interval from the machine interval table by using the interval id found from the tmp_table
        SELECT * INTO vCurrentInterval 
        FROM Machine_Interval MI 
        WHERE R1.Machine_Interval_ID = MI.Machine_Interval_Id; 

        -- gets the shifts containning the start and end dates obtained from the machine interval
        Shift_pkg.getShiftsContaining(R1.start_date_time, R1.calc_end_time, cCurs, vRecCount);

        FETCH cCurs INTO vCurrentRec;
          LOOP

            -- populates the tmp_shift_list_tbl dynamically
            BEGIN          
              EXIT WHEN cCurs%notfound;

              EXECUTE IMMEDIATE 'insert into tmp_shift_list_tbl
                                 (shift_id_pk, shift_name, shift_start_day, shift_start_time, 
                                  shift_end_day, shift_end_time, site_id_fk, shift_day_id,
                                  startoffset, endoffset) 
                           VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)' 
                           USING vCurrentRec.SHIFT_ID_PK, vCurrentRec.SHIFT_NAME,
                                 vCurrentRec.SHIFT_START_DAY, vCurrentRec.SHIFT_START_TIME,
                                 vCurrentRec.SHIFT_END_DAY, vCurrentRec.SHIFT_END_TIME,
                                 vCurrentRec.SITE_ID_FK, vCurrentRec.SHIFT_DAY_ID, 
                                 vCurrentRec.STARTOFFSET, vCurrentRec.ENDOFFSET;

            END;
          END LOOP;  
          pb_util.logdata(3, lProcName, 'FOUND: ', 'found ' || vRecCount ||
                          ' shifts in machine interval: ' || R1.MACHINE_INTERVAL_ID);

        -- depending on the # of shifts found in vRecCount different cases are applied.
        CASE
        WHEN vRecCount = 1 -- apply shift id to interval   
           THEN 
             BEGIN             
               FOR R2 IN C2
                 LOOP

                    UPDATE MACHINE_INTERVAL MI
                    SET MI.SHIFT_ID = R2.SHIFT_ID_PK
                    WHERE MI.MACHINE_INTERVAL_ID = R1.MACHINE_INTERVAL_ID;

                 END LOOP;
             END;      
        WHEN vRecCount > 1 -- split up the interval between the shifts i.e. create intervals
           THEN 
            BEGIN            
              FOR R2 IN C2
                LOOP  

                  -- make copy of the current interval.
                  vNewInterval := vCurrentInterval;
                  -- set the new interval duration
                  vCurrentInterval.Interval_Duration := pb_util.intervaltosecond(R2.shift_Start_Time - 
                                                                                 vCurrentInterval.Start_Date_Time);
                  -- set the new shift id for the machine interval table
                  vCurrentInterval.Shift_ID := R2.Shift_ID_PK;

                  -- update the record.
                  DM.MachineIntervalRecordUpdate(MachineIntervalID => vCurrentInterval.Machine_Interval_ID,
                                                 StartDateTime => vCurrentInterval.Start_Date_Time,                                                 
                                                 IntervalDuration => vCurrentInterval.Interval_Duration,
                                                 IntervalCategory => vCurrentInterval.INTERVAL_CATEGORY,
                                                 NPTCategoryID => vCurrentInterval.NPT_CATEGORY_ID,
                                                 NPTControlledID => vCurrentInterval.CATEGORYTYPENUMERIC,
                                                 NPTOPStateID => vCurrentInterval.OPERATIONALSTATENUMERIC,
                                                 pExecutionSecs => vDummyRecsecs);

                  UPDATE MACHINE_INTERVAL MI
                  SET MI.Shift_ID = vCurrentInterval.Shift_ID
                  WHERE MI.Machine_Interval_ID = vCurrentInterval.Machine_Interval_ID;

                   -- set new start date time & interval duration                              
                  vNewInterval.Start_Date_Time := R2.Shift_End_Time;
                  vNewInterval.Interval_Duration := pb_util.intervaltosecond(vNewInterval.Calc_End_Time - 
                                                                             vNewInterval.Start_Date_Time);

                  -- create new record in interval table.
                  RC := DM.MachineIntervalRecordInsert(MachineIntervalRecord_IN => vNewInterval, 
                                                       pExecutionSecs => vDummyRecsecs);

                  -- set current interval to the newly created interval and loop.
                  vCurrentInterval := vNewInterval;

                END LOOP;
            END;      
        ELSE -- if no shifts are found then set id to null

          UPDATE MACHINE_INTERVAL MI
          SET SHIFT_ID = 0
          WHERE MI.MACHINE_INTERVAL_ID = R1.MACHINE_INTERVAL_ID;

        END CASE;   
      END LOOP;
      pb_util.logdata(3, lProcName, 'Process Completed ' );
      COMMIT;
  END;

exception when others then
pb_util.logdata(1, 'TRANSFORM_PROCESS_2.SplitIntrvlsAtShiftBoundaries', 'EXCEPTION THROWN (880B)', SQLERRM || ' STACK: ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);

END SplitIntrvlsAtShiftBoundaries;
程序似乎到达打开的最大光标的区域超过:

 FOR R1 IN C1
  LOOP  

    -- gets the current interval from the machine interval table by using the interval id found from the tmp_table
    SELECT * INTO vCurrentInterval 
    FROM Machine_Interval MI 
    WHERE R1.Machine_Interval_ID = MI.Machine_Interval_Id; 

    -- gets the shifts containning the start and end dates obtained from the machine interval
    Shift_pkg.getShiftsContaining(R1.start_date_time, R1.calc_end_time, cCurs, vRecCount);

    FETCH cCurs INTO vCurrentRec;
      LOOP

        -- populates the tmp_shift_list_tbl dynamically
        BEGIN          
          EXIT WHEN cCurs%notfound;

          EXECUTE IMMEDIATE 'insert into tmp_shift_list_tbl
                             (shift_id_pk, shift_name, shift_start_day, shift_start_time, 
                              shift_end_day, shift_end_time, site_id_fk, shift_day_id,
                              startoffset, endoffset) 
                       VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)' 
                       USING vCurrentRec.SHIFT_ID_PK, vCurrentRec.SHIFT_NAME,
                             vCurrentRec.SHIFT_START_DAY, vCurrentRec.SHIFT_START_TIME,
                             vCurrentRec.SHIFT_END_DAY, vCurrentRec.SHIFT_END_TIME,
                             vCurrentRec.SITE_ID_FK, vCurrentRec.SHIFT_DAY_ID, 
                             vCurrentRec.STARTOFFSET, vCurrentRec.ENDOFFSET;

        END;
      END LOOP;  
      pb_util.logdata(3, lProcName, 'FOUND: ', 'found ' || vRecCount ||
                      ' shifts in machine interval: ' || R1.MACHINE_INTERVAL_ID);

隐式游标永远不会生成ORA-01000错误,因为Oracle会在它们超出范围时关闭它们

代码中的问题在于
cCurs
Shift\u pkg.getshiftcscontaining
打开光标,代码从光标中提取。但是,当您完成从游标中提取时,没有代码关闭游标。当您完成从光标中提取数据时,您的代码将需要关闭光标

代码本身有点混乱。通常,您会从循环中的游标获取。但是,您发布的代码在循环外部进行获取,这意味着如果
cCurs
返回一行,您将有一个无限循环。因此,无论是
cCurs
FETCH
时都不会返回任何数据,或者您有一个无限循环,或者您发布的代码与实际运行的代码不完全相同

通常情况下,您会有类似这样的内容,它将从循环中的游标获取数据,在没有更多行可用时退出循环,然后关闭游标

LOOP
  FETCH cCurs INTO vCurrentRec;
  EXIT WHEN cCurs%notfound;

  EXECUTE IMMEDIATE 'insert into tmp_shift_list_tbl
                     (shift_id_pk, shift_name, shift_start_day, shift_start_time, 
                      shift_end_day, shift_end_time, site_id_fk, shift_day_id,
                      startoffset, endoffset) 
               VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)' 
               USING vCurrentRec.SHIFT_ID_PK, vCurrentRec.SHIFT_NAME,
                     vCurrentRec.SHIFT_START_DAY, vCurrentRec.SHIFT_START_TIME,
                     vCurrentRec.SHIFT_END_DAY, vCurrentRec.SHIFT_END_TIME,
                     vCurrentRec.SITE_ID_FK, vCurrentRec.SHIFT_DAY_ID, 
                     vCurrentRec.STARTOFFSET, vCurrentRec.ENDOFFSET;
END LOOP;

CLOSE cCurs;

尽管如此,这里似乎没有任何理由使用动态SQL。为变量选择更有意义的名称也很有帮助:
C1
R1
C2
R2
、和
ccur
都不是真正有意义的名称。您通常希望将代码分成更小的块,以便阅读和消化。

隐式游标永远不会生成ORA-01000错误,因为Oracle会在它们超出范围时关闭它们

代码中的问题在于
cCurs
Shift\u pkg.getshiftcscontaining
打开光标,代码从光标中提取。但是,当您完成从游标中提取时,没有代码关闭游标。当您完成从光标中提取数据时,您的代码将需要关闭光标

代码本身有点混乱。通常,您会从循环中的游标获取。但是,您发布的代码在循环外部进行获取,这意味着如果
cCurs
返回一行,您将有一个无限循环。因此,无论是
cCurs
FETCH
时都不会返回任何数据,或者您有一个无限循环,或者您发布的代码与实际运行的代码不完全相同

通常情况下,您会有类似这样的内容,它将从循环中的游标获取数据,在没有更多行可用时退出循环,然后关闭游标

LOOP
  FETCH cCurs INTO vCurrentRec;
  EXIT WHEN cCurs%notfound;

  EXECUTE IMMEDIATE 'insert into tmp_shift_list_tbl
                     (shift_id_pk, shift_name, shift_start_day, shift_start_time, 
                      shift_end_day, shift_end_time, site_id_fk, shift_day_id,
                      startoffset, endoffset) 
               VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)' 
               USING vCurrentRec.SHIFT_ID_PK, vCurrentRec.SHIFT_NAME,
                     vCurrentRec.SHIFT_START_DAY, vCurrentRec.SHIFT_START_TIME,
                     vCurrentRec.SHIFT_END_DAY, vCurrentRec.SHIFT_END_TIME,
                     vCurrentRec.SITE_ID_FK, vCurrentRec.SHIFT_DAY_ID, 
                     vCurrentRec.STARTOFFSET, vCurrentRec.ENDOFFSET;
END LOOP;

CLOSE cCurs;

尽管如此,这里似乎没有任何理由使用动态SQL。为变量选择更有意义的名称也很有帮助:
C1
R1
C2
R2
、和
ccur
都不是真正有意义的名称。通常,您会希望将代码分成更小的块,以便于阅读和消化。

为什么
EXCECUTE IMMEDIATE
插入数据。为什么不直接插入…呢?@Luc老实说,当时我的导师建议我使用它,而不是直接使用插入。为什么他要我改用它,我记不起来了,只是当时听起来很合理。我对Oracle的知识只掌握了这么多,但我在过程中没有看到光标上有一个
close
。为什么
EXCECUTE IMMEDIATE
用于插入数据。为什么不直接插入…呢?@Luc老实说,当时我的导师建议我使用它,而不是直接使用插入。为什么他要我改用它,我记不起来了,只是当时听起来很合理。我对Oracle的知识只掌握了这么多,但在这个过程中,我没有看到光标上有一个
close