OceanBase logo

OceanBase

A unified distributed database ready for your transactional, analytical, and AI workloads.

DEPLOY YOUR WAY

OceanBase Cloud

The best way to deploy and scale OceanBase

OceanBase Enterprise

Run and manage OceanBase on your infra

TRY OPEN SOURCE

OceanBase Community Edition

The free, open-source distributed database

OceanBase seekdb

Open source AI native search database

Customer Stories

Real-world success stories from enterprises across diverse industries.

View All
BY USE CASES

Mission-Critical Transactions

Global & Multicloud Application

Elastic Scaling for Peak Traffic

Real-time Analytics

Active Geo-redundancy

Database Consolidation

Resources

Comprehensive knowledge hub for OceanBase.

Blog

Live Demos

Training & Certification

Documentation

Official technical guides, tutorials, API references, and manuals for all OceanBase products.

View All
PRODUCTS

OceanBase Cloud

OceanBase Database

Tools

Connectors and Middleware

QUICK START

OceanBase Cloud

OceanBase Database

BEST PRACTICES

Practical guides for utilizing OceanBase more effectively and conveniently

Company

Learn more about OceanBase – our company, partnerships, and trust and security initiatives.

About OceanBase

Partner

Trust Center

Contact Us

International - English
中国站 - 简体中文
日本 - 日本語
Sign In
Start on Cloud

A unified distributed database ready for your transactional, analytical, and AI workloads.

DEPLOY YOUR WAY

OceanBase Cloud

The best way to deploy and scale OceanBase

OceanBase Enterprise

Run and manage OceanBase on your infra

TRY OPEN SOURCE

OceanBase Community Edition

The free, open-source distributed database

OceanBase seekdb

Open source AI native search database

Customer Stories

Real-world success stories from enterprises across diverse industries.

View All
BY USE CASES

Mission-Critical Transactions

Global & Multicloud Application

Elastic Scaling for Peak Traffic

Real-time Analytics

Active Geo-redundancy

Database Consolidation

Comprehensive knowledge hub for OceanBase.

Blog

Live Demos

Training & Certification

Documentation

Official technical guides, tutorials, API references, and manuals for all OceanBase products.

View All
PRODUCTS
OceanBase CloudOceanBase Database
ToolsConnectors and Middleware
QUICK START
OceanBase CloudOceanBase Database
BEST PRACTICES

Practical guides for utilizing OceanBase more effectively and conveniently

Learn more about OceanBase – our company, partnerships, and trust and security initiatives.

About OceanBase

Partner

Trust Center

Contact Us

Start on Cloud
编组
All Products
    • Databases
    • iconOceanBase Database
    • iconOceanBase Cloud
    • iconOceanBase Tugraph
    • iconInteractive Tutorials
    • iconOceanBase Best Practices
    • Tools
    • iconOceanBase Cloud Platform
    • iconOceanBase Migration Service
    • iconOceanBase Developer Center
    • iconOceanBase Migration Assessment
    • iconOceanBase Admin Tool
    • iconOceanBase Loader and Dumper
    • iconOceanBase Deployer
    • iconKubernetes operator for OceanBase
    • iconOceanBase Diagnostic Tool
    • iconOceanBase Binlog Service
    • Connectors and Middleware
    • iconOceanBase Database Proxy
    • iconEmbedded SQL in C for OceanBase
    • iconOceanBase Call Interface
    • iconOceanBase Connector/C
    • iconOceanBase Connector/J
    • iconOceanBase Connector/ODBC
    • iconOceanBase Connector/NET
icon

OceanBase Database

SQL - V4.3.0

    Download PDF

    OceanBase logo

    The Unified Distributed Database for the AI Era.

    Follow Us
    Products
    OceanBase CloudOceanBase EnterpriseOceanBase Community EditionOceanBase seekdb
    Resources
    DocsBlogLive DemosTraining & Certification
    Company
    About OceanBaseTrust CenterLegalPartnerContact Us
    Follow Us

    © OceanBase 2026. All rights reserved

    Cloud Service AgreementPrivacy PolicySecurity
    Contact Us
    Document Feedback
    1. Documentation Center
    2. OceanBase Database
    3. SQL
    4. V4.3.0
    iconOceanBase Database
    SQL - V 4.3.0
    SQL
    KV
    • V 4.4.2
    • V 4.3.5
    • V 4.3.3
    • V 4.3.1
    • V 4.3.0
    • V 4.2.5
    • V 4.2.2
    • V 4.2.1
    • V 4.2.0
    • V 4.1.0
    • V 4.0.0
    • V 3.1.4 and earlier

    Create a composite trigger

    Last Updated:2026-04-15 08:30:01  Updated
    share
    What is on this page
    Overview
    Limitations on composite triggers
    Create a trigger

    folded

    share

    A composite trigger can contain one to four types of the before-row, after-row, before-statement, and after-statement triggering events, or contain only one INSTEAD OF triggering event.

    Overview

    A composite trigger created on a table or editable view can be activated at multiple points in time. A composite trigger is segmented based on each point in time. Each time segment has its own executable part and exception-handling part (optional). All the parts can access a universal procedural language (PL) state. The universal state is created when the trigger statement starts being executed and destroyed after the trigger statement is executed. It is not affected even if the trigger statement causes errors.

    Limitations on composite triggers

    Compound DML triggers are subject to the following limitations:

    • OLD, NEW, and PARENT cannot appear in the declarative part, BEFORE STATEMENT part, or AFTER STATEMENT part.

    • Only the BEFORE EACH ROW part can change the value of NEW.

    • One timing point section cannot handle the exceptions raised in another timing point section.

    • If a time segment contains the GOTO statement, the destination of the GOTO statement must be in the same time segment.

    Create a trigger

    In the declarative part (optional) of a composite trigger, you can declare the variables and subprograms that can be used by all time segments of the composite trigger. When the composite trigger is activated, the declarative part runs before any time segment. The variables and subprograms exist in the time segments to which the trigger statement applies.

    A composite trigger created on an uneditable view is not a real composite trigger because it has only one time segment. The syntax for creating a simple composite trigger on an uneditable view is as follows:

    CREATE TRIGGER FOR dml_event_clause ON view_name
    [{ FOLLOWS | PRECEDES } other_trigger_name]
    COMPOUND TRIGGER
    INSTEAD OF EACH ROW IS BEGIN
    sql_statement;
    END INSTEAD OF EACH ROW;
    

    A composite trigger created on a table or editable view contains at least one time segment. If a trigger contains multiple time segments, they can be sorted in any order but the points in time cannot be duplicated. If a time segment does not exist, no operation will be performed at this point in time.

    The following table describes the time segments contained in a composite trigger.

    Time node Time segment
    Before the triggering statement runs BEFORE STATEMENT
    After the triggering statement runs AFTER STATEMENT
    Before each row affected by the triggering statement BEFORE EACH ROW
    After each row affected by the triggering statement AFTER EACH ROW

    A composite trigger has no initialization part. However, BEFORE STATEMENT can perform any initialization as needed because it runs before other time segments.

    If a composite trigger has neither the BEFORE STATEMENT part nor the AFTER STATEMENT part and its trigger statement does not affect any rows, this composite trigger will never activate.

    For example, use a composite trigger to record the changes in the child table of a table.

    1. Assume there are two tables, employees and emp_salaries.

      obclient [SYS]> CREATE TABLE employees ( 
          employee_id NUMBER PRIMARY KEY, 
          name VARCHAR2(50), 
          salary NUMBER(8,2),
          department_id NUMBER);
      
      obclient [SYS]> CREATE TABLE emp_salaries (
         emp_id NUMBER NOT NULL,
         change_date DATE   NOT NULL,
         salary NUMBER(8,2) NOT NULL,
         FOREIGN KEY (emp_id)
         REFERENCES employees (employee_id) ON DELETE CASCADE);
      

      Data in the employees table is as follows:

      obclient [SYS]> SELECT * FROM employees;
      
      +-------------+------+--------+---------------+
      | EMPLOYEE_ID | NAME | SALARY | DEPARTMENT_ID |
      +-------------+------+--------+---------------+
      |        1001 | zs   |   4400 |            50 |
      |        1002 | li   |   4840 |            50 |
      |        1003 | wong |   5104 |            50 |
      |        1004 | xin  |   5984 |            50 |
      |        1005 | mai  |   5984 |            50 |
      |        1006 | cai  |   5280 |            50 |
      |        1007 | cai  |   7040 |            50 |
      |        1008 | cai  |   7084 |            50 |
      |        1009 | deng |   6644 |            50 |
      |        1010 | yang |   6160 |            50 |
      |        1011 | qiu  |   7000 |            51 |
      +-------------+------+--------+---------------+
      11 rows in set
      
    2. Create the trigger maintain_emp_salaries.

       obclient [SYS]> delimiter /
      
      obclient [SYS]> CREATE OR REPLACE TRIGGER maintain_emp_salaries
      FOR UPDATE OF salary ON employees
        COMPOUND TRIGGER
      
      threshold CONSTANT SIMPLE_INTEGER := 7;
      
      TYPE salaries_t IS TABLE OF emp_salaries%ROWTYPE INDEX BY SIMPLE_INTEGER;
      sal  salaries_t;
      idx  SIMPLE_INTEGER := 0;
      
      PROCEDURE flush_proc IS
        n CONSTANT SIMPLE_INTEGER := sal.count();
      BEGIN
        FORALL j IN 1..n
          INSERT INTO emp_salaries VALUES sal(j);
        sal.delete();
        idx := 0;
        DBMS_OUTPUT.PUT_LINE('flush' || n || 'row');
      END flush_proc;
      
      -- AFTER EACH ROW time period:
      
      AFTER EACH ROW IS
      BEGIN
        idx := idx + 1;
        sal(idx).emp_id := :NEW.employee_id;
        sal(idx).change_date := SYSTIMESTAMP;
        sal(idx).salary := :NEW.salary;
        IF idx >= threshold THEN
          flush_proc();
        END IF;
      END AFTER EACH ROW;
      
      -- AFTER STATEMENT time period:
      
      AFTER STATEMENT IS
      BEGIN
        flush_proc();
      END AFTER STATEMENT;
      END maintain_emp_salaries;
      /
      
      obclient [SYS]> SET SERVEROUTPUT ON;
      
    3. Increase the salary of each employee in department 50 by 8%.

      obclient [SYS]> UPDATE employees
              SET salary = salary * 1.08
              WHERE department_id = 50
              /
      Query OK, 10 rows affected 
      Rows matched: 10  Changed: 10  Warnings: 0
      
      Flushed 7 rows
      Flushed 3 rows
      

      Wait for 3 seconds and then execute the following command:

      obclient [SYS]> BEGIN
        DBMS_LOCK.SLEEP(3);
        END
       /
      
    4. Increase the salary of each employee in department 50 by 10%.

      obclient [SYS]> UPDATE employees
            SET salary = salary * 1.1
            WHERE department_id = 50
          /
      Query OK, 10 rows affected 
      Rows matched: 10  Changed: 10  Warnings: 0
      
      Flushed 7 rows
      Flushed 3 rows
      
    5. View the changes in the employee table in the emp_salaries table.

      obclient [SYS]> SELECT emp_id, count(*) num
            FROM emp_salaries
            GROUP BY emp_id
          /
      

      The query result is as follows:

      +--------+------+
      | EMP_ID | NUM  |
      +--------+------+
      |   1001 |    2 |
      |   1002 |    2 |
      |   1003 |    2 |
      |   1004 |    2 |
      |   1005 |    2 |
      |   1006 |    2 |
      |   1007 |    2 |
      |   1008 |    2 |
      |   1009 |    2 |
      |   1010 |    2 |
      +--------+------+
      10 rows in set
      

    Previous topic

    Create an INSTEAD OF trigger
    Last

    Next topic

    Modify and drop a trigger
    Next
    What is on this page
    Overview
    Limitations on composite triggers
    Create a trigger