2014-10-15 4 views
0

참조 커서를 반환하는 Oracle 저장 프로 시저가 있습니다. 내가 그것을 계산하기 위해 반환하기 전에 커서를 열고 싶은 경우 예외를 throw하지만 구문에 문제가 있고 어떻게 해야하는지 메신저. 당신이 커서를 두 번 열 수오라클, 커서를 열고 하나의 열을 여러 변수로 선택하는 방법

CREATE OR REPLACE PROCEDURE YOUR_PROC(O_CURSOR OUT SYS_REFCURSOR) is 
    ASN_NO NUMBER; -- have to define all columns the cursor returns 

    V_CHECK_ASN_NO NUMBER; 

    -- local function to generate the cursor, to avoid repeating the text 
    -- or using dynamic SQL 
    FUNCTION GET_CURSOR RETURN SYS_REFCURSOR IS 
    V_CURSOR SYS_REFCURSOR; 
    BEGIN 
    OPEN V_CURSOR FOR 
     SELECT * 
     FROM AN_ORDER_INFO OI, AN_SHIPMENT_INFO SI 
     -- where bunch of stuff 
    RETURN V_CURSOR; 
    END; 
BEGIN 
    -- open the cursor for your check; might be better to have a local 
    -- variable for this rather than touching the OUT parameter this early 
    O_CURSOR := GET_CURSOR; 
    LOOP 
    FETCH O_CURSOR INTO ASN_NO; -- and all other columns! 
    EXIT WHEN O_CURSOR%NOTFOUND; 
    IF V_CHECK_ASN_NO IS NOT NULL AND V_CHECK_ASN_NO != ASN_NO THEN 
     -- means we have two distinct values 
     CLOSE O_CURSOR; 
     RAISE MULTIPLE_ASNS; 
    END IF; 
    V_CHECK_ASN_NO := ASN_NO; 
    END LOOP; 
    -- close the check version of the cursor 
    CLOSE O_CURSOR; 

    -- re-open the cursor for the caller 
    O_CURSOR := GET_CURSOR; 
END YOUR_PROC; 

: 당신이 그 위에 카운트 같은 커서 여러 번 열고 싶다면

 V_ASN_COUNT   NUMBER; 

     OPEN O_CURSOR FOR    
     SELECT column1, -- a bunch of columns 
       column2,   
       COUNT(DISTINCT SI.ASN_NO) OVER (PARTITION BY SI.ASN_NO) AS ASN_COUNT 
      FROM AN_ORDER_INFO OI, AN_SHIPMENT_INFO SI 
      WHERE -- a bunch of criteria  

     OPEN O_CURSOR; 
      LOOP 
      FETCH ASN_COUNT INTO V_ASN_COUNT; 
      END LOOP; 
     CLOSE O_CURSOR; 

     IF(V_ASN_COUNT > 1) THEN 
     RAISE MULTIPLE_ASNS; 
     END IF; 
+3

'FETCH'는 행을 사용하므로 커서를 반환하기 전에 일부 행을 가져 오는 경우 해당 행을 호출자가 반환 된 커서를 통해 더 이상 사용할 수 없습니다. 그게 받아 들일 수 있니? –

+0

나는 그것을 몰랐다. 호출자는 asn_count를 제외하고이 select 명령문이 필요한 모든 것을 필요로합니다. 이 시나리오에서 오류 처리를 위해 asn_count를 가져 오는 방법은 무엇입니까? – SomeRandomDeveloper

+0

정확히 당신의 요구 사항은 무엇입니까? _ "** ** 'ASN_COUNT> 1'이면 ** 어떤 행도 처리되지 않아 예외가 발생해야합니다 ** ** ** _? 아니면,'ASN_COUNT> 1 '이 발생하면 처리를 시작한 다음 중단하고'ROLLBACK '을 받아 들일 수 있습니까? –

답변

0

이 뭔가를 할 수있는, 이전 질문에 이어 동적 SQL을 사용하는 동일한 SQL 문자열이지만이 버전에서는 로컬 함수를 사용하여 커서 SQL 정적 (따라서 컴파일 타임에 구문 분석)을 만듭니다.

커서가 두 번 실행되고 첫 번째 실행 (중복이없는 경우 모든 행, 중복이있는 경우 모두를 가져올 수 있음)에서 적어도 일부 행을 가져옵니다. 호출자는 모든 행을 포함하는 새로운 결과 세트를 가져옵니다.

+1

트랜잭션 격리 수준이 무엇이든간에 두 커서가 동일한 행을 반환하도록 보장됩니까? –

+0

@SylvainLeroux - 아니요. 특히 데이터가 휘발성 인 경우 좋은 점입니다. 격리 수준을 변경하거나 잠금을 설정하지 않고이를 방지 할 수있는 방법이 있는지 확신하지 못합니까? 동적 SQL을 사용하더라도 커서를 되감기위한 방법이 없다고 생각합니까? –

+0

아니요, 커서를 되감기 할 수 없습니다. 나는 OP가 그 시나리오에서 최소한 반복적 인 읽기를 필요로 할까봐 두려울 것입니다 - 아니면 너무 많은 행이 없으면 코드를 _table 함수에 넣으십시오. 그리고 결과를 BULK COLLECT로 검사하여 데이터 ... –

1

난 당신이 하나를 수행 할 수 있다고 생각 : DBMS_SQL.TO_CURSOR_NUMBER 기능 :

자세한 내용은
curid NUMBER; 
    desctab DBMS_SQL.DESC_TAB; 
    colcnt NUMBER; -- total number of columns 
    res NUMBER; 
    V_ASN_COUNT NUMBER; 
BEGIN 

    OPEN O_CURSOR FOR    
    SELECT 
     column1, -- a bunch of columns 
     column2, 
     ... 
     COUNT(DISTINCT SI.ASN_NO) OVER (PARTITION BY SI.ASN_NO) AS ASN_COUNT         
    FROM AN_ORDER_INFO OI, AN_SHIPMENT_INFO SI 
    WHERE -- a bunch of criteria  

    curid := DBMS_SQL.TO_CURSOR_NUMBER (O_CURSOR); 

    DBMS_SQL.DESCRIBE_COLUMNS(curid, colcnt, desctab); 
    -- "ASN_COUNT" is the last column, i. e. "colcnt" refers to column number of "ASN_COUNT" 
    -- or set colcnt directly, e.g. colcnt := 12; 

    FOR i IN 1..colcnt LOOP 
     IF desctab(i).col_type = 2 THEN 
      DBMS_SQL.DEFINE_COLUMN(curid, i, V_ASN_COUNT); 
     ELSIF desctab(i).col_type = 12 THEN 
      DBMS_SQL.DEFINE_COLUMN(curid, i, datevar); 
      ....... 
     ELSE 
      DBMS_SQL.DEFINE_COLUMN(curid, i, namevar, 25); 
     END IF;   
    END LOOP; 
    -- I do not know if this loop is needed, perhaps you can simply do 
    -- DBMS_SQL.DEFINE_COLUMN(curid, colcnt, V_ASN_COUNT); 
    -- for a single column 

    res := DBMS_SQL.FETCH_ROWS(curid); -- Fetch only the first row, no loop required 
    DBMS_SQL.COLUMN_VALUE(curid, colcnt, V_ASN_COUNT); -- Loop over all column not required, you just like to get the last column 

    IF V_ASN_COUNT > 1 THEN 
     RAISE MULTIPLE_ASNS; 
    END IF; 
    DBMS_SQL.CLOSE_CURSOR(curid); 

는 오라클 설명서를 참조하십시오.

그러나 열린/되감기 된 커서의 문제점은 남아 있습니다!

+0

+1 'DBMS_SQL'에 대해 많이 알지 못했습니다 –

+0

아마도'O_CURSOR : = DBMS_SQL.TO_REFCURSOR (curid); '와 같은 것을 할 수 있습니다. 또는 커서를 가져 오기 전에'curid' 값의 복사본을 만들 수도 있습니다. –

0

첫 번째 행이 유효성 검사를 위해 소모적일까요?

이 코드는 커서를 한 번만 열어서 동시성 문제는 발생하지 않습니다. 커서의 두 번째 첫 번째 행은 모두 의도 한 결과 집합의 첫 번째 행을 나타냅니다. 유효성 검사를 위해 첫 번째 복사본을 가져오고 유효성 검사가 성공하면 나머지는 반환합니다.

그래도 모든 열을 가져와야합니다.

V_ASN_COUNT   NUMBER; 

    OPEN O_CURSOR FOR    
    WITH qry AS (SELECT column1, -- a bunch of columns 
          column2,   
          COUNT(DISTINCT SI.ASN_NO) OVER (PARTITION BY SI.ASN_NO) AS ASN_COUNT 
        FROM AN_ORDER_INFO OI, AN_SHIPMENT_INFO SI 
        WHERE -- a bunch of criteria 
        ) 
    SELECT * 
    FROM qry 
    WHERE rownum = 1 
    UNION ALL 
    SELECT * 
    FROM qry; 

    -- Consume the expendable first row. 
    FETCH O_CURSOR INTO V_ASN_COUNT; -- and all the other columns! 

    IF(V_ASN_COUNT > 1) THEN 
    CLOSE O_CURSOR; 
    RAISE MULTIPLE_ASNS; 
    END IF;