Triggers help run extra actions automatically when data changes. Row-level triggers run once for each row changed. Statement-level triggers run once for the whole action.
Row-level vs statement-level triggers in PostgreSQL
Start learning this pattern below
Jump into concepts and practice - no test required
CREATE TRIGGER trigger_name { BEFORE | AFTER | INSTEAD OF } { INSERT | UPDATE | DELETE } ON table_name [ FOR EACH { ROW | STATEMENT } ] EXECUTE FUNCTION function_name();
FOR EACH ROW means the trigger runs once per row changed.
FOR EACH STATEMENT means the trigger runs once per SQL command, no matter how many rows it affects.
CREATE TRIGGER log_update AFTER UPDATE ON employees FOR EACH ROW EXECUTE FUNCTION log_employee_update();
CREATE TRIGGER audit_changes AFTER INSERT OR DELETE ON orders FOR EACH STATEMENT EXECUTE FUNCTION audit_order_changes();
This example shows two triggers on the products table. One runs after each row update and prints 'Row updated'. The other runs once after the whole update statement and prints 'Update statement executed'.
-- Create a simple table CREATE TABLE products (id SERIAL PRIMARY KEY, name TEXT, price NUMERIC); -- Create a function to count updated rows CREATE OR REPLACE FUNCTION count_updates() RETURNS TRIGGER AS $$ DECLARE counter INTEGER := 0; BEGIN IF TG_OP = 'UPDATE' THEN counter := counter + 1; END IF; RAISE NOTICE 'Row updated'; RETURN NEW; END; $$ LANGUAGE plpgsql; -- Row-level trigger: runs once per updated row CREATE TRIGGER product_update_row AFTER UPDATE ON products FOR EACH ROW EXECUTE FUNCTION count_updates(); -- Statement-level trigger function CREATE OR REPLACE FUNCTION statement_update_notice() RETURNS TRIGGER AS $$ BEGIN RAISE NOTICE 'Update statement executed'; RETURN NULL; END; $$ LANGUAGE plpgsql; -- Statement-level trigger: runs once per update statement CREATE TRIGGER product_update_statement AFTER UPDATE ON products FOR EACH STATEMENT EXECUTE FUNCTION statement_update_notice(); -- Insert sample data INSERT INTO products (name, price) VALUES ('Pen', 1.20), ('Notebook', 2.50); -- Update data to see triggers in action UPDATE products SET price = price + 1 WHERE id IN (1, 2);
Row-level triggers can slow down operations if many rows are affected because they run once per row.
Statement-level triggers are good for actions that only need to happen once per command.
Use RAISE NOTICE in PostgreSQL to see messages during trigger execution for learning or debugging.
Row-level triggers run once for each row affected by a command.
Statement-level triggers run once for the entire command, no matter how many rows change.
Choose the trigger type based on whether you need per-row or per-command actions.
Practice
Solution
Step 1: Understand trigger execution scope
Row-level triggers run once for every row affected by the SQL command, meaning if 10 rows are updated, the trigger runs 10 times.Step 2: Understand statement-level trigger behavior
Statement-level triggers run only once per SQL command, regardless of how many rows are affected.Final Answer:
Row-level triggers execute once for each affected row; statement-level triggers execute once per SQL statement. -> Option CQuick Check:
Row-level = per row, Statement-level = per statement [OK]
- Confusing which trigger runs per row vs per statement
- Thinking row-level triggers run only once per statement
- Assuming statement-level triggers run per row
- Believing trigger types depend on operation type (INSERT/UPDATE)
Solution
Step 1: Identify correct trigger syntax
The syntax for creating a row-level trigger requires the clause FOR EACH ROW to specify it runs per affected row.Step 2: Check full syntax correctness
CREATE TRIGGER trg AFTER INSERT ON table FOR EACH ROW EXECUTE FUNCTION func(); correctly includes AFTER INSERT, ON table, FOR EACH ROW, and EXECUTE FUNCTION func(); which is the proper syntax.Final Answer:
CREATE TRIGGER trg AFTER INSERT ON table FOR EACH ROW EXECUTE FUNCTION func(); -> Option AQuick Check:
Row-level triggers use FOR EACH ROW [OK]
- Omitting FOR EACH ROW for row-level triggers
- Using FOR EACH STATEMENT for row-level triggers
- Missing EXECUTE FUNCTION keyword
- Incorrect order of clauses
CREATE FUNCTION trg_func() RETURNS trigger AS $$ BEGIN RAISE NOTICE 'Triggered'; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg AFTER UPDATE ON employees FOR EACH ROW EXECUTE FUNCTION trg_func(); UPDATE employees SET salary = salary * 1.1 WHERE department = 'Sales';What will be the output when the UPDATE affects 3 rows?
Solution
Step 1: Identify trigger type and execution count
The trigger is defined FOR EACH ROW, so it runs once for every row updated.Step 2: Calculate total trigger executions
Since 3 rows are updated, the trigger function runs 3 times, each raising the notice 'Triggered'.Final Answer:
The notice 'Triggered' will appear 3 times. -> Option AQuick Check:
Row-level trigger runs per row = 3 notices [OK]
- Assuming notice appears only once per statement
- Confusing FOR EACH ROW with FOR EACH STATEMENT
- Thinking AFTER UPDATE triggers don't raise notices
- Believing trigger runs multiple times per row
Solution
Step 1: Understand trigger definition impact
If a trigger runs multiple times per row update, it is likely defined as FOR EACH ROW, not FOR EACH STATEMENT.Step 2: Verify PostgreSQL trigger capabilities
PostgreSQL supports both row-level and statement-level triggers; statement-level triggers run once per statement.Final Answer:
You accidentally defined the trigger as FOR EACH ROW instead of FOR EACH STATEMENT. -> Option DQuick Check:
FOR EACH ROW triggers run per row, causing multiple executions [OK]
- Believing statement-level triggers run per row
- Ignoring trigger definition syntax
- Assuming PostgreSQL lacks statement-level triggers
- Blaming trigger function code without checking trigger type
Solution
Step 1: Determine trigger timing for logging after update
Logging after the update completes requires an AFTER trigger.Step 2: Choose trigger level for single summary message
To log once per statement regardless of rows, use a statement-level trigger (FOR EACH STATEMENT).Final Answer:
An AFTER UPDATE statement-level trigger -> Option BQuick Check:
Summary logging = AFTER + statement-level trigger [OK]
- Using row-level triggers causing multiple logs
- Using BEFORE triggers missing final state
- Confusing timing and level for logging
- Assuming row-level triggers can log once per statement
