[DB] 6-3. Advanced SQL (3)

Park Yeongseo·2024년 2월 25일
0

DB

목록 보기
9/9
post-thumbnail

서울대학교 이상구 교수님의 SNUON 강의 데이타베이스: 빅데이터 시대의 필수 정보관리 개론Database System Concepts 7th Edition의 내용을 바탕으로 정리한 내용입니다.

강의에서는 다루지 않는 내용으로, Database System Concepts의 Chapter 5를 번역 및 정리해서 포스팅합니다.

3. Triggers

트리거(trigger)는 데이터베이스 수정 시 시스템이 부작용으로서 자동으로 실행하는 구문이다. 트리거를 정의하려면,

  • 언제 트리거가 실행될지를 명시해야한다. 이는 트리거를 확인하게 하는 이벤트(event)와 트리거 실행이 일어나기 위해 만족되어야 하는 조건(condition)으로 나뉠 수 있다.
  • 트리거가 실행될 때 취할 액션(action)들을 명시해야한다.

트리거를 데이터베이스에 입력하면, 데이터베이스 시스템은 명시된 이벤트가 일어나고 그에 따른 조건이 만족될 때마다 트리거를 실행할 책임을 가지게 된다.

3-1. Need for Triggers

트리거는 SQL의 제약 메커니즘만으로는 명시할 수 없는 특정 무결성 제약을 구현하는 데에 쓰일 수 있으며, 또한 특정 조건이 만족되었을 때 사용자에게 경고하거나 특정 작업을 자동적으로 시작하는 데에도 쓰일 수 있다.

트리거가 데이터베이스 밖에서의 업데이트를 수행할 수는 없다는 데에 유의하자. 이를 위해서는 주기적으로 트리거가 영향을 주는 릴레이션을 스캔하고 그에 맞는 업데이트를 수행할, 별도의 영구적으로 돌아가는 시스템 프로세스가 필요하다. 어떤 데이터베이스 시스템은 이러한 방법으로 SQL 쿼리와 트리거를 보고 메일을 보내는 것 등을 지원하기도 한다.

3-2. Triggers in SQL

그렇다면 트리거는 어떻게 구현할 수 있을까? 여기서 보일 문법은 SQL 스탠다드로 정의된 것이지만, 대부분의 데이터베이스들은 이 문법의 비표준적 버전을 구현한다. 문법은 다를지라도 개념은 적용 가능하다. 이 비표준 구현들에 대해서는 후에 살펴볼 것이다. 각 시스템에서 트리거 문법은 해당 프로그램의 함수 및 프로시저 문법에 기반을 두고 있다.

create trigger timeslot_check1 after insert on section
referencing new row as nrow
for each row
when (nrow.time_slot_id not in (
	select time_slot_id
	from time_slot)) /* time slot id not present in time slot */
begin
	rollback
end;

create trigger timeslot_check2 after delete on timeslot
referencing old row as orow
for each row
when (orow.time_slot_id not in (
		select time_slot_id
		from time_slot) /* last tuple for time slot id deleted from time slot */
	and orow.time_slot_id in (
		select time_slot_id
		from section)) /* and time slot id still referenced from section*/
begin
	rollback
end;

위는 트리거를 통해 section 릴레이션의 time_slot_id 애트리뷰트의 참조 무결성을 보장하는 방법을 보여주고 있다. 첫 번째 트리거 정의는 트리거가 section 릴레이션에서 모든 삽입 이후에 시작함과 삽입되는 time_slot_id의 값이 유효함을 보장한다. 벌크 삽입문을 사용하면 다수의 튜플을 릴레이션에 삽입할 수 있고, 삽입되는 그 각각의 행들은 트리거 코드 안의 for each row 구문을 통해 명시적으로 돌아볼 수 있다. referencing new row as 구는 변수 nrow를 만들며, 여기에는 삽입되는 행의 값들이 저장된다.

when문은 조건을 명시한다. 시스템은 begin ... end 사이를 해당 조건을 만족하는 튜플들에 대해서만 적용한다. begin atomic ... end를 쓰면 해당 구문 내의 여러 SQL문들을 하나의 트랜잭션으로 처리할 수 있게 된다. 위 예에서는 트리커를 실행하게 하는 트랜잭션을 롤백하게 한다. 참조 무결성 제약을 위반하는 모든 트랜잭션을 롤백해, 데이터베이스 내의 데이터들이 해당 제약을 만족함을 보장할 수 있게 하는 것이다.

물론 첫 번째 트리거 정의처럼 insert에 대한 트리거만 만들 수 있는 것은 아니고, 업데이트나 삭제에 대해서도 만들 수 있다. referencing old row as는 업데이트되거나 삭제되는 행의 오래된 값들을 저장하는 변수를 만드는 데에 쓸 수 있고, 삽입에서 사용했던 referencing new row as는 업데이트에서도, 수정되는 행들에 대해 쓰일 수 있다.

많은 데이터베이스 시스템들은 여러 트리거 이벤트들을 지원하는데, 유저 애플리케이션이 DB에 접속할 때, 시스템이 내려갈 때, 혹은 시스템 세팅이 변할 때 등도 있다.

트리거는 이벤트 발생 전에도 활성화될 수 있다. 이벤트 전에 실행되는 트리거는 유효하지 않은 업데이트, 삽입, 삭제를 방지하기 위한 추가적인 제약 조건과도 같이 사용될 수 있다. 그런 유효하지 않은 액션이 일어나 오류를 일으키도록 두지 않고 문제를 바로잡기 위한 액션을 취함으로써 해당 액션들을 유효하게 만드는 것이다. 예를 들면 널 값이 들어오는 경우를 확인하고 set 구문을 이용해 그것을 특정 값으로 수정하는 경우가 있을 수 있다.

영향을 받는 각 행에 대해서가 아니라, 삽입, 삭제, 업데이트를 일으키는 모든 SQL문에 대해서도 하나의 액션을 수행할 수도 있다. 이때는 for each row 대신 for each statement 구문을 사용한다. 이 경우 referencing old table asreferencing new table as로 모든 영향 받는 행들을 포함하는 테이블(전이 테이블, transition table) 을 참조할 수 있다. 전이 테이블은 before 트리거에서는 사용될 수 없고, after 트리거에만 사용될 수 있으며, 이때는 SQL문 트리거에도 쓸 수 있고 행 트리거에도 쓸 수 있다. 이를 통해 하나의 SQL문은 전이 테이블을 바탕으로 여러 액들을 수행할 수 있게 된다.

트리거는 활성화되거나 비활성화될 수 있는데, 기본적으로는 만들어질 때 바로 활성화된다. alter trigger triger_name disable을 통해 비활성화할 수 있다. 트리거는 drop trigger trigger_name을 통해 삭제될 수도 있다.

SQL 기반 데이터베이스 시스템들은, 트리거가 비표준이었던 SQL:1999 이전부터 트리거를 다방면으로 사용해왔다. 하지만 그 결과 각 데이터베이스 시스템은 트리거를 저마다의 문법을 이용해 구현했고, 이는 비호환성으로 이어졌다. 여기서 사용한 SQL:1999의 트리거 문법은 IBM DB2와 오라클 DB 시스템의 문법과 비슷하지만 완전히 같지는 않다.

3-3. When Not to Use Triggers

트리거를 사용하는 데에는 많은 이점이 있지만, 어떤 경우에는 다른 테크닉을 사용하는 게 나을 수도 있다. 예를 들어 외래키 제약의 on delete cascade 기능은 트리거를 이용해 구현될 수도 있다. 하지만 이를 통한 구현은 품이 더 들 뿐만 아니라, 데이터베이스 사용자가 이햏하기도 더 힘들다.

트리거를 매개화된 뷰를 유지하는 데에도 쓸 수 있다. 하지만 많은 데이터베이스 시스템들이 해당 시스템에 의해 자동적으로 유지되는 매개화된뷰를 지원하게 되면서, 이를 위해서 트리거 코드를 짤 필요는 없어졌다.

트리거는 데이터베이스 복사본을 관리하기 위해서도 사용돼왔다. 삽입, 삭제, 업데이트에 대한 트리거들은 해당 릴레이션의 변화들을 기록하기 위한 릴레이션에 저장될 수 있고, 별개의 프로세스는 해당 변경 사항들을 데이터베이스의 레플리카에 복사한다. 하지만 현대 데이터베이스 시스템들이 데이터베이스 복제에 대한 자체 기능을 제공하게 되면서, 이를 위해 트리거를 만드는 것도 대부분의 경우 불필요해졌다.

트리거 사용의 다른 문제는 백업된 데이터가 로드될 때, 혹은 데이터를 백업본으로 복사할 때 의도되지 않은 트리거 액션 실행이 일어날 수 있다는 것이다. 데이터를 로드할 때, 트리거는 명시적으로 비활성화될 수 있다. 주 시스템으로부터 넘겨받을 백업 복제 시스템들의 경우, 트리거는 처음에 비활성화되고, 백업 시스템이 주 시스템으로부터 프로세싱을 넘겨받을 때 활성화되어야 한다. 대안으로, 어떤 데이터베이스 시스템은 트리거가 데이터베이스 복제 중 백업본에 대해서는 실행되지 않게 설정할 수도 있다. 또 다른 데이터베이스 시스템들은 데이터베이스가 복제본임을 표시하기 위한 시스템 변수를 사용할 수도 있다. 트리거는 이 변수를 확인하고, 만약 복제본이라면 실행되지 않고 종료된다. 두 해결법 모두 명시적으로 트리거를 활성/비활성화 할 필요를 없앤다.

트리거는 주의 깊게 사용되어야 한다. 런타임에 발견된 트리거는 해당 트리거를 일으킨 구문의 실패를 일으킬 수 있기 때문이다. 또한 한 트리거의 액션은 다른 트리거를 일으킬 수도 있다. 최악의 경우, 이는 트리거의 무한 연쇄로도 이어질 수 있다. 어떤 데이터베이스 시스템은 이러한 트리거 연쇄의 길이를 제한하고, 그보다 긴 연쇄를 트리거 오류로 생각하기도 하며, 어떤 시스템은 트리거를 일으키는 릴레이션에의 참조가 일어나는 경우 트리거를 오류로 표시하기도 한다.

트리거는 아주 유용하게 사용될 수 있지만, 대체할 수 있는 것들이 있다면 사용하지 않는 것이 좋다. 많은 트리거 애플리케이션들은 저장된 프로시저를 적절하게 사용함으로써 대체될 수 있다.

profile
박가 영서라 합니다

0개의 댓글