Suppose that I have table
create table foo (
insert_current timestamp default now(),
insert_previous timestamp,
bar int primary key,
baz varchar(10)
);
The data will be replaced now and then when fresh batch arrived. I would like to keep track on when previous batch was inserted as well as current timestamp. What would be a good way to do this?
I would create a trigger that automatically sets insert_current and insert_previous on UPDATE statements.
First, create the trigger function:
CREATE OR REPLACE FUNCTION do_update() RETURNS "trigger"
AS $$
BEGIN
NEW.insert_previous := OLD.insert_current;
NEW.insert_current := NOW();
return NEW;
END;
$$
LANGUAGE plpgsql;
Then add the trigger to your table:
CREATE TRIGGER do_update
BEFORE UPDATE ON foo
FOR EACH ROW
EXECUTE PROCEDURE do_update();
When you update a row, copy the value of insert_current to insert_previous and set insert_current to the current timestamp:
UPDATE foo
SET baz = 'whatever',
insert_previous = insert_current,
insert_current = NOW()
WHERE bar = 1;
Related
I am modifying data directly in pg admin 4, where I have to validate manually by marking a boolean value to true that is false by default.
I want that when I modify the value in that column, the updated_at column value should also be updated to current timestamp so I can query data with modified date.
How do I achieve this?
The post desired behavior is more simple than code demo in https://www.postgresql.org/docs/current/plpgsql-trigger.html.
setup:
CREATE temp TABLE test (
misc int,
is_valid boolean,
updated_at timestamptz
);
INSERT INTO test (misc, is_valid)
VALUES (1, FALSE);
CREATE FUNCTION update_test_update_at() RETURNS trigger AS $func$
BEGIN
IF NEW.* != OLD.* THEN NEW.updated_at := current_timestamp;
END IF;
RETURN NEW;
END;
$func$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER update_last_edit
BEFORE UPDATE OF is_valid ON test
FOR EACH ROW
EXECUTE FUNCTION update_test_update_at();
test:
UPDATE
test
SET
is_valid = true
RETURNING
*;
I used this example and it worked well when all my tables were in public schema.
But trigger hasn't been working since I separated tables into different schemas and applied inheriting.
Here is example of my structure:
CREATE SCHEMA common;
CREATE SCHEMA video;
CREATE TABLE common.file (
file_id SERIAL PRIMARY KEY,
url VARCHAR(255) NOT NULL,
mime_type VARCHAR(31) DEFAULT '' NOT NULL,
size INTEGER NOT NULL,
modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE TABLE video.file (
width INTEGER NOT NULL,
height INTEGER NOT NULL,
local_path VARCHAR(255) DEFAULT '' NOT NULL
)
INHERITS (common.file);
CREATE FUNCTION common.update_modified()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.modified = now();
RETURN NEW;
END;
CREATE TRIGGER update_modified
BEFORE UPDATE ON common.file
FOR EACH ROW EXECUTE PROCEDURE common.update_modified();
When I do UPDATE common.file ... or UPDATE video.file ... field common.file.modified doesn't change itself. It seems trigger doesn't run, but I don't understand why.
What should I do to repair the behavior?
In described issue trigger is set only on common.file, so UPDATE common.file ... doesn't work if row inserted in video.file
Documentation says: INSERT always inserts into exactly the table specified
So trigger should be applied to both common.file and video.file.
-- Creating schemas and tables the same
-- Let function be in public scheme
CREATE FUNCTION update_modified()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.modified = now();
RETURN NEW;
END;
CREATE TRIGGER update_modified
BEFORE UPDATE ON common.file
FOR EACH ROW EXECUTE PROCEDURE update_modified();
CREATE TRIGGER update_modified
BEFORE UPDATE ON video.file
FOR EACH ROW EXECUTE PROCEDURE update_modified();
In that case when we update rows inserted either in common.file or in video.file corresponding trigger will call.
I am having a problem with a trigger. I created a trigger and a function
for when performing an INSERT update a field in the same table.
Is returning:
Error: function "loss_func" in FROM has return type trigger that is
not supported LINE 1: SELECT * FROM table.loss_func ()
Function
CREATE OR REPLACE FUNCTION loss_func()
RETURNS trigger AS $loss_func$
BEGIN
NEW.dt_creation := to_char(now(), 'YYYY-MM-DD');
RETURN NULL;
END;
$loss_func$ LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION loss_func()
OWNER TO postgres;
Trigger
CREATE TRIGGER tgr_loss
AFTER INSERT ON loss
FOR EACH ROW
EXECUTE PROCEDURE loss_func();
What am I doing wrong?
A working version of your code.
- The trigger now fires BEFORE insert and updates the value of dt_creation and returns the NEW version of the record :
drop table loss;
create table loss (
id int ,
dt_created varchar);
CREATE OR REPLACE FUNCTION loss_func()
RETURNS trigger AS $loss_func$
BEGIN
NEW.dt_created := to_char(now(), 'YYYY-MM-DD');
RETURN NEW;
END;
$loss_func$ LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION loss_func()
OWNER TO postgres;
CREATE TRIGGER tgr_loss
BEFORE INSERT ON loss
FOR EACH ROW
EXECUTE PROCEDURE loss_func();
insert into loss(id) values(1);
Another solution that i can propose to avoid the usage of a trigger is to use a default value for dt_creation when you create the table (and use timestamp instead of storing the date as varchar) :
...
dt_creation timestamp default now(),
...
or you can alter your table to set the default value to now() :
alter table loss
alter column dt_creation set default now();
Here is my sample table.
CREATE TABLE employee_test(
idTst SERIAL PRIMARY KEY,
monthDownload VARCHAR(6),
changeDate DATE);
I am trying to create a function and trigger that would update changeDate attribute with a current date when monthDownload attribute is updated.
The function I have it works with one problem. It updates all records instead of the one that was updated.
CREATE OR REPLACE FUNCTION downloadMonthChange()
RETURNS TRIGGER AS
$$
BEGIN
IF NEW.monthDownload <> OLD.monthDownload THEN
UPDATE employee_test
SET changeDate = current_date
where OLD.idTst = NEW.idTst;
END IF;
RETURN NEW;
END;
$$
Language plpgsql;
Trigger
Create TRIGGER dataTest
AFTER UPDATE
ON employee_test
FOR EACH ROW
EXECUTE PROCEDURE downloadMonthChange();
When I execute the following Update statement:
UPDATE employee_test SET monthDownload = 'oct12'
WHERE idTst = 1;
All changeDate rows get update with a current date.
Is there a way to have only a row with changed record to have a current date updated.
If you use a before trigger you can write directly to NEW
CREATE OR REPLACE FUNCTION downloadMonthChange()
RETURNS TRIGGER AS
$$
BEGIN
IF NEW.monthDownload <> OLD.monthDownload THEN
NEW.changeDate = current_date;
END IF;
RETURN NEW;
END;
$$
Language plpgsql;
the other option when you must use an after trigger is to include the primary key in the where clause. It appears that you were trying to do this, but you had a spurious OLD in the query. beause of that the where clause was only looking at the record responsible for the trigger call, and not limiting which records were to be updated.
IF NEW.monthDownload <> OLD.monthDownload THEN
UPDATE employee_test
SET changeDate = current_date
where idTst = NEW.idTst;
I have a simple table:
CREATE TABLE IF NOT EXISTS someTable (
row_id smallserial PRIMARY KEY,
name text NOT NULL,
creation_date timestamp with time zone DEFAULT current_timestamp,
last_updated_date timestamp with time zone DEFAULT current_timestamp,
created_by text DEFAULT "current_user"(),
last_updated_by text DEFAULT "current_user"()
);
with the following rule:
CREATE OR REPLACE RULE log_update_some_table AS
ON UPDATE TO someTable
DO ALSO
UPDATE someTable
SET last_updated_date = current_timestamp,
last_updated_by = current_user;
and a very simple function in plpgsql:
CREATE OR REPLACE FUNCTION test_update ()
RETURNS void AS $$
BEGIN
UPDATE someTable
SET name = 'test'
WHERE row_id = 1;
END;
$$ LANGUAGE plpgsql;
One would think the function would run just fine, but I get the following error:
psycopg2.ProgrammingError: infinite recursion detected in rules for relation "sometable"
CONTEXT: SQL statement "UPDATE someTable
SET name = 'test'
WHERE row_id = 1"
PL/pgSQL function test_update() line 3 at SQL statement
Why isn't this working and how do I fix it? Thanks!
So your update rule on someTable triggers an update on someTable which executes the rule which updates someTable which executes the rule...
I'd use a simple trigger instead, something like this:
create or replace function log_update_some_table() returns trigger as $$
begin
NEW.last_updated_date = current_timestamp;
NEW.last_updated_by = current_user;
return NEW;
end;
$$ language plpgsql;
create trigger log_update_some_table_trigger
before update on someTable
for each row execute procedure log_update_some_table();
should do the trick. That will modify the row before the update happens rather than adding another update (which triggers the recursion problem) to the queue.