A trigger:
can run the PL/pgSQL trigger function in the database. *A SQL trigger function and trigger procedure don't exist.
is fired when
INSERT
,UPDATE
,DELETE
orTRUNCATE
operation happens or is prevented on a table or view in the database.
*The doc explains a trigger.
*My post explains an event trigger.
For example, you create person
table as shown below:
CREATE TABLE person (
id INT,
first_name VARCHAR(20),
last_name VARCHAR(20),
age INT
);
Then, you insert 2 rows into person
table as shown below:
INSERT INTO person (id, first_name, last_name, age)
VALUES (1, 'John', 'Smith', 27), (2, 'David', 'Miller', 32);
Next, you create log
table as shown below:
CREATE TABLE log (
num INT
);
Then, you insert the row whose num
is 0
into log
table as shown below:
INSERT INTO log (num) VALUES (0);
Now, you can create my_func()
trigger function with RETURNS trigger
and LANGUAGE plpgsql
which increments num
by 1 as shown below:
CREATE FUNCTION my_func() RETURNS trigger
AS $$
BEGIN
UPDATE log SET num = num + 1;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
*Memos:
-
An trigger function:
- must have no parameters otherwise there is the error.
- must be called with a trigger otherwise there is error.
- cannot be created with
LANGUAGE SQL
otherwise there is the error. - must have
trigger
inRETURNS <type>
clause to be used with an trigger otherwise there is error. - must return either
NULL
or a record/row value having exactly the structure of the table the trigger was fired for.
A trigger procedure doesn't exist.
The doc explains a trigger function.
My post explains how to create a PL/pgSQL function.
And now, you can create my_t
trigger which runs my_func()
after INSERT
or UPDATE
operation happens on person
table for each row as shown below:
CREATE TRIGGER my_t AFTER INSERT OR UPDATE ON person
FOR EACH ROW EXECUTE FUNCTION my_func();
Or:
CREATE TRIGGER my_t AFTER INSERT OR UPDATE ON person
FOR /* EACH */ ROW EXECUTE FUNCTION my_func();
*Memos:
You can set
BEFORE
instead ofAFTER
to runmy_func()
beforeUPDATE
orINSERT
operation happens onperson
table for each row. *You can also setINSTEAD OF
which I explain later.You can set one or more events
INSERT
,UPDATE
andDELETE
withFOR EACH ROW
orFOR EACH STATEMENT
for a function or view andTRUNCATE
only withFOR EACH STATEMENT
only for a function. *You can set multiple events withOR
.You can omit
EACH
which is optional so with and withoutEACH
is the same.Under
my_func()
returningNULL
, if you setBEFORE
withFOR EACH ROW
,INSERT
orUPDATE
operation set onperson
table doesn't occur, thennum
is incremented by 1.
Then, you insert 2 rows to person
table, then num
is 2
as shown below:
postgres=# INSERT INTO person (id, first_name, last_name, age) VALUES (3, 'Robert', 'Wilson', 18), (4, 'Mark', 'Taylor', 40);
INSERT 0 2
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
1 | John | Smith | 27
2 | David | Miller | 32
3 | Robert | Wilson | 18
4 | Mark | Taylor | 40
(4 rows)
postgres=# SELECT num FROM log;
num
-----
2
(1 row)
Then, you update first_name
from Robert
to Tom
and last_name
from Wilson
to Brown
where id
is 3
on person
table, then num
is 3
as shown below:
postgres=# UPDATE person SET first_name = 'Tom', last_name = 'Brown' WHERE id = 3;
UPDATE 1
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
1 | John | Smith | 27
2 | David | Miller | 32
4 | Mark | Taylor | 40
3 | Tom | Brown | 18
(4 rows)
postgres=# SELECT num FROM log;
num
-----
3
(1 row)
Then, you delete all 4 rows from person
table, then num
is still 3
as shown below:
postgres=# DELETE FROM person;
DELETE 4
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
(0 rows)
postgres=# SELECT num FROM log;
num
-----
3
(1 row)
Next, you can create my_t
trigger which runs my_func()
before UPDATE
or TRUNCATE
operation happens on first_name
or last_name
on person
table or on person
table respectively for each statement as shown below:
CREATE TRIGGER my_t BEFORE UPDATE OF first_name, last_name OR TRUNCATE ON person
FOR EACH STATEMENT EXECUTE FUNCTION my_func();
Or:
CREATE TRIGGER my_t BEFORE UPDATE OF first_name, last_name OR TRUNCATE ON person
FOR /* EACH */ STATEMENT EXECUTE FUNCTION my_func();
*Memo:
Only
UPDATE
can specify one or more columns withOF
whileINSERT
,DELETE
andTRUNCATE
cannot.You can omit
EACH
which is optional so with and withoutEACH
is the same.
Then, you update first_name
to Tom
on all 2 rows on person
table, then num
is 1
as shown below:
postgres=# UPDATE person SET first_name = 'Tom';
UPDATE 2
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
1 | Tom | Smith | 27
2 | Tom | Miller | 32
(2 rows)
postgres=# SELECT num FROM log;
num
-----
1
(1 row)
Then, you update age
to 50
on all 2 rows on person
table, then num
is still 1
as shown below:
postgres=# UPDATE person SET age = 50;
UPDATE 2
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
1 | Tom | Smith | 50
2 | Tom | Miller | 50
(2 rows)
postgres=# SELECT num FROM log;
num
-----
1
(1 row)
Then, you truncate person
table to delete all 2 rows from person
table, then num
is 2
as shown below:
postgres=# TRUNCATE TABLE person;
TRUNCATE TABLE
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
(0 rows)
postgres=# SELECT num FROM log;
num
-----
2
(1 row)
Next, you create my_v
view as shown below. *My post explains a view:
CREATE VIEW my_v AS
SELECT * FROM person;
Now, you can create my_t
trigger which runs my_func()
when UPDATE
or DELETE
operation is prevented on my_view
for each row as shown below:
CREATE TRIGGER my_t INSTEAD OF UPDATE OR DELETE ON my_v
FOR EACH ROW EXECUTE FUNCTION my_func();
*Memos:
INSTEAD OF
can prevent operations.You can use
INSTEAD OF
only with a view andFOR EACH ROW
otherwise there is error.You cannot specify one or more columns with
UPDATE OF
when usingINSTEAD OF
otherwise there is error.You cannot use
TRUNCATE
withINSTEAD OF
otherwise there is error.The trigger with
BEFORE
orAFTER
andINSERT
,UPDATE
orDELETE
on a view doesn't work. *My question and the answers explains it.
Then, you insert a row to person
table with my_v
, then num
is still 0
as shown below:
postgres=# INSERT INTO my_v (id, first_name, last_name, age) VALUES (3, 'Robert', 'Wilson', 18);
INSERT 0 1
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
1 | John | Smith | 27
2 | David | Miller | 32
3 | Robert | Wilson | 18
(3 rows)
postgres=# SELECT num FROM log;
num
-----
0
(1 row)
Then, you try to update first_name
to Tom
on all 3 rows on person
table with my_v
, then the update is prevented, then num
is 3
as shown below:
postgres=# UPDATE my_v SET first_name = 'Tom';
UPDATE 0
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
1 | John | Smith | 27
2 | David | Miller | 32
3 | Robert | Wilson | 18
(3 rows)
postgres=# SELECT num FROM log;
num
-----
3
(1 row)
Then, you try to delete 2 rows from person
table where id
is 2
and 3
with my_v
, then the deletion is prevented, then num
is 5
as shown below:
postgres=# DELETE FROM my_v WHERE id IN (2, 3);
DELETE 0
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
1 | John | Smith | 27
2 | David | Miller | 32
3 | Robert | Wilson | 18
(3 rows)
postgres=# SELECT num FROM log;
num
-----
5
(1 row)
Next, you can use NEW
and OLD
records in func()
trigger function to return the sum of age
of NEW
and OLD
on person
table as shown below:
CREATE FUNCTION my_func() RETURNS trigger
AS $$
BEGIN
UPDATE log SET num = num + 1;
NEW.age := NEW.age + OLD.age;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
*Memo:
-
NEW
record is the new row which you are about to input. -
OLD
record is the old row which you have already input. - Basically, you should return
NEW
orOLD
instead ofNULL
because there is the caseINSERT
,UPDATE
,DELETE
orTRUNCATE
doesn't occur becauseNEW
orOLD
is not returned. *I explains the case later. - You can use
=
instead of:=
. - Basically and initially for
INSERT
,NEW
has a record whileOLD
hasNULL
. - Basically and initially for
UPDATE
,NEW
andOLD
have a record. - Basically and initially for
DELETE
,NEW
hasNULL
whileOLD
has a record. - Always and initially for
TRUNCATE
,NEW
andOLD
haveNULL
.
Now, you can create my_t
trigger which runs my_func()
before UPDATE
operation happens on person
table for each row as shown below:
CREATE TRIGGER my_t BEFORE UPDATE ON person
FOR EACH ROW EXECUTE FUNCTION my_func();
*Memos:
In only case you set
BEFORE
andFOR EACH ROW
to a trigger, you can change the actual record ofINSERT
orUPDATE
by returningNEW
orOLD
which has a record. *Initially,NULL
or a record is passed toNEW
orOLD
.If
my_func()
returnsNULL
or returnsNEW
orOLD
which hasNULL
and you setBEFORE
withFOR EACH ROW
to a trigger, the operationINSERT
,UPDATE
orDELETE
which you set to a trigger doesn't occur, thennum
is incremented by 1. *Initially,NULL
or a record is passed toNEW
orOLD
.Whether or not
my_func()
returnsNULL
or returnsNEW
orOLD
which has a record and if you setBEFORE
withFOR EACH STATEMENT
to a trigger, the operationINSERT
,UPDATE
,DELETE
orTRUNCATE
works with the actual record which you input without any modification, thennum
is incremented by 1. *Initially,NULL
or a record is passed toNEW
orOLD
.Whether or not
my_func()
returnsNULL
or returnsNEW
orOLD
which has a record and if you setAFTER
withFOR EACH ROW
to a trigger, the operationINSERT
,UPDATE
orDELETE
works with the actual record which you input without any modification, thennum
is incremented by 1. *Initially,NULL
or a record is passed toNEW
orOLD
.Whether or not
my_func()
returnsNULL
or returnsNEW
orOLD
which has a record and if you setAFTER
withFOR EACH STATEMENT
to a trigger, the operationINSERT
,UPDATE
,DELETE
orTRUNCATE
works with the actual record which you input without any modification, thennum
is incremented by 1. *Initially, onlyNULL
is passed toNEW
andOLD
.Whether or not
my_func()
returnsNULL
or returnsNEW
orOLD
which has a record and if you setINSTEAD OF
withFOR EACH ROW
to a trigger, the operationINSERT
,UPDATE
orDELETE
works with the actual record which you input without any modification, thennum
is incremented by 1. *Initially,NULL
or a record is passed toNEW
orOLD
.
Then, you update age
to 68
where id
is 2
on person
table, then age
where id
is 2
is 100
and num
is 1
as shown below:
postgres=# UPDATE person SET age = 68 WHERE id = 2;
UPDATE 1
postgres=# SELECT * FROM person;
id | first_name | last_name | age
----+------------+-----------+-----
1 | John | Smith | 27
2 | David | Miller | 100
(2 rows)
postgres=# SELECT num FROM log;
num
-----
1
(1 row)
In addition, you can check if NEW
or OLD
has a record or NULL
with RAISE statement as shown below:
CREATE FUNCTION my_func() RETURNS trigger
AS $$
BEGIN
RAISE INFO 'NEW:%, OLD:%', NEW, OLD;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Top comments (0)