본문 바로가기

카테고리 없음

MySQL 5.5 Usage of Trigger for Data Cleansing v1.0

MySQL 5.5 Usage of Trigger for Data Cleansing v1.0

 

Date

Ver

Etc.

12.09.20

1.0

 

 

 

 

 

 

 

 

 

1.    Scenario

기존에 sync_test 라는 이름의 테이블이 있었다.

sync_test  는 다음과 같은 컬럼을 가진다.

 

sync_test(syncid + vldenddt)

 

vldenddt datetime default 값으로 ‘9999-12-31 00:00:00’ 이란 값을 가진다.

(* 이하에서 ‘9999’ 라 표현하겠다.)

 

기존에는 9999 인 값과 아닌 값이 sync_test 에 뒤섞여 있는 상황이었으나

9999 인 값을 sync_test ,

9999 가 아닌 값을 sync_test_history 에 넣길 원한다.

 

APP 단의 수정사항은 존재하지 않는다. (들어오는 쿼리형태는 동일하다는 뜻)

APP 단의 수정사항은 존재하지 않기 때문에 DML 은 모두 sync_test 를 바라본다.

 

추가로 기존 데이터에 대한 초기적재는 여기서 이야기 하지 않겠다.

사실 이 부분은 트리거 구성 후 기존 데이터에 대해

insert into sync_test_history select * from sync_test where vldenddt <> ‘9999-12-31 00:00:00’

 

위와같이 수행하면 된다.

PK 중복에 대해 걱정이 된다면 IGNORE 옵션을 추가하면 된다.

 

마지막으로 sync_test 에 대해서 delete 오퍼레이션은 존재하지 않는다고 가정한다.

 

2.    SQL for Test

테스트 초기화 구문

drop trigger trig_i_sync_test_vldenddt;

drop trigger trig_u_sync_test_vldenddt;

drop trigger trig_i_sync_test_history_vldenddt;

 

truncate table sync_test;

truncate table sync_test_history;

 

테이블 생성 구문

CREATE TABLE `sync_test` (

  `syncid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

  `vldenddt` datetime NOT NULL DEFAULT '9999-12-31 00:00:00',

PRIMARY KEY (`syncid`) USING BTREE);

 

CREATE TABLE `sync_test_history` (

  `syncid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

  `vldenddt` datetime NOT NULL DEFAULT '9999-12-31 00:00:00',

PRIMARY KEY (`syncid`) USING BTREE);

 

3.    Conditions for Creating Triggers

DML 은 알다시피 INSERT, UPDATE, DELETE 작업이 있다.

앞서 전제했듯이 DELETE 작업이 sync_test 에 대해 없기에 INSERT, UPDATE 에 대해 데이터를 어떻게 sync_test_history 에 반영할지를 생각하면 된다.

 

l  INSERT

-      vldenddt 9999 인 데이터가 들어오는 경우

ð  sync_test 에 정상적으로 데이터가 입력되면 된다. 부가적인 동작이 필요없다. 굳이 넣는다면 sync_test_history 에 잘못 들어가 있을 데이터 정리를 위해 delete 를 할 수는 있다.

-      vldenddt 9999 가 아닌 데이터가 들어오는 경우이다.

ð  9999 가 아닌 데이터는 hist 테이블로 들어가야 된다. 이 경우 들어온 데이터를 hist 테이블에 복제하고, 원본 데이터는 삭제해야 한다.

 

l  UPDATE

-      vldenddt 9999 인 데이터로 수정되는 경우가 있다.

ð  수정된 경우 새로운 데이터를 hist 테이블에 복제하고, 원래의 row 는 삭제한다.

 

4.    DDL for Creating Triggers

두 개의 트리거를 생성한다.

 

l  trig_i_sync_test_vldenddt

Insert 에 대처하기 위한 트리거이다.

9999 가 아닌 데이터 (history로 가야하는) sync_test_history 로 입력한다.

입력 후 트리거를 작동시킨 데이터를 삭제한다. (이 데이터는 9999 가 아닌 데이터로 history 로 가야하는 데이타이다.)

 

9999 인 데이터가 들어오는 경우는 원본 데이터를 건드릴 부분이 없으나, 복제테이블 (hist) 에 데이터가 잘못 들어가 있는 경우를 대비해 삭제한다.

 

l  trig_u_sync_test_vldenddt

Update 에 대처하기 위한 트리거이다.

 

이테이블에는 기본적으로 9999 인 데이터들이 모여있다. 업데이트 결과가 9999 가 아니게 된 경우는 history 로 옮겨져야 하며, 이에 대해 데이터를 history 로 복제하고 트리거를 동작시킨 데이터를 삭제한다. 데이터가 9999 인 경우에는 sync_test 에 존재해야 할 데이터로 혹시 모를 history 에 존재할 데이터를 삭제한다.

 

DELIMITER $$

USE `street`$$

DROP TRIGGER /*!50032 IF EXISTS */ `trig_i_sync_test_vldenddt` $$

CREATE

  TRIGGER `trig_i_sync_test_vldenddt` AFTER INSERT ON `sync_test`

  FOR EACH ROW BEGIN

    IF !(@DISABLE_TRIGGERS <=> 1) THEN 

      IF (NEW.vldenddt <> '9999-12-31 00:00:00') THEN

        INSERT IGNORE sync_test_history

        (

        syncid ,vldenddt

        ) VALUES

        (

        NEW.syncid ,NEW.vldenddt

        );

        DELETE IGNORE from sync_test where syncid = NEW.syncid;     

      ELSE

        DELETE IGNORE from sync_test_history where syncid = NEW.syncid;     

      END IF;        

    END IF;

  END;

$$

DELIMITER ;

 

 

 

DELIMITER $$

USE `street`$$

DROP TRIGGER /*!50032 IF EXISTS */ `trig_u_sync_test_vldenddt` $$

CREATE

  TRIGGER `trig_u_sync_test_vldenddt` AFTER UPDATE ON `sync_test`

  FOR EACH ROW BEGIN

    IF !(@DISABLE_TRIGGERS <=> 1) THEN  

      IF (NEW.vldenddt <> '9999-12-31 00:00:00') THEN

        INSERT IGNORE sync_test_history

        (

        syncid,vldenddt

        ) VALUES

        (

        NEW.syncid,NEW.vldenddt

        );

        DELETE IGNORE from sync_test where syncid = NEW.syncid;

      ELSE

        DELETE IGNORE from sync_test_history where syncid = NEW.syncid;

      END IF;        

    END IF;

  END;

$$

DELIMITER ;

 

트리거 생성 후 다음의 DML 로 내용을 검증해 보았다.

 

case#1 기대되는 효과

이 데이타는 sync_test 에만 있을 것이다.

 

insert into sync_test(vldenddt) values ('9999-12-31 00:00:00');

 

 

case#2 기대되는 효과

이 데이타는 sync_history 에만 있을 것 것이다.

 

insert into sync_test(vldenddt) values ('2012-12-31 00:00:00');

 

case#3 기대되는 효과

이 데이타는 sync_test_history 로 이전 될 것이다.

 

update sync_test

set vldenddt = '2012-12-31 00:00:00'

where vldenddt ='9999-12-31 00:00:00';

 

case#1

데이터가 sync_test 에만 존재하며 이상없다.

 

case#2

다음의 에러가 발생하며 table 에 변경은 취소되었다.

 

ERROR 1442 (HY000): Can't update table 'sync_test' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.

 

case#3

다음의 에러가 발생하며 table 에 변경은 취소되었다.

 

ERROR 1442 (HY000): Can't update table 'sync_test' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.

 

위의 1442 에러는 트리거의 sync_test 테이블을 업데이트 할 수 없습니다. 이미 trigger 를 호출한 statement 에 의해 사용되고 있습니다. 란 의미이다.

 

트리거를 유발한 rows 에 대해 삭제할 수 없는 것으로 추측한다. 실제로도 9999 가 아닌 데이터에 대해 history 테이블에 insert 후 사용하는 DELETE 구문에 대해 주석처리를 했을 때 정상 동작하였다.

 

5.    DDL for Creating Alternative Trigger

4번의 트리거는 DELETE 오퍼레이션에서 실패하였다. 회피할 방법으로는 history 테이블에 데이터가 INSERT 되었을 때 원본 테이블의 데이터를 뒤쳐 삭제하는 방법이다.

 

DELIMITER $$

USE `street`$$

DROP TRIGGER /*!50032 IF EXISTS */ `trig_i_sync_test_vldenddt` $$

CREATE

  TRIGGER `trig_i_sync_test_vldenddt` AFTER INSERT ON `sync_test`

  FOR EACH ROW BEGIN

    IF !(@DISABLE_TRIGGERS <=> 1) THEN 

      IF (NEW.vldenddt <> '9999-12-31 00:00:00') THEN

        INSERT IGNORE sync_test_history

        (

        syncid ,vldenddt

        ) VALUES

        (

        NEW.syncid ,NEW.vldenddt

        );

--        DELETE IGNORE from sync_test where syncid = NEW.syncid;     

      ELSE

        DELETE IGNORE from sync_test_history where syncid = NEW.syncid;     

      END IF;        

    END IF;

  END;

$$

DELIMITER ;

 

 

 

DELIMITER $$

USE `street`$$

DROP TRIGGER /*!50032 IF EXISTS */ `trig_u_sync_test_vldenddt` $$

CREATE

  TRIGGER `trig_u_sync_test_vldenddt` AFTER UPDATE ON `sync_test`

  FOR EACH ROW BEGIN

    IF !(@DISABLE_TRIGGERS <=> 1) THEN  

      IF (NEW.vldenddt <> '9999-12-31 00:00:00') THEN

        INSERT IGNORE sync_test_history

        (

        syncid,vldenddt

        ) VALUES

        (

        NEW.syncid,NEW.vldenddt

        );

--        DELETE IGNORE from sync_test where syncid = NEW.syncid;

      ELSE

        DELETE IGNORE from sync_test_history where syncid = NEW.syncid;

      END IF;        

    END IF;

  END;

$$

DELIMITER ;

 

 

DELIMITER $$

USE `street`$$

DROP TRIGGER /*!50032 IF EXISTS */ `trig_i_sync_test_history_vldenddt` $$

CREATE

  TRIGGER `trig_i_sync_test_history_vldenddt` AFTER INSERT ON `sync_test_history`

  FOR EACH ROW BEGIN

    IF !(@DISABLE_TRIGGERS <=> 1) THEN 

      IF (NEW.vldenddt <> '9999-12-31 00:00:00') THEN

        DELETE IGNORE from sync_test where syncid = NEW.syncid;     

      END IF;        

    END IF;

  END;

$$

DELIMITER ;

 

데이터 초기화 후 트리거를 재생성하였고, 5번의 CASE#1,2,3 를 확인해 보았다.

 

CASE#1

이상없음

 

CASE#2

5번과 동일한 에러 발생 (ERROR 1442)

 

CASE#3

5번과 동일한 에러 발생 (ERROR 1442)

 

위 결과로 보아 에러를 유발한 부분 (DELETE) 을 제3의 트리거로 분리시키더라도 같이 있을 때와 동일하게 에러가 발생함을 알 수 있었다.

 

6.    Conclusion

Trigger 를 동작시키는 액션이 INSERT, UPDATE 인 경우 그 트리거를 동작시키는 대상 ROW 에 대해 DELETE 수행이 불가능하였다.

 

다시 말해 시나리오를 DB 만으로 만족시키기 어렵다는 이야기다.

다만 sync_test 안의 데이터가 혼재되어 있는 경우에 한해서는 정기적으로 9999 가 아닌 데이터를 삭제하는 식으로 요건을 충족할 수는 있다.