FOR 루프 예제에서 PlSql 커서 사용

19133 단어 plsqlsqloracle
PL/SQL 커서를 사용하기 위해 기본 테이블에서 값을 업데이트하고 이에 대한 감사 데이터도 추가하려는 시나리오를 설정했습니다.

책갈피 및 개정 정보 데이터(책갈피 감사 테이블 및 개정 정보 테이블)에 대한 데이터를 보유하기 위해 Oracle 데이터베이스에 테이블을 생성합니다.

  • BOOKMARK - 책갈피에 대한 데이터를 보관합니다
  • .

  • REVINFO - 시스템에서 발생한 개정에 대한 데이터를 보유합니다
  • .

  • BOOKMARK_AUD - 감사 테이블, 다른 개정
  • 의 영향을 받은 기본 테이블(BOOKMARK)의 데이터를 보관합니다.

    The concept from the scenarios is inspired from the Hibernate Envers project, where you would the same functionality
    in java, automatically, with annotations. There might be cases where you need to this at the SQL level, maybe for
    some single time house keeping jobs or similar...



    CREATE TABLE bookmark (
        id                 NUMBER(10, 0), -- number 10 digits before the decimal and 0 digits after the decimal
        title              VARCHAR2(255 CHAR) NOT NULL, -- String with a maximum length of 255 charachters
        url                VARCHAR2(500 CHAR) UNIQUE NOT NULL, -- holds unique values across the table data
        category      VARCHAR2(500 CHAR) NOT NULL, -- holds unique values across the table data
        is_public      NUMBER(1, 0) NOT NULL, -- plays the role of a boolean '0'-false, '1'-true ,
        created_at   DATE NOT NULL, --  when the entry is created
        PRIMARY KEY( id )
    );
    
    
    -- revision info table
    CREATE TABLE revinfo
    (
        id NUMBER(10, 0) NOT NULL,
        revtstmp NUMBER(10, 0), -- unix time in milliseconds
        PRIMARY KEY( id )
    )
    
    -- audit table for BOOKMARK table
    CREATE TABLE bookmark_aud (
        id                 NUMBER(10, 0),
        rev NUMBER(10, 0) NOT NULL, -- the id of the revision
        revtype NUMBER(1, 0), -- 0=CREATE, 1=MODIFY and 2=DELETE
        title              VARCHAR2(255 CHAR) NOT NULL,
        url                VARCHAR2(500 CHAR) UNIQUE NOT NULL,
        category      VARCHAR2(500 CHAR) NOT NULL,
        is_public      NUMBER(1, 0) NOT NULL,
        created_at   DATE NOT NULL,
        PRIMARY KEY( id )
    );
    


    더 정확히 말하면, 우리는 주어진 범주(plsql 코드에서 v_category)가 있는 책갈피 테이블의 모든 항목을 새 범주( plsql 스크립트에서 v_new_category)로 업데이트하려고 합니다. 커서(plsql 스크립트의 bookmark_cur)에서 영향을 받는 값을 선택하고 plsqlFOR LOOP에서 반복하여 개별 업데이트 및 삽입을 수행합니다. 루프에서 예외가 발생하면 DBMS_OUTPUT 다음과 같이 인쇄됩니다.

    SET SERVEROUTPUT ON;
    DECLARE
        v_category VARCHAR2(500) := 'javax';
        v_new_category VARCHAR2(500) := 'java';
        next_rev_id NUMBER := HIBERNATE_SEQUENCE.nextval;
        created_at NUMBER(19, 0);
        CURSOR bookmark_cur IS
            SELECT
                *
            FROM
                bookmark
            WHERE
              category = v_category
           );
    BEGIN
        -- get current timestamp as number
        SELECT
                    EXTRACT(DAY FROM(sys_extract_utc(systimestamp) - to_timestamp('1970-01-01', 'YYYY-MM-DD'))) * 86400000
                + to_number(TO_CHAR(sys_extract_utc(systimestamp), 'SSSSSFF3'))
        INTO created_at
        FROM
            dual;
    
        -- update REVISION table
        INSERT
        INTO revinfo (
                   rev,
                   revtstmp
        )
        VALUES (
           next_rev_id,
           created_at
        );
    
        FOR c IN bookmark_cur LOOP
            BEGIN
                -- update BOOKMARK table
                UPDATE bookmark
                SET
                    category = v_new_category
                WHERE
                     id = c.id;
    
                -- INSERT ENTRY into BOOKMARK_AUD table
                INSERT INTO bookmark_aud (
                   id
                   title,
                   url,
                   category,
                   is_public,
                   created_at,
                   rev,
                   revtype
                ) VALUES (
                   c.id
                   c.title,
                   c.url,
                   c.category,
                   c.is_public,
                   c. created_at,
                   next_rev_id,
                   1 -- 1=UPDATE/MODIFY (0=INSERT and 2=DELETE)
               );
            EXCEPTION
                WHEN OTHERS THEN --handle all exceptions
                    DBMS_OUTPUT.PUT_LINE('Exception' || SQLCODE || ' captured for bookmark with id ' || c.id);
            END;
        END LOOP;
    END;
    /
    


    그러나 본문이 쿼리가 아닌 DML(INSERT, UPDATE, DELETE, MERGE)을 실행하는 경우 커서 FOR 루프를 사용하지 않는 것이 좋습니다.
    INSERT 또는 UPDATE가 행 단위로 발생하기 때문입니다1.

    이 특별한 경우에는 커서를 버리고 그림과 같이 plsql 본문에 일반 SQL 업데이트 및 삽입을 사용할 수 있습니다.
    아래 스니펫에서:

    BEGIN
        -- get current timestamp as number
        SELECT
                    EXTRACT(DAY FROM(sys_extract_utc(systimestamp) - to_timestamp('1970-01-01', 'YYYY-MM-DD'))) * 86400000
                + to_number(TO_CHAR(sys_extract_utc(systimestamp), 'SSSSSFF3'))
        INTO created_at
        FROM
            dual;
    
        -- update REVISION table
        INSERT
            INTO revinfo (
               rev,
               revtstmp
            )
            VALUES (
               next_rev_id,
               created_at
            );
    
    
        -- update BOOKMARK table
        UPDATE bookmark
        SET
            category = v_new_category
        WHERE
            category = v_category; --
    
        -- INSERT ENTRY into BOOKMARK_AUD table
        INSERT INTO bookmark_aud (
            id
            title,
            url,
            category,
            is_public,
            created_at,
            rev,
            revtype
        )
        SELECT
            c.id
            c.title,
            c.url,
            c.category,
            c.is_public,
            c. created_at,
            next_rev_id,
            1 -- 1=UPDATE/MODIFY (0=INSERT and 2=DELETE)
        FROM bookmark c
        WHERE
            c.id in (
               SELECT
                   id
               FROM
                    bookmark
               WHERE
                 category = v_category
            )
        );
        EXCEPTION
            WHEN OTHERS THEN --handle all exceptions
                  DBMS_OUTPUT.PUT_LINE('Exception' || SQLCODE || ' captured for bookmark with id ' || c.id);
    END;
    /
    


    Note some times is not possible and then you might have to stick to the cursor. In oracle you can take advantage
    of the CURSOR bulk updates and INSERTS2



    참고문헌





    https://blogs.oracle.com/oraclemagazine/on-cursor-for-loops  

    https://blogs.oracle.com/oraclemagazine/bulk-processing-with-bulk-collect-and-forall  

    좋은 웹페이지 즐겨찾기