SQL Triggers
SQL Triggers Interview with follow-up questions
1. What is a SQL Trigger and when is it used?
A SQL trigger is a stored database object that automatically executes a specified block of SQL when a defined event occurs on a table or view.
Trigger events:
INSERT— fires when a row is addedUPDATE— fires when a row is modifiedDELETE— fires when a row is removed- Some databases also support
TRUNCATEand DDL-level triggers
Trigger timing:
BEFORE— fires before the operation; can modify the data being written or cancel the operationAFTER— fires after the operation; used for auditing, cascading updates, or notificationsINSTEAD OF— replaces the operation entirely; commonly used on views
Common use cases:
Audit logging — record who changed what and when
-- After UPDATE on employees, write to audit_logEnforcing business rules — e.g., prevent salary reductions greater than 20%
Derived column maintenance — auto-update a
last_modifiedtimestampCascading changes — propagate changes to related tables not covered by FK cascades
Important caveats for interviews:
- Triggers execute implicitly — their effects can be invisible to developers unfamiliar with the schema, making debugging harder
- Triggers can fire other triggers (cascading triggers), which can cause unexpected behavior and performance issues
BEFOREtriggers in MySQL cannot directly modify the table they are defined on- Triggers are not a substitute for application-level validation — they are a last line of defense
Alternatives in 2026: Many teams prefer application-level logic, database constraints, or event-driven architectures (change data capture) over triggers, reserving triggers for cross-cutting concerns like audit trails.
Follow-up 1
Can you explain the different types of SQL triggers?
There are three types of SQL triggers:
BEFORE Triggers: These triggers are executed before the triggering event occurs. They can be used to modify the values of the data being inserted or updated.
AFTER Triggers: These triggers are executed after the triggering event occurs. They can be used to perform actions such as updating related tables or logging changes.
INSTEAD OF Triggers: These triggers are executed instead of the triggering event. They are commonly used with views to allow updates on the view that affect the underlying tables.
Follow-up 2
What is the difference between a FOR and AFTER trigger?
The main difference between a FOR and AFTER trigger is the timing of their execution.
FOR Triggers: These triggers are executed before the triggering event occurs. They can be used to modify the values of the data being inserted or updated.
AFTER Triggers: These triggers are executed after the triggering event occurs. They can be used to perform actions such as updating related tables or logging changes.
In summary, FOR triggers are executed before the event and can modify the data, while AFTER triggers are executed after the event and can perform actions based on the modified data.
Follow-up 3
Can you provide an example of a situation where a trigger would be useful?
Sure! Let's say we have a database table called 'orders' and another table called 'order_items'. Whenever a new order is inserted into the 'orders' table, we want to automatically update the 'order_items' table with the corresponding items for that order. We can achieve this using a trigger. Here's an example:
CREATE TRIGGER update_order_items
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
INSERT INTO order_items (order_id, item_id, quantity)
VALUES (NEW.order_id, NEW.item_id, NEW.quantity);
END;
In this example, the trigger 'update_order_items' is executed after an INSERT operation on the 'orders' table. It inserts a new row into the 'order_items' table using the values from the newly inserted row in the 'orders' table.
Follow-up 4
What are the potential downsides of using triggers?
While triggers can be useful in certain scenarios, they also have some potential downsides:
Performance Impact: Triggers can introduce overhead and impact the performance of database operations, especially if they involve complex logic or perform actions on multiple tables.
Implicit Logic: Triggers can make it harder to understand and maintain the logic of a database system, as they introduce implicit actions that are not always obvious.
Dependency Management: Triggers can create dependencies between database objects, making it more difficult to modify or refactor the database schema.
Debugging and Testing: Triggers can be harder to debug and test compared to explicit code, as they are executed automatically and may have complex dependencies.
It's important to carefully consider the use of triggers and weigh the benefits against these potential downsides.
2. How do you create a trigger in SQL?
Trigger syntax varies by database. Here is the pattern across the most common systems:
PostgreSQL (two-step: function + trigger):
-- Step 1: Create the trigger function
CREATE OR REPLACE FUNCTION log_salary_change()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO salary_audit (employee_id, old_salary, new_salary, changed_at)
VALUES (OLD.id, OLD.salary, NEW.salary, NOW());
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Step 2: Attach to the table
CREATE TRIGGER trg_salary_audit
AFTER UPDATE OF salary ON employees
FOR EACH ROW
EXECUTE FUNCTION log_salary_change();
MySQL:
CREATE TRIGGER trg_salary_audit
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
IF OLD.salary != NEW.salary THEN
INSERT INTO salary_audit (employee_id, old_salary, new_salary, changed_at)
VALUES (OLD.id, OLD.salary, NEW.salary, NOW());
END IF;
END;
SQL Server:
CREATE TRIGGER trg_salary_audit
ON employees
AFTER UPDATE
AS
BEGIN
INSERT INTO salary_audit (employee_id, old_salary, new_salary, changed_at)
SELECT d.id, d.salary, i.salary, GETDATE()
FROM deleted d
JOIN inserted i ON d.id = i.id
WHERE d.salary != i.salary;
END;
Key concepts:
OLD/deleted— virtual table containing the row values before the operationNEW/inserted— virtual table containing the row values after the operationFOR EACH ROW— row-level trigger (vs statement-level in PostgreSQL withFOR EACH STATEMENT)
Follow-up 1
What is the syntax for creating a trigger?
The syntax for creating a trigger in SQL is as follows:
CREATE TRIGGER trigger_name
{BEFORE | AFTER} {INSERT | UPDATE | DELETE} ON table_name
[FOR EACH ROW]
[WHEN (condition)]
BEGIN
-- Trigger logic goes here
END;
trigger_nameis the name of the triggerBEFOREorAFTERspecifies when the trigger should executeINSERT,UPDATE, orDELETEspecifies the event that triggers the executiontable_nameis the name of the table on which the trigger is createdFOR EACH ROWspecifies that the trigger should be executed for each row affected by the eventWHEN (condition)is an optional condition that must be satisfied for the trigger to executeBEGINandENDenclose the trigger logic
Follow-up 2
Can you provide an example of a trigger creation statement?
Sure! Here's an example of a trigger creation statement in SQL:
CREATE TRIGGER update_salary
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
IF NEW.salary > OLD.salary THEN
INSERT INTO salary_history (employee_id, old_salary, new_salary, change_date)
VALUES (NEW.employee_id, OLD.salary, NEW.salary, NOW());
END IF;
END;
In this example, the trigger named update_salary is created to execute after an UPDATE operation on the employees table. It inserts a record into the salary_history table whenever the salary of an employee is increased.
Follow-up 3
What are the key components of a trigger creation statement?
The key components of a trigger creation statement in SQL are as follows:
CREATE TRIGGER: This is the statement used to create a trigger.trigger_name: This is the name of the trigger.BEFOREorAFTER: This specifies when the trigger should execute.INSERT,UPDATE, orDELETE: This specifies the event that triggers the execution.table_name: This is the name of the table on which the trigger is created.FOR EACH ROW: This specifies that the trigger should be executed for each row affected by the event.WHEN (condition): This is an optional condition that must be satisfied for the trigger to execute.BEGINandEND: These keywords enclose the trigger logic.
3. What is the difference between a row-level trigger and a statement-level trigger?
Row-level triggers fire once for each row affected by the DML statement. If an UPDATE touches 500 rows, the trigger executes 500 times.
Statement-level triggers fire once per SQL statement, regardless of how many rows are affected — even zero.
-- Row-level (PostgreSQL syntax)
CREATE TRIGGER trg_row_level
AFTER UPDATE ON employees
FOR EACH ROW
EXECUTE FUNCTION some_function();
-- Statement-level (PostgreSQL syntax)
CREATE TRIGGER trg_stmt_level
AFTER UPDATE ON employees
FOR EACH STATEMENT
EXECUTE FUNCTION some_function();
| Aspect | Row-Level | Statement-Level |
|---|---|---|
| Fires | Once per affected row | Once per DML statement |
| Access to OLD/NEW values | Yes (OLD.col, NEW.col) |
No (no per-row data) |
| Use case | Audit individual row changes, validate per-row logic | Log that a bulk operation occurred, update summary tables |
| Performance | Can be slow for bulk operations | More efficient for high-volume DML |
| Database support | All major databases | PostgreSQL, Oracle, SQL Server; MySQL supports only row-level |
MySQL limitation: MySQL only supports FOR EACH ROW — there is no statement-level trigger in MySQL.
Oracle distinction: Oracle's AFTER STATEMENT triggers can see the entire affected set but cannot access individual OLD/NEW values.
Interview nuance: If you need to prevent a bulk UPDATE that affects more than a threshold number of rows, a statement-level trigger is the right choice (it fires once and can count the affected rows). A row-level trigger for this purpose would fire on each row and is much less efficient.
Follow-up 1
Can you provide an example of a situation where a row-level trigger would be used?
Sure! Let's say we have a table called 'Orders' with a column called 'TotalAmount'. We want to enforce a business rule that states that the 'TotalAmount' cannot exceed a certain threshold for each individual order. In this case, we can create a row-level trigger that is fired for each row affected by an INSERT or UPDATE statement on the 'Orders' table. The trigger can then check the 'TotalAmount' value for each row and raise an error if it exceeds the threshold.
Follow-up 2
Can you provide an example of a situation where a statement-level trigger would be used?
Certainly! Let's consider a scenario where we have a table called 'Employees' with a column called 'Salary'. We want to track the total salary expense for each DML statement that affects the 'Salary' column. In this case, we can create a statement-level trigger that is fired once for each DML statement on the 'Employees' table. The trigger can then calculate the total salary expense by summing up the 'Salary' values for all affected rows and store it in a separate table or variable.
Follow-up 3
What are the potential downsides of using a row-level trigger?
There are a few potential downsides of using a row-level trigger. Firstly, since the trigger is fired for each affected row, it can significantly impact the performance of DML statements that affect a large number of rows. This is because the trigger logic is executed multiple times. Additionally, row-level triggers can make the code more complex and harder to maintain, especially when dealing with complex business rules that involve multiple tables or conditions.
Follow-up 4
What are the potential downsides of using a statement-level trigger?
Using a statement-level trigger also has its downsides. One potential downside is that the trigger is fired only once for each DML statement, regardless of the number of rows affected. This means that if the trigger logic needs to perform some action for each individual row, it may not be possible to achieve that using a statement-level trigger. Additionally, statement-level triggers can be more difficult to debug and troubleshoot, as they operate on a set of rows rather than individual rows.
4. What is a mutating trigger in SQL?
A mutating table error (ORA-04091 in Oracle) occurs when a row-level trigger tries to query or modify the same table that caused the trigger to fire. Because the table is in an intermediate state mid-DML, reading it can produce inconsistent results.
Why it happens (Oracle specifically):
-- This triggers ORA-04091 in Oracle
CREATE OR REPLACE TRIGGER check_salary_trigger
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
-- Querying the same table that fired the trigger = mutating table
SELECT AVG(salary) INTO :new.avg_dept_salary
FROM employees
WHERE department_id = :NEW.department_id;
END;
The employees table is currently being updated; querying it mid-update gives an undefined state.
Solutions:
Use a compound trigger (Oracle 11g+): Collect affected rows in a package-level collection during the row-level phase, then perform the table query in the statement-level phase after the DML completes.
Use a statement-level trigger instead: Statement-level triggers fire after all row changes are done, so the table is no longer mutating.
Refactor to application logic: Avoid the trigger entirely and handle the constraint in the application or a stored procedure.
Other databases:
- PostgreSQL: Does not use the term "mutating trigger" but has similar constraints. A
BEFORErow trigger can modify its own table's row viaRETURN NEW, but querying the same table in a row-level trigger can see the pre-statement state. - SQL Server: Less restrictive — row-level triggers can query the triggering table, but the
insertedanddeletedvirtual tables should be used instead.
Follow-up 1
What causes a trigger to mutate?
A trigger can mutate when it tries to read or modify the same table that it is triggered by. This can happen when the trigger's action includes SQL statements that reference the triggering table.
Follow-up 2
What are the potential issues with mutating triggers?
There are several potential issues with mutating triggers:
- Performance: Mutating triggers can cause performance issues because they can result in recursive triggers being fired multiple times.
- Data integrity: Mutating triggers can lead to data integrity issues if the trigger's actions are not properly designed.
- Error handling: Mutating triggers can make error handling more complex, as they can cause 'mutating table' errors that need to be handled.
Follow-up 3
How can you avoid mutating trigger errors?
There are several ways to avoid mutating trigger errors:
- Use row-level triggers: Instead of using statement-level triggers, which can cause mutating table errors, use row-level triggers that operate on individual rows.
- Use compound triggers: Compound triggers can be used to combine multiple trigger actions into a single trigger, reducing the chances of a mutating table error.
- Redesign the trigger logic: If possible, redesign the trigger logic to avoid reading or modifying the same table that triggers the trigger.
- Use temporary tables or views: Instead of directly referencing the triggering table, use temporary tables or views to perform the required operations.
- Use autonomous transactions: In some cases, using autonomous transactions can help avoid mutating trigger errors by allowing the trigger to perform its actions independently of the main transaction.
5. Can you disable a trigger in SQL? If so, how?
Yes — triggers can be disabled and re-enabled without dropping them. Syntax varies by database:
SQL Server:
-- Disable a specific trigger
DISABLE TRIGGER trg_salary_audit ON employees;
-- Re-enable it
ENABLE TRIGGER trg_salary_audit ON employees;
-- Disable ALL triggers on a table
DISABLE TRIGGER ALL ON employees;
-- Enable ALL triggers on a table
ENABLE TRIGGER ALL ON employees;
PostgreSQL:
-- Disable (requires superuser or table owner)
ALTER TABLE employees DISABLE TRIGGER trg_salary_audit;
-- Disable all triggers on the table
ALTER TABLE employees DISABLE TRIGGER ALL;
-- Re-enable
ALTER TABLE employees ENABLE TRIGGER trg_salary_audit;
MySQL:
MySQL does not support ENABLE/DISABLE trigger syntax directly. To disable a trigger in MySQL, you must either:
- Drop it (
DROP TRIGGER trigger_name) and recreate it later - Use a session variable as a flag inside the trigger body to conditionally skip execution:
sql -- Inside trigger body: IF @disable_trigger IS NULL THEN -- trigger logic here END IF; -- Then to disable: SET @disable_trigger = 1;
Oracle:
ALTER TRIGGER trg_salary_audit DISABLE;
ALTER TRIGGER trg_salary_audit ENABLE;
ALTER TABLE employees DISABLE ALL TRIGGERS;
Common use case for disabling triggers: Bulk data loads — disabling triggers during an ETL/migration avoids row-by-row trigger overhead and speeds up the load significantly. Always re-enable and verify data integrity afterward.
Follow-up 1
What is the syntax for disabling a trigger?
To disable a trigger in SQL, you can use the DISABLE TRIGGER statement followed by the name of the trigger and the table it is associated with. Here is the syntax:
DISABLE TRIGGER trigger_name ON table_name;
Follow-up 2
Can you provide an example of a situation where you might want to disable a trigger?
One example of a situation where you might want to disable a trigger is during data migration or bulk data updates. Disabling the trigger can help improve performance by temporarily bypassing the trigger logic and avoiding unnecessary trigger executions.
Follow-up 3
What happens when a trigger is disabled?
When a trigger is disabled, it will not be triggered or executed when the associated table event occurs. This means that any actions or logic defined in the trigger will not be performed. Disabling a trigger can be useful in scenarios where you want to temporarily suspend the trigger's functionality without permanently removing it.
Live mock interview
Mock interview: SQL Triggers
- Read your scene and goals
- Talk it out; goals tick off live
- Get a score and stronger lines
Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.