Skip to main content

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples in this guide work on VillageSQL. Install Now →
A trigger is SQL that runs automatically when a row is inserted, updated, or deleted. MySQL fires triggers before or after the data change, giving you access to the row values being written.

Creating a Trigger

A practical example — log every price change:
Drop a trigger:
List all triggers in the current database:

BEFORE vs AFTER

A BEFORE INSERT trigger can set default values or reject bad data. An AFTER INSERT trigger can write audit records after the insert succeeds.

NEW and OLD References

Modify NEW values in a BEFORE trigger to change what gets written:

Multiple Triggers Per Event

MySQL supports multiple triggers on the same table and event. The FOLLOWS and PRECEDES keywords control execution order:
Without FOLLOWS/PRECEDES, MySQL assigns execution order based on creation time.

Raising Errors in Triggers

Use SIGNAL to abort the operation and return an error to the caller:
When a BEFORE trigger signals an error, the insert/update/delete is aborted — nothing is written.

Trigger Limitations

MySQL triggers have several restrictions worth knowing:
  • Triggers cannot call stored procedures that return result sets
  • Triggers cannot use COMMIT, ROLLBACK, or SAVEPOINT directly (they participate in the surrounding transaction)
  • Triggers cannot reference the table they’re defined on (no recursive trigger calls via direct queries on the same table in most contexts)
  • AFTER triggers cannot modify NEW values — the row is already written
  • MySQL does not support statement-level triggers (every trigger is FOR EACH ROW)

When to Use Triggers

Triggers work well for:
  • Audit logging — capturing who changed what and when, regardless of application
  • Enforcing complex constraints that CHECK constraints can’t express
  • Keeping derived data in sync (denormalized summary columns, audit timestamps)
Avoid triggers when:
  • The logic is complex enough that debugging it requires tracing hidden side effects
  • Performance is critical — every trigger fires per row, on every affected statement
  • The trigger modifies data in a way that surprises application developers unaware of its existence
Hidden triggers are a common source of “why did this row change?” confusion. If a trigger exists, document it prominently at the application level, not just in the database.

Frequently Asked Questions

Do triggers fire during LOAD DATA INFILE?

Yes. INSERT triggers fire for each row loaded by LOAD DATA INFILE. If your trigger does significant work, bulk-loading through LOAD DATA will be much slower than loading into a staging table first, then copying with INSERT…SELECT (which also fires triggers, but you can temporarily disable them with SET @disable_trigger = TRUE and a conditional check inside the trigger).

Can triggers call stored procedures?

Yes, with one exception: the called procedure cannot return a result set (a SELECT without INTO). If it does, MySQL returns an error. Procedures called from triggers can use OUT parameters and SELECT...INTO.

Do triggers fire on REPLACE INTO?

REPLACE executes as a DELETE + INSERT when there’s a conflict. On conflict: BEFORE DELETE, AFTER DELETE, BEFORE INSERT, and AFTER INSERT triggers all fire on the target table. ON DELETE CASCADE rules on related tables do NOT fire from a REPLACE.

Troubleshooting

See also