I want only to maintain present 1 month records log details. need to delete past record log details.I tried this code however could not work this,
create sequence userseq1;
CREATE TABLE users
( id integer NOT NULL DEFAULT nextval('userseq1'::regclass)
);
INSERT INTO users VALUES(126);
CREATE TABLE History
( userid integer
, createdate timestamp
);
CREATE OR REPLACE FUNCTION recordcreatetime() RETURNS trigger language plpgsql
AS $$
DECLARE
BEGIN
INSERT INTO History values(new.id,NOW() );
RETURN NEW;
END;
$$;
CREATE TRIGGER user_hist
BEFORE INSERT ON users
FOR EACH ROW
EXECUTE procedure recordcreatetime();
However it is working to insert values sequencing one by one adding.I want to delete the previous 1 month record Log details.I tried this below code and it is not working
CREATE OR REPLACE FUNCTION trf_keep_row_number_steady()
RETURNS TRIGGER AS
$body$
DECLARE
BEGIN
IF (SELECT count(createdate) FROM history) > rownum_limit
THEN
DELETE FROM history
WHERE createdate = (SELECT min(createdate) FROM history);
END IF;
END;
$body$
LANGUAGE plpgsql;
CREATE TRIGGER tr_keep_row_number_steady
AFTER INSERT ON history
FOR EACH ROW EXECUTE PROCEDURE trf_keep_row_number_steady();
I can see in your second code block, you have a trigger on history table and you are trying to DELETE FROM history in that same trigger.
Insert / Update / Delete on a table through a trigger on the same table is not allowed. Please think of some other alternative, e.g., running a separate DELETE statement for the required cleanup of rows before or after your main INSERT statement.
Related
I'm trying to set an "after insert" trigger that executes a procedure. The procedure would take all inserted rows in table A, group them by a column and insert the result in a table B. I know about "new" variable but it gets inserted rows one by one. Is it possible to get all of them?
I think I can't use a for each row statement as I need to group rows depending on the "trackCode" variable, shared by different rows in tableA.
CREATE OR REPLACE PROCEDURE Public.my_procedure(**inserted rows in tableA?**)
LANGUAGE 'plpgsql'
AS $$
BEGIN
INSERT INTO Public."tableB" ("TrackCode", "count")
SELECT "TrackCode", count(*) as "count" FROM Public."tableA" --new inserted rows in this table
GROUP BY "vmsint"."TrackCode" ;
COMMIT;
END;
$$;
create trigger Public.my_trigger
after insert ON Public.tableA
execute procedure Public.my_procedure(**inserted rows in tableA?**)
Thank you!
You create a statement lever trigger, but do not attempt to pass parameters. Instead use the clause referencing new table as reference_table_name. In the trigger function you use the reference_table_name in place of the actual table name. Something like: (see demo)
create or replace function group_a_ais()
returns trigger
language 'plpgsql'
as $$
begin
insert into table_b(track_code, items)
select track_code, count(*)
from rows_inserted_to_a
group by track_code ;
return null;
end;
$$;
create trigger table_a_ais
after insert on table_a
referencing new table as rows_inserted_to_a
for each statement
execute function group_a_ais();
Do not attempt to commit in a trigger, it is a very bad id even if allowed. Suppose the insert to the main table is part of a larger transaction, which fails later in its process.
Be sure to refer to links provided by Adrian.
I have subscription data that is being appended to a table in real-time (via kafka). i have set up a trigger such that once the data is added it is checked for consistency. If checks pass some of the data should be added to other tables (that have master information on the customer profile etc.). The checks function i wrote works fine but i keep getting errors on the function used in the trigger. The function for the trigger is:
CREATE OR REPLACE FUNCTION update_tables()
RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
CASE (SELECT check_incoming_data()) WHEN 0
THEN INSERT INTO
sub_master(sub_id, sub_date, customer_id, product_id)
VALUES(
(SELECT sub_id::int FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime)),
(SELECT sub_date::date FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime)),
(SELECT customer_id::int FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime)),
(SELECT product_id::int FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime))
);
RETURN sub_master;
END CASE;
RETURN sub_master;
END;
$$
The trigger is then:
CREATE TRIGGER incoming_data
AFTER INSERT
ON claims_realtime_3
FOR EACH ROW
EXECUTE PROCEDURE update_tables();
What I am saying is 'if checks pass then select data from the last added row and add them to the master table'. What is the best way to structure this query?
Thanks a lot!
The trigger functions are executed for each row and you must use a record type variable called "NEW" which is automatically created by the database in the trigger functions. "NEW" gets only inserted records. For example, I want to insert data to users_log table when inserting records to users table.
CREATE OR REPLACE FUNCTION users_insert()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
insert into users_log
(
username,
first_name,
last_name
)
select
new.username,
new.first_name,
new.last_name;
return new;
END;
$function$;
create trigger store_data_to_history_insert
before insert
on users for each row execute function users_insert();
I have the requirement to move data from one table to another table when the value of one of the columns is updated. And I just want to move the updated row to the new table.
Below is my trigger that I have written. The issue with my code is, that it is moving all the data and not just the row which was updated. Can anyone give a suggestion?
create or replace function moveToAC1ControlHist()
returns trigger as $$
begin if NEW.file_status='CO'
then
insert into ac1_control_hist (file_status,identitifier)
(
select file_status,identitifier
from
ac1_control where new.FILE_STATUS = 'CO'
);
end if;
return new;
end;
$$ language plpgsql;
create TRIGGER AC1_CONTROL_TRIGGER AFTER update of file_status ON AC1_CONTROL
FOR EACH ROW when (new.file_status ='CO')EXECUTE PROCEDURE moveToAC1ControlHist();
I think the logic you want is:
create or replace function moveToAC1ControlHist()
returns trigger as
$$
begin
insert into ac1_control_hist (file_status,identitifier)
values (new.file_status, new.identitifier);
return null;
end;
$$ language plpgsql;
create trigger ac1_control_trigger
after update of file_status on ac1_control
for each row
when (new.file_status ='co')
execute function movetoac1controlhist()
;
Rationale:
you just want to copy (part of) the row being updated, so there is no need to select; you can access the values of the current row with new in a row-level trigger
the trigger definition filters on new file_status that is equal to 'CO', so there is no need for a if construct in the function
this is an after trigger, so you can just return null - the result is discarded anyway
When a new row is inserted into table forumtopics (cols: id | userid), than I want to do a trigger that makes an insert into table upvotes, that uses the id and userid from the forumtopics row.
So the upvotes table would look: id | userid | forumtopicsid (id from original insert)
How may I do this?
First, create a trigger function, e.g. (assuming that upvotes.id is of type serial):
create or replace function before_insert_on_forumtopics()
returns trigger language plpgsql as $$
begin
insert into upvotes (userid, forumtopicsid)
values (new.userid, new.id);
return new;
end $$;
Next, create a trigger:
create trigger before_insert_on_forumtopics
before insert on forumtopics
for each row
execute procedure before_insert_on_forumtopics();
Read in the documentation about Trigger Behavior, Trigger Procedures and CREATE TRIGGER.
CREATE TRIGGER init_upvote
AFTER INSERT ON forumtopics
FOR EACH ROW
EXECUTE PROCEDURE function_that_inserts_into_upvote(NEW.id, NEW.userid);
I am trying to create a trigger, so that when ever I add a new record it adds another record in the same table. The session field will only take values between 1 and 4. So when I add a 1 in session I want it to add another record but with session 3 blocked. But the problem is that it leads to cascading triggers and it inserts itself again and again because the trigger is triggered when inserted.
I have for example a simple table:
CREATE TABLE example
(
id SERIAL PRIMARY KEY
,name VARCHAR(100) NOT NULL
,session INTEGER
,status VARCHAR(100)
);
My trigger function is:
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO example VALUES (NEW.id + 1, NEW.name, NEW.session+2, 'blocked');
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';
Trigger is:
CREATE TRIGGER add_block
AFTER INSERT OR UPDATE
ON example
FOR EACH ROW
EXECUTE PROCEDURE add_block();
I get error:
SQL statement "INSERT INTO example VALUES ( $1 +1, $2 , $3 + 2, $4)"
PL/pgSQL function "add_block" line 37 at SQL statement
This error repeats itself so many times that I can't see the top.
How would I solve this?
EDIT:
CREATE TABLE block_rules
(
id SERIAL PRIMARY KEY
,session INTEGER
,block_session INTEGER
);
This table holds the block rules. So if a new record is inserted into the EXAMPLE table with session 1 then it blocks session 3 accordingly by inserting a new record with blocked status in the same (EXAMPLE) table above (not block_rules). Same for session 2 but it blocks session 4.
The block_rules table holds the rules (or pattern) to block a session by. It holds
id | session | block_session
------------------------------
1 | 1 | 3
2 | 2 | 4
3 | 3 | 2
How would I put that in the WHEN statement of the trigger going with Erwin Branstetter's answer below?
Thanks
New answer to edited question
This trigger function adds blocked sessions according to the information in table block_rules.
I assume that the tables are linked by id - information is missing in the question.
I now assume that the block rules are general rules for all sessions alike and link by session. The trigger is only called for non-blocked sessions and inserts a matching blocked session.
Trigger function:
CREATE OR REPLACE FUNCTION add_block()
RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO example (name, session, status)
VALUES (NEW.name
,(SELECT block_session
FROM block_rules
WHERE session = NEW.session)
,'blocked');
RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;
Trigger:
CREATE TRIGGER add_block
AFTER INSERT -- OR UPDATE
ON example
FOR EACH ROW
WHEN (NEW.status IS DISTINCT FROM 'blocked')
EXECUTE PROCEDURE add_block();
Answer to original question
There is still room for improvement. Consider this setup:
CREATE OR REPLACE FUNCTION add_block()
RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO example (name, session, status)
VALUES (NEW.name, NEW.session + 2, 'blocked');
RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;
CREATE TRIGGER add_block
AFTER INSERT -- OR UPDATE
ON example
FOR EACH ROW
WHEN (NEW.session < 3)
-- WHEN (status IS DISTINCT FROM 'blocked') -- alternative guess at filter
EXECUTE PROCEDURE add_block();
Major points:
For PostgreSQL 9.0 or later you can use a WHEN condition in the trigger definition. This would be most efficient. For older versions you use the same condition inside the trigger function.
There is no need to add a column, if you can define criteria to discern auto-inserted rows. You did not tell, so I assume that only auto-inserted rows have session > 2 in my example. I added an alternative WHEN condition for status = 'blocked' as comment.
You should always provide a column list for INSERTs. If you don't, later changes to the table may have unexpected side effects!
Do not insert NEW.id + 1 in the trigger manually. This won't increment the sequence and the next INSERT will fail with a duplicate key violation.
id is a serial column, so don't do anything. The default nextval() from the sequence is inserted automatically.
Your description only mentions INSERT, yet you have a trigger AFTER INSERT OR UPDATE. I cut out the UPDATE part.
The keyword plpgsql doesn't have to be quoted.
OK so can't you just add another column, something like this:
ALTER TABLE example ADD COLUMN trig INTEGER DEFAULT 0;
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
IF NEW.trig = 0 THEN
INSERT INTO example VALUES (NEXTVAL('example_id_seq'::regclass), NEW.name, NEW.session+2, 'blocked', 1);
END IF;
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';
it's not great, but it works :-)
CREATE OR REPLACE FUNCTION add_block() RETURNS TRIGGER AS $$
BEGIN
SET SESSION session_replication_role = replica;
INSERT INTO example VALUES (NEXTVAL('example_id_seq'::regclass), NEW.name, NEW.session+2, 'blocked');
SET SESSION session_replication_role = origin;
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';