PL/SQL 트리거, 시퀀스, 시노님

Oracle 트리거의 유형·실행 시점·주의사항, 시퀀스로 자동 PK 생성, 12c IDENTITY 컬럼, 그리고 시노님을 이용한 객체 별칭을 다룹니다.

· 6 min read · PALDYN Team

지난 글에서 패키지의 구조와 오버로딩을 다뤘다. 이번에는 PL/SQL의 세 가지 보조 객체, 트리거·시퀀스·시노님을 함께 살펴본다.

트리거

트리거는 테이블이나 뷰에 DML이 발생했을 때, 또는 DDL·데이터베이스 이벤트가 발생했을 때 자동으로 실행되는 PL/SQL 블록이다.

트리거 실행 시점 분류

시점레벨:NEW/:OLD주요 용도
BEFORE + 행각 행:NEW 수정 가능값 보정, PK 자동 채움
AFTER + 행각 행읽기만감사 로그, 연쇄 DML
BEFORE/AFTER + 문장1회없음집계 초기화, 로깅
INSTEAD OF (뷰)각 행읽기만복잡 뷰 DML 대체

INSERTING, UPDATING, DELETING 조건부 서술어로 이벤트 유형을 구분한다.

CREATE OR REPLACE TRIGGER trg_audit_emp
BEFORE INSERT OR UPDATE OR DELETE ON employees
FOR EACH ROW
BEGIN
  IF INSERTING THEN
    :NEW.created_at := SYSDATE;
    :NEW.created_by := USER;
  END IF;
  :NEW.updated_at := SYSDATE;
  :NEW.updated_by := USER;
END trg_audit_emp;
/

Oracle 트리거 유형과 실행 시점


INSTEAD OF 트리거

조인 뷰나 집계 뷰처럼 직접 DML이 불가능한 뷰에 적용한다. 뷰에 대한 DML을 대신(INSTEAD OF) 실행한다.

-- 두 테이블 조인 뷰
CREATE OR REPLACE VIEW v_emp_dept AS
  SELECT e.employee_id, e.last_name, d.department_name
  FROM   employees e JOIN departments d
         ON e.department_id = d.department_id;

-- INSTEAD OF 트리거로 뷰에 UPDATE 허용
CREATE OR REPLACE TRIGGER trg_upd_v_emp_dept
INSTEAD OF UPDATE ON v_emp_dept
FOR EACH ROW
BEGIN
  UPDATE employees
  SET    last_name = :NEW.last_name
  WHERE  employee_id = :OLD.employee_id;
END;
/

트리거 주의사항

돌연변이 테이블(Mutating Table): 행 레벨 트리거 안에서 트리거를 발생시킨 같은 테이블을 쿼리하거나 DML하면 ORA-04091 오류가 발생한다.

-- ORA-04091 발생 예
CREATE OR REPLACE TRIGGER trg_check_dept_count
BEFORE INSERT ON employees FOR EACH ROW
DECLARE v_cnt NUMBER;
BEGIN
  SELECT COUNT(*) INTO v_cnt        -- 같은 테이블 조회: 오류!
  FROM   employees
  WHERE  department_id = :NEW.department_id;
END;

해결책은 COMPOUND 트리거로 통계를 AFTER STATEMENT 단계에서 처리하거나, 별도 패키지 전역 변수에 정보를 모아 AFTER 문장 트리거에서 일괄 처리하는 것이다.

대량 DML 성능: 수백만 행 DML 시 트리거가 행마다 실행되면 성능이 저하된다. ALTER TRIGGER trg_name DISABLE; → 대량 DML → ENABLE; 패턴을 고려한다.


시퀀스

시퀀스는 유일한 정수를 순서대로 생성하는 데이터베이스 객체다.

CREATE SEQUENCE seq_order_id
  START WITH 1000
  INCREMENT BY 1
  CACHE 20        -- 메모리에 20개 미리 생성 (성능 개선)
  NOCYCLE
  NOORDER;        -- RAC 순서 보장 불필요 시

-- 사용
INSERT INTO orders (order_id, customer_id, amount)
VALUES (seq_order_id.NEXTVAL, 100, 9900);

-- 현재 값 확인 (세션에서 NEXTVAL 1회 이상 호출 후)
SELECT seq_order_id.CURRVAL FROM dual;

CACHE 옵션은 성능상 중요하다. Oracle RAC 환경에서 NOORDER를 유지하면 각 인스턴스가 캐시에서 독립적으로 값을 소비해 성능이 높지만 순서는 보장되지 않는다. 순서가 필요하면 ORDER 옵션을 추가한다.

Oracle 12c+ IDENTITY 컬럼

Oracle 12c부터는 트리거 없이 테이블 정의에서 직접 자동 증가 컬럼을 선언할 수 있다.

CREATE TABLE products (
  product_id   NUMBER GENERATED ALWAYS AS IDENTITY
               (START WITH 1 INCREMENT BY 1 CACHE 20),
  product_name VARCHAR2(100) NOT NULL,
  CONSTRAINT pk_products PRIMARY KEY (product_id)
);

-- INSERT 시 product_id 생략 (ALWAYS는 직접 값 삽입 금지)
INSERT INTO products (product_name) VALUES ('노트북');

GENERATED ALWAYS는 직접 값 입력을 막고, GENERATED BY DEFAULT는 값을 명시하지 않을 때만 자동 생성한다.

트리거로 자동 PK + 시퀀스 + 시노님


시노님

시노님은 다른 스키마 객체에 대한 **별칭(Alias)**이다. 다른 스키마 객체를 스키마 이름 없이 접근하거나, 긴 이름을 짧게 줄이는 데 사용한다.

-- 프라이빗 시노님 (현재 사용자만)
CREATE SYNONYM emp FOR hr.employees;
SELECT * FROM emp WHERE department_id = 60;

-- 퍼블릭 시노님 (모든 사용자, DBA 권한 필요)
CREATE PUBLIC SYNONYM employees FOR hr.employees;

-- 시노님 삭제
DROP SYNONYM emp;
DROP PUBLIC SYNONYM employees;

-- 현재 시노님 목록
SELECT synonym_name, table_owner, table_name
FROM   user_synonyms
UNION ALL
SELECT synonym_name, table_owner, table_name
FROM   all_synonyms
WHERE  owner = 'PUBLIC';

Oracle은 이름 해석 순서를 로컬 객체 → 프라이빗 시노님 → 퍼블릭 시노님 순으로 처리한다. 시노님은 객체가 아니라 이름 포인터이므로, 가리키는 객체가 없어도 생성은 가능하다.


지난 글: PL/SQL 패키지

다음 글: PL/SQL 컬렉션


읽어주셔서 감사합니다. 😊