Save Big on Cyber Monday! Up to 40% Off
ends in   {{days}}
Days
{{timeFormat.hours}}
:
{{timeFormat.minutes}}
:
{{timeFormat.seconds}}

Triggers in MySQL: Definition and How to Use Them

Triggers in MySQL are automated rules that run SQL to keep your data consistent when you insert, update, or delete data in a table. Constraints catch the simple checks, but triggers handle more difficult validation, such as syncing a sales_totals table or automatically recording every user role change to an audit_log table.

Generally, you'll want your main business logic in the app layer, but using triggers lets you guarantee these rules are always followed right when the database changes.

Let's break down what MySQL triggers are, their types, practical uses, and how to use them with step-by-step examples.

What is a trigger in MySQL?

A MySQL trigger is a database object that activates whenever an INSERT, UPDATE, or DELETE event happens on a table. It fires before or after the operation you define, and only if you're using SQL queries, not APIs or tools that skip SQL entirely.

You define them using a CREATE TRIGGER statement:

DELIMITER $$

CREATE TRIGGER trigger_name
trigger_time trigger_event
ON table_name
FOR EACH ROW
BEGIN
    -- trigger logic here
END;
$$

DELIMITER ;

This doesn't need to be called like a stored procedure. MySQL triggers react to changes automatically, row by row. This means that if you insert 1,000 rows in a batch, the trigger will run 1,000 times, which, coupled with executing its logic, can cause overhead. That's why they're generally used for things where constraints or app code alone won't do (like enforcing rules with subqueries, audit logging, and cross-table dependencies).

Types of triggers in MySQL

Triggers in MySQL are categorized based on when they activate (BEFORE or AFTER) and what they activate for (INSERT, UPDATE, or DELETE). That gives you six possible types of triggers.

BEFORE INSERT trigger

A BEFORE INSERT trigger runs before a new row is added. So, if you want to ensure product codes are uppercase before saving to a products table, you'd write:

DELIMITER $$

CREATE TRIGGER before_product_insert
BEFORE INSERT ON products
FOR EACH ROW
BEGIN
    SET NEW.code = UPPER(NEW.code);
END;
$$

DELIMITER ;

AFTER INSERT trigger

This trigger activates right after a row gets inserted. Suppose you have an orders table and want to maintain a running total of all orders in another table. You'd do the following:

DELIMITER $$

CREATE TRIGGER update_order_total_after_insert
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
    UPDATE order_summary SET total_amount = total_amount + NEW.amount;
END;
$$

DELIMITER ;

BEFORE UPDATE trigger

This one is similar to BEFORE INSERT, used to prevent invalid data or apply rules before a row is updated. For instance, to stop anyone from setting an employee's hire date to the past in payroll, you'd run:

DELIMITER $$

CREATE TRIGGER before_employee_update
BEFORE UPDATE ON employees
FOR EACH ROW
BEGIN
    SET NEW.hire_date = OLD.hire_date;
END;
$$

DELIMITER ;

AFTER UPDATE trigger

Use an AFTER UPDATE trigger for logging changes, syncing related tables, or auditing data after a row is modified. Below you have an example to track price changes in a products table:

DELIMITER $$

CREATE TRIGGER after_price_update
AFTER UPDATE ON products
FOR EACH ROW
BEGIN
    IF OLD.price != NEW.price THEN
        INSERT INTO price_log (product_id, old_price, new_price, updated_at)
        VALUES (NEW.id, OLD.price, NEW.price, NOW());
    END IF;
END;
$$

DELIMITER ;

BEFORE DELETE trigger

A BEFORE DELETE trigger runs before a row is removed — great for archiving data or preventing unwanted deletions.

DELIMITER $$

CREATE TRIGGER before_customer_delete
BEFORE DELETE ON customers
FOR EACH ROW
BEGIN
    INSERT INTO archived_customers (id, name, email, deleted_at)
    VALUES (OLD.id, OLD.name, OLD.email, NOW());
END;
$$

DELIMITER ;

Here, we're saving customer data before it's deleted, copying their details into an archives table.

AFTER DELETE trigger

An AFTER DELETE trigger runs after a row's gone, which is useful for audit logs, syncing other tables, or triggering alerts. For example, you can update an inventory count after removing a product from the products table using:

DELIMITER $$

CREATE TRIGGER after_product_delete
AFTER DELETE ON products
FOR EACH ROW
BEGIN
    UPDATE inventory SET stock = stock - 1 WHERE product_id = OLD.id;
END;
$$

DELIMITER ;

How to create a trigger in MySQL

As we've seen above, there are a few things we need to define to create a trigger with CREATE trigger:

  • trigger_name: Unique, like before_sales_insert
  • trigger_time: BEFORE or AFTER
  • trigger_event: INSERT, UPDATE, or DELETE
  • table_name: The target table
  • body: The SQL logic between BEGIN and END, using NEW/OLD to access row data

NEW holds the new or updated data you can modify in INSERT or UPDATE triggers, while OLD only lets you look at the row's values before an UPDATE or DELETE.

Let's see this with another example. We'll create a trigger to cap discounts at 30% before inserting into a sales table.

  1. Ensure you have root privileges in MySQL.
  2. Create the table. In this case, we run:
CREATE TABLE sales (
    sale_id INT PRIMARY KEY AUTO_INCREMENT,
    discount DECIMAL(5,2)
);
  1. Define the trigger.
DELIMITER $$

CREATE TRIGGER before_sales_insert
BEFORE INSERT ON sales
FOR EACH ROW
BEGIN
    IF NEW.discount > 30 THEN
        SET NEW.discount = 30;
    END IF;
END;
$$

DELIMITER ;

We use DELIMITER here because MySQL's default semicolon would confuse the parser inside the trigger's BEGIN...END block. If you want a simpler trigger with just one statement, you don't need it, but it's always a good practice.

Common errors and debugging triggers

There are a few tricky issues to watch out for when working with MySQL triggers, such as:

  • Recursive updates: If you log changes in an orders table and the trigger tries updating the same table, MySQL will block it. Move the update to a stored procedure or a separate table to avoid this.
  • Invalid OLD/NEW access: Always verify OLD for DELETE/UPDATE and NEW for INSERT/UPDATE via SHOW TRIGGERS.
  • Slow triggers: They can slow down DML operations, so simplify your code and index referenced tables to speed things up a bit.

Practical MySQL trigger examples

Take a look below to see some other examples you can use as templates for your own projects.

Using triggers for audit logs

This is an AFTER UPDATE trigger that logs salary changes to an audit table anytime an employee record is updated:

DELIMITER $$

CREATE TRIGGER after_employee_update
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
    INSERT INTO audit_log (employee_id, old_salary, new_salary, updated_by, updated_at)
    VALUES (OLD.id, OLD.salary, NEW.salary, USER(), NOW());
END;
$$

DELIMITER ;

Automating timestamp updates with triggers

Here's a BEFORE UPDATE trigger on an employees table that sets a last_modified column to the current timestamp whenever a record is updated.

DELIMITER $$

CREATE TRIGGER update_last_modified
BEFORE UPDATE ON employees
FOR EACH ROW
BEGIN
    SET NEW.last_modified = NOW();
END;
$$

DELIMITER ;

Restricting invalid data with triggers

The following BEFORE INSERT trigger checks NEW.quantity in the inventory table and throws an error if invalid.

DELIMITER $$

CREATE TRIGGER before_inventory_insert
BEFORE INSERT ON inventory
FOR EACH ROW
BEGIN
    IF NEW.quantity < 0 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Quantity cannot be negative';
    END IF;
END;
$$

DELIMITER ;

Best practices for creating and using triggers

Triggers are powerful for automating tasks and enforcing rules, but piling on too many can hurt performance and complicate maintenance.

Optimizing trigger performance

Keep triggers simple to minimize overhead and make sure any tables they touch have good indexes.

Also, batching actions outside triggers, when you can, is way better for performance than doing things row by row and causing slowdowns.

Managing triggers in large databases

Managing triggers in large environments gets a lot easier with clear documentation and consistent naming. Here are other good habits:

  • Use SHOW TRIGGERS and INFORMATION_SCHEMA.TRIGGERS to monitor triggers.
  • When you have multiple triggers on the same event, control their order with FOLLOWS or PRECEDES.
  • Stick to essential tasks in triggers and move more complex logic to your app or scheduled jobs.
  • Always test triggers thoroughly to catch hidden bugs and avoid slowdowns.

Introducing dbForge Studio for MySQL

As we've seen, working with triggers in MySQL can be tough. The good news is, dbForge Studio for MySQL makes it easier with an intuitive MySQL management GUI that lets you define, handle, and debug triggers visually.

Using the MySQL Table Designer, you can quickly build or modify tables and triggers with just a few clicks. It comes with a bunch of tools to make your work less of a headache, such as auto-generated SQL scripts and real-time syntax checks. There's also a built-in MySQL debugger that helps you step through trigger logic and easily find errors.

When you're ready to deploy, use the Schema Compare and sync features to automatically generate and apply the necessary update scripts. And if performance ever becomes an issue, the Explain Plan tool will show you exactly how your triggers are running, making it easy to pinpoint and fix any slowdowns.

The best way to see how all this works to improve your workflow is to try it for yourself. Download the 30-day free trial today.

Conclusion

A trigger in MySQL helps automate INSERTs, UPDATEs, and DELETEs, especially if you need to reject invalid data automatically, enforcing audit trails, maintaining referential integrity, or syncing related tables. Use them sparingly, though, and always have a solid rollback plan and thorough testing before deploying to production.

Now, go ahead and implement your own triggers using the examples we've covered as a guide. Download dbForge Studio for MySQL free version to speed up the whole process. It'll simplify your workflow and make managing your database much easier.

FAQ

Which types of triggers are supported by MySQL?
MySQL supports BEFORE and AFTER triggers for the following DML events: INSERT, UPDATE, DELETE. Each trigger type can be created to execute once for each row affected by the DML operation. However, unlike SQL Server, MySQL does not support DDL triggers (e.g., for events like CREATE, ALTER, or DROP). SQL Server allows DDL triggers to respond to schema changes, but MySQL lacks this feature as of the latest versions.
Why should I use triggers in MySQL instead of handling logic in my application?
Triggers enforce rules and automate actions directly at the database level, which helps maintain consistent business logic across all applications accessing the database, automate repetitive tasks like timestamping updates or auditing changes, catch invalid data before it enters your tables, and reduce dependency on application code for data integrity.
How do I create a MySQL trigger that prevents duplicate records?
While triggers can't fully prevent duplicates (since constraint violations are better handled with UNIQUE keys), you can use a BEFORE INSERT trigger to check for duplicates and raise an error.
Can MySQL triggers call stored procedures or functions?
Yes, triggers can call stored functions, but with restrictions: they cannot call stored procedures directly, and they can call stored functions, as long as those functions don't modify data (i.e., no INSERT/UPDATE/DELETE inside the function).
How can I debug a trigger in MySQL when it's not working correctly?
Debugging triggers in plain MySQL is tricky since you can't step through them. But you can add logging statements to another table to track trigger behavior, use SHOW TRIGGERS, or check INFORMATION_SCHEMA.TRIGGERS to inspect the logic and check for errors in trigger logic, like referencing non-existent fields or failing conditions. Also, if you're using dbForge Studio for MySQL, you can visually examine the layout and debug triggers using its Call Stack window, which makes this burdensome task much easier.
How do I visually create and manage triggers in MySQL with dbForge Studio?
dbForge Studio for MySQL offers a visual interface to create, edit, and delete triggers from the Table Designer, use built-in syntax checking to prevent mistakes, step through logic using the MySQL Debugger, and manage all triggers in one place via the Database Explorer.
Can I simulate trigger execution in dbForge Studio for MySQL before applying changes?
Yes. dbForge Studio for MySQL includes a range of built-in features that let you define, handle, and debug triggers visually. Using the MySQL Table Designer, you can quickly build or modify tables and triggers with just a few clicks, debug them, and preview the results.

dbForge Studio for MySQL

The most intelligent tool for MySQL development and management