그러므로 이를 Trigger나 Package등으로 설정을 하여야 하는데
이를 간단하게 사용할 수 있는 Package가 있다.
PL/SQL로 다음의 Package를 생성한다.
CREATE OR REPLACE PACKAGE UPDATE_CASCADE
AS
PROCEDURE ON_TABLE( P_TABLE_NAME IN VARCHAR2,
P_PRESERVE_ROWID IN BOOLEAN DEFAULT TRUE,
P_USE_DBMS_OUTPUT IN BOOLEAN DEFAULT FALSE );
END UPDATE_CASCADE;
/
CREATE OR REPLACE PACKAGE BODY UPDATE_CASCADE
AS
TYPE CNAMEARRAY IS TABLE OF USER_CONS_COLUMNS.COLUMN_NAME%TYPE
INDEX BY BINARY_INTEGER;
SQL_STMT VARCHAR2(32000);
USE_DBMS_OUTPUT BOOLEAN DEFAULT FALSE;
PRESERVE_ROWID BOOLEAN DEFAULT TRUE;
FUNCTION Q( S IN VARCHAR2 ) RETURN VARCHAR2
IS
BEGIN
RETURN '"'||S||'"';
END Q;
FUNCTION PKG_NAME( S IN VARCHAR2 ) RETURN VARCHAR2
IS
BEGIN
RETURN Q( 'U' || S || 'P' );
END PKG_NAME;
FUNCTION VIEW_NAME( S IN VARCHAR2 ) RETURN VARCHAR2
IS
BEGIN
RETURN Q( 'U' || S || 'V' );
END VIEW_NAME;
FUNCTION TRIGGER_NAME( S IN VARCHAR2, S2 IN VARCHAR2 ) RETURN VARCHAR2
IS
BEGIN
RETURN Q( 'U' || S || S2 );
END TRIGGER_NAME;
FUNCTION STRIP( S IN VARCHAR2 ) RETURN VARCHAR2
IS
BEGIN
RETURN LTRIM(RTRIM(S));
END STRIP;
PROCEDURE ADD( S IN VARCHAR2 )
IS
BEGIN
IF ( USE_DBMS_OUTPUT ) THEN
DBMS_OUTPUT.PUT_LINE( CHR(9) || S );
ELSE
SQL_STMT := SQL_STMT || CHR(10) || S;
END IF;
END ADD;
PROCEDURE EXECUTE_IMMEDIATE
AS
EXEC_CURSOR INTEGER DEFAULT DBMS_SQL.OPEN_CURSOR;
ROWS_PROCESSED NUMBER DEFAULT 0;
BEGIN
IF ( USE_DBMS_OUTPUT ) THEN
DBMS_OUTPUT.PUT_LINE( '/' );
ELSE
DBMS_SQL.PARSE(EXEC_CURSOR, SQL_STMT, DBMS_SQL.NATIVE );
ROWS_PROCESSED := DBMS_SQL.EXECUTE(EXEC_CURSOR);
DBMS_SQL.CLOSE_CURSOR( EXEC_CURSOR );
DBMS_OUTPUT.PUT_LINE(
SUBSTR( SQL_STMT, 2, INSTR( SQL_STMT,CHR(10),2)-2 ) );
SQL_STMT := NULL;
END IF;
EXCEPTION
WHEN OTHERS THEN
IF DBMS_SQL.IS_OPEN(EXEC_CURSOR) THEN
DBMS_SQL.CLOSE_CURSOR(EXEC_CURSOR);
END IF;
RAISE;
END;
PROCEDURE GET_PKEY_NAMES
( P_TABLE_NAME IN OUT USER_CONSTRAINTS.TABLE_NAME%TYPE,
P_PKEY_NAMES OUT CNAMEARRAY,
P_CONSTRAINT_NAME IN OUT USER_CONSTRAINTS.CONSTRAINT_NAME%TYPE )
IS
BEGIN
SELECT TABLE_NAME, CONSTRAINT_NAME
INTO P_TABLE_NAME, P_CONSTRAINT_NAME
FROM USER_CONSTRAINTS
WHERE ( TABLE_NAME = P_TABLE_NAME
OR TABLE_NAME = UPPER(P_TABLE_NAME) )
AND CONSTRAINT_TYPE = 'P' ;
FOR X IN ( SELECT COLUMN_NAME , POSITION
FROM USER_CONS_COLUMNS
WHERE CONSTRAINT_NAME = P_CONSTRAINT_NAME
ORDER BY POSITION ) LOOP
P_PKEY_NAMES( X.POSITION ) := X.COLUMN_NAME;
END LOOP;
END GET_PKEY_NAMES;
PROCEDURE WRITE_SPEC( P_TABLE_NAME IN USER_CONSTRAINTS.TABLE_NAME%TYPE,
P_PKEY_NAMES IN CNAMEARRAY )
IS
L_COMMA CHAR(1) DEFAULT ' ';
BEGIN
ADD( 'CREATE OR REPLACE PACKAGE ' || PKG_NAME(P_TABLE_NAME) );
ADD( 'AS' );
ADD( '--' );
ADD( ' ROWCNT NUMBER DEFAULT 0;' );
ADD( ' INTRIGGER BOOLEAN DEFAULT FALSE;' );
ADD( '--' );
FOR I IN 1 .. 16 LOOP
BEGIN
ADD( ' TYPE C' || STRIP(I) || '_TYPE IS TABLE OF ' ||
Q(P_TABLE_NAME) || '.' || Q(P_PKEY_NAMES(I)) ||
'%TYPE INDEX BY BINARY_INTEGER;' );
ADD( '--' );
ADD( ' EMPTY_C' || STRIP(I) || ' C' || STRIP(I) || '_TYPE;' );
ADD( ' OLD_C' || STRIP(I) || ' C' || STRIP(I) || '_TYPE;' );
ADD( ' NEW_C' || STRIP(I) || ' C' || STRIP(I) || '_TYPE;' );
ADD( '--' );
EXCEPTION
WHEN NO_DATA_FOUND THEN EXIT;
END;
END LOOP;
ADD( '--' );
ADD( ' PROCEDURE RESET;' );
ADD( '--' );
ADD( ' PROCEDURE DO_CASCADE;' );
ADD( '--' );
ADD( ' PROCEDURE ADD_ENTRY' );
ADD( ' ( ' );
FOR I IN 1 .. 16 LOOP
BEGIN
ADD( ' ' || L_COMMA || 'P_OLD_C' || STRIP(I) || ' IN ' ||
Q(P_TABLE_NAME) || '.' || Q(P_PKEY_NAMES(I)) || '%TYPE ' );
L_COMMA := ',';
ADD( ' ,P_NEW_C' || STRIP(I) || ' IN OUT ' ||
Q(P_TABLE_NAME) || '.' || Q(P_PKEY_NAMES(I)) || '%TYPE ' );
EXCEPTION
WHEN NO_DATA_FOUND THEN EXIT;
END;
END LOOP;
ADD( ' );' );
ADD( '--' );
ADD( 'END ' || PKG_NAME(P_TABLE_NAME) || ';' );
END WRITE_SPEC;
PROCEDURE WRITE_BODY
( P_TABLE_NAME IN USER_CONSTRAINTS.TABLE_NAME%TYPE,
P_PKEY_NAMES IN CNAMEARRAY,
P_CONSTRAINT_NAME IN USER_CONSTRAINTS.CONSTRAINT_NAME%TYPE )
IS
L_COL_CNT NUMBER DEFAULT 0;
L_COMMA CHAR(1) DEFAULT ' ';
L_PKEY_STR VARCHAR2(2000);
L_PKEY_NAME_STR VARCHAR2(2000);
L_OTHER_COL_STR VARCHAR2(2000);
BEGIN
ADD( 'CREATE OR REPLACE PACKAGE BODY ' || PKG_NAME(P_TABLE_NAME) );
ADD( 'AS' );
ADD( '--' );
ADD( ' PROCEDURE RESET ' );
ADD( ' IS' );
ADD( ' BEGIN' );
ADD( '--' );
ADD( ' IF ( INTRIGGER ) THEN RETURN; END IF;' );
ADD( '--' );
ADD( ' ROWCNT := 0;' );
FOR I IN 1 .. 16 LOOP
BEGIN
IF (P_PKEY_NAMES(I) = P_PKEY_NAMES(I)) THEN
L_COL_CNT := L_COL_CNT+1;
END IF;
ADD( ' OLD_C' || STRIP(I) || ' := EMPTY_C' || STRIP(I) || ';' );
ADD( ' NEW_C' || STRIP(I) || ' := EMPTY_C' || STRIP(I) || ';' );
EXCEPTION
WHEN NO_DATA_FOUND THEN EXIT;
END;
END LOOP;
ADD( ' END RESET;' );
ADD( '--' );
ADD( ' PROCEDURE ADD_ENTRY ' );
ADD( ' ( ' );
FOR I IN 1 .. 16 LOOP
BEGIN
ADD( ' ' || L_COMMA || 'P_OLD_C' || STRIP(I) || ' IN ' ||
Q(P_TABLE_NAME) || '.' || Q(P_PKEY_NAMES(I)) || '%TYPE ' );
L_COMMA := ',';
ADD( ' ,P_NEW_C' || STRIP(I) || ' IN OUT ' ||
Q(P_TABLE_NAME) || '.' || Q(P_PKEY_NAMES(I)) || '%TYPE ' );
EXCEPTION
WHEN NO_DATA_FOUND THEN EXIT;
END;
END LOOP;
ADD( ' )' );
ADD( ' IS' );
ADD( ' BEGIN' );
ADD( '--' );
ADD( ' IF ( INTRIGGER ) THEN RETURN; END IF;' );
ADD( '--' );
ADD( ' IF ( ' );
FOR I IN 1 .. L_COL_CNT LOOP
IF ( I <> 1 ) THEN
ADD( ' OR' );
END IF;
ADD( ' P_OLD_C' || STRIP(I) || ' <> ' ||
'P_NEW_C' || STRIP(I) );
END LOOP;
ADD( ' ) THEN ' );
ADD( ' ROWCNT := ROWCNT + 1;' );
FOR I IN 1 .. L_COL_CNT LOOP
ADD( ' OLD_C' || STRIP(I) ||
'( ROWCNT ) := P_OLD_C' || STRIP(I) || ';' );
ADD( ' NEW_C' || STRIP(I) ||
'( ROWCNT ) := P_NEW_C' || STRIP(I) || ';' );
ADD( ' P_NEW_C' || STRIP(I) ||
' := P_OLD_C' || STRIP(I) || ';' );
END LOOP;
ADD( ' END IF;' );
ADD( ' END ADD_ENTRY;' );
ADD( '--' );
L_COMMA := ' ';
FOR I IN 1 .. L_COL_CNT LOOP
L_PKEY_STR := L_PKEY_STR||L_COMMA||'$$_C' || STRIP(I) || '(I)';
L_PKEY_NAME_STR := L_PKEY_NAME_STR || L_COMMA || Q(P_PKEY_NAMES(I));
L_COMMA := ',';
END LOOP;
FOR X IN ( SELECT COLUMN_NAME
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = P_TABLE_NAME
AND COLUMN_NAME NOT IN
( SELECT COLUMN_NAME
FROM USER_CONS_COLUMNS
WHERE CONSTRAINT_NAME = P_CONSTRAINT_NAME )
ORDER BY COLUMN_ID )
LOOP
L_OTHER_COL_STR := L_OTHER_COL_STR || ',' || Q(X.COLUMN_NAME);
END LOOP;
ADD( ' PROCEDURE DO_CASCADE' );
ADD( ' IS' );
ADD( ' BEGIN' );
ADD( '--' );
ADD( ' IF ( INTRIGGER ) THEN RETURN; END IF;' );
ADD( ' INTRIGGER := TRUE;' );
ADD( '--' );
ADD( ' FOR I IN 1 .. ROWCNT LOOP' );
ADD( ' INSERT INTO ' || P_TABLE_NAME || ' ( ' );
ADD( ' ' || L_PKEY_NAME_STR );
ADD( ' ' || L_OTHER_COL_STR || ') SELECT ');
ADD( ' ' || REPLACE( L_PKEY_STR, '$$', 'NEW' ) );
ADD( ' ' || L_OTHER_COL_STR );
ADD( ' FROM ' || Q(P_TABLE_NAME) || ' A' );
ADD( ' WHERE (' || L_PKEY_NAME_STR || ' ) = ' );
ADD( ' ( SELECT ' || REPLACE(L_PKEY_STR,'$$','OLD') );
ADD( ' FROM DUAL );' );
ADD( '--' );
IF ( PRESERVE_ROWID ) THEN
ADD( ' UPDATE ' || Q(P_TABLE_NAME) || ' SET ' );
ADD( ' ( ' || L_PKEY_NAME_STR || ' ) = ' );
ADD( ' ( SELECT ' );
FOR I IN 1 .. L_COL_CNT LOOP
IF ( I <> 1 ) THEN ADD( ' ,' ); END IF;
ADD( ' DECODE( ' || Q(P_PKEY_NAMES(I)) ||
REPLACE(', OLD_C$$(I), NEW_C$$(I), OLD_C$$(I) )', '$$',STRIP(I)) );
END LOOP;
ADD( ' FROM DUAL )' );
ADD( ' WHERE ( ' || L_PKEY_NAME_STR || ' ) =' );
ADD( ' ( SELECT ' || REPLACE(L_PKEY_STR,'$$','NEW') );
ADD( ' FROM DUAL )' );
ADD( ' OR ( ' || L_PKEY_NAME_STR || ' ) =' );
ADD( ' ( SELECT ' || REPLACE(L_PKEY_STR,'$$','OLD') );
ADD( ' FROM DUAL );' );
END IF;
FOR X IN ( SELECT TABLE_NAME, CONSTRAINT_NAME
FROM USER_CONSTRAINTS
WHERE R_CONSTRAINT_NAME = P_CONSTRAINT_NAME
AND CONSTRAINT_TYPE = 'R' ) LOOP
L_COMMA := ' ';
L_OTHER_COL_STR := '';
FOR Y IN ( SELECT COLUMN_NAME
FROM USER_CONS_COLUMNS
WHERE CONSTRAINT_NAME = X.CONSTRAINT_NAME
ORDER BY POSITION ) LOOP
L_OTHER_COL_STR := L_OTHER_COL_STR || L_COMMA || Q(Y.COLUMN_NAME);
L_COMMA := ',';
END LOOP;
ADD( '--' );
ADD( ' UPDATE ' || Q( X.TABLE_NAME ) || ' SET ');
ADD( ' ( ' || L_OTHER_COL_STR || ' ) = ' );
ADD( ' ( SELECT ' || REPLACE( L_PKEY_STR, '$$', 'NEW' ) );
ADD( ' FROM DUAL )' );
ADD( ' WHERE ( ' || L_OTHER_COL_STR || ' ) = ' );
ADD( ' ( SELECT ' ||
REPLACE( L_PKEY_STR, '$$', 'OLD' ) );
ADD( ' FROM DUAL );' );
END LOOP;
ADD( '--' );
ADD( ' DELETE FROM ' || Q(P_TABLE_NAME) );
ADD( ' WHERE ( ' || L_PKEY_NAME_STR || ' ) = ' );
ADD( ' ( SELECT ' ||
REPLACE( L_PKEY_STR, '$$', 'OLD' ) );
ADD( ' FROM DUAL);' );
ADD( ' END LOOP;' );
ADD( '--' );
ADD( ' INTRIGGER := FALSE;' );
ADD( ' RESET;' );
ADD( ' EXCEPTION' );
ADD( ' WHEN OTHERS THEN' );
ADD( ' INTRIGGER := FALSE;' );
ADD( ' RESET;' );
ADD( ' RAISE;' );
ADD( ' END DO_CASCADE;' );
ADD( '--' );
ADD( 'END ' || PKG_NAME( P_TABLE_NAME ) || ';' );
END WRITE_BODY;
PROCEDURE WRITE_BU_TRIGGER( P_TABLE_NAME IN USER_CONSTRAINTS.TABLE_NAME%TYPE,
P_PKEY_NAMES IN CNAMEARRAY )
IS
L_COMMA CHAR(1) DEFAULT ' ';
BEGIN
ADD( 'CREATE OR REPLACE TRIGGER ' || TRIGGER_NAME( P_TABLE_NAME, '1' ) );
ADD( 'BEFORE UPDATE OF ' );
FOR I IN 1 .. 16 LOOP
BEGIN
ADD( ' ' || L_COMMA || Q(P_PKEY_NAMES(I)) );
L_COMMA := ',';
EXCEPTION
WHEN NO_DATA_FOUND THEN EXIT;
END;
END LOOP;
ADD( 'ON ' || Q(P_TABLE_NAME) );
ADD( 'BEGIN ' || PKG_NAME(P_TABLE_NAME) || '.RESET; END;' );
END WRITE_BU_TRIGGER;
PROCEDURE WRITE_BUFER_TRIGGER
( P_TABLE_NAME IN USER_CONSTRAINTS.TABLE_NAME%TYPE,
P_PKEY_NAMES IN CNAMEARRAY )
IS
L_COMMA CHAR(1) DEFAULT ' ';
BEGIN
ADD( 'CREATE OR REPLACE TRIGGER '||TRIGGER_NAME( P_TABLE_NAME, '2' ) );
ADD( 'BEFORE UPDATE OF ' );
FOR I IN 1 .. 16 LOOP
BEGIN
ADD( ' ' || L_COMMA || Q(P_PKEY_NAMES(I)) );
L_COMMA := ',';
EXCEPTION
WHEN NO_DATA_FOUND THEN EXIT;
END;
END LOOP;
ADD( 'ON ' || Q(P_TABLE_NAME) );
ADD( 'FOR EACH ROW' );
ADD( 'BEGIN ' );
ADD( ' ' || PKG_NAME(P_TABLE_NAME) || '.ADD_ENTRY(' );
L_COMMA := ' ';
FOR I IN 1 .. 16 LOOP
BEGIN
ADD( ' ' || L_COMMA || ':OLD.' || Q(P_PKEY_NAMES(I)) );
ADD( ' ,:NEW.' || Q(P_PKEY_NAMES(I)) );
L_COMMA := ',';
EXCEPTION
WHEN NO_DATA_FOUND THEN EXIT;
END;
END LOOP;
ADD( ' );' );
ADD( 'END;' );
END WRITE_BUFER_TRIGGER;
PROCEDURE WRITE_AU_TRIGGER( P_TABLE_NAME IN USER_CONSTRAINTS.TABLE_NAME%TYPE,
P_PKEY_NAMES IN CNAMEARRAY )
IS
L_COMMA CHAR(1) DEFAULT ' ';
BEGIN
ADD( 'CREATE OR REPLACE TRIGGER ' || TRIGGER_NAME( P_TABLE_NAME, '3' ) );
ADD( 'AFTER UPDATE OF ' );
FOR I IN 1 .. 16 LOOP
BEGIN
ADD( ' ' || L_COMMA || Q(P_PKEY_NAMES(I)) );
L_COMMA := ',';
EXCEPTION
WHEN NO_DATA_FOUND THEN EXIT;
END;
END LOOP;
ADD( 'ON ' || Q(P_TABLE_NAME) );
ADD( 'BEGIN ' || PKG_NAME(P_TABLE_NAME) || '.DO_CASCADE; END;' );
END WRITE_AU_TRIGGER;
PROCEDURE ON_TABLE( P_TABLE_NAME IN VARCHAR2,
P_PRESERVE_ROWID IN BOOLEAN DEFAULT TRUE,
P_USE_DBMS_OUTPUT IN BOOLEAN DEFAULT FALSE )
IS
L_TABLE_NAME USER_CONSTRAINTS.TABLE_NAME%TYPE DEFAULT P_TABLE_NAME;
L_CONSTRAINT_NAME USER_CONSTRAINTS.CONSTRAINT_NAME%TYPE;
L_PKEY_NAMES CNAMEARRAY;
L_COMMA CHAR(1) DEFAULT ' ';
BEGIN
USE_DBMS_OUTPUT := P_USE_DBMS_OUTPUT;
PRESERVE_ROWID := P_PRESERVE_ROWID;
GET_PKEY_NAMES( L_TABLE_NAME, L_PKEY_NAMES, L_CONSTRAINT_NAME );
SQL_STMT := NULL;
WRITE_SPEC( L_TABLE_NAME, L_PKEY_NAMES );
EXECUTE_IMMEDIATE;
WRITE_BODY( L_TABLE_NAME, L_PKEY_NAMES, L_CONSTRAINT_NAME );
EXECUTE_IMMEDIATE;
WRITE_BU_TRIGGER( L_TABLE_NAME, L_PKEY_NAMES );
EXECUTE_IMMEDIATE;
WRITE_BUFER_TRIGGER( L_TABLE_NAME, L_PKEY_NAMES );
EXECUTE_IMMEDIATE;
WRITE_AU_TRIGGER( L_TABLE_NAME, L_PKEY_NAMES );
EXECUTE_IMMEDIATE;
END ON_TABLE;
END UPDATE_CASCADE;
/
SQL상에서 UPDATE CASCADE할 Table명을 포함하여 실행하면 끝!
만일, TBL_MEMBER의 PK를 참조하는 다른 FK들에 대하여 UPDATE CASCADE를 한다면,
EXEC UPDATE_CASCADE.ON_TABLE('TBL_MEMBER');
라고 입력하면 된다.
이 Package 외에도 여러방법이 있으므로, 참조해 보도록 한다.
출처는 http://asktom.oracle.com/tkyte/update_cascade/
펼쳐두기..
댓글 없음:
댓글 쓰기