How to use a newly inserted value from another table in PostgreSQL? - sql

Assuming I have two tables final table and table_1, I want to use the the newest values from table_1 and insert them with a trigger in the final_table with every INSERT ON table_1. When I create the triggerfunction inserttrigger() as shown in the example and create the trigger, I get the newest value times the number of rows in table_1. How to write the trigger proper that I get only the single newest record in table1?
Doing:
-- Create tables and inserting example values
CREATE TABLE final_table(id INTEGER, value_fin INTEGER);
CREATE TABLE table_1(id INTEGER, value INTEGER);
INSERT INTO table_1 VALUES(1, 200), (2,203), (3, 209);
-- Create Triggerfunction
CREATE OR REPLACE FUNCTION inserttrigger()
RETURNS TRIGGER AS
$func$
BEGIN
INSERT INTO final_table
SELECT latest.id, latest.value
FROM (SELECT NEW.id, NEW.value FROM table_1) AS latest;
RETURN NEW;
END;
$func$ language plpgsql;
-- Create Trigger
CREATE TRIGGER final_table_update BEFORE INSERT ON table_1
FOR EACH ROW EXECUTE PROCEDURE inserttrigger() ;
--Insert example values
INSERT INTO table_1 VALUES(4, 215);
Results in:
SELECT * FROM final_table
id | value_fin
4 215
4 215
4 215
But should look like:
id | value_fin
4 215
While:
CREATE TRIGGER final_table_update BEFORE INSERT ON table_1
EXECUTE PROCEDURE inserttrigger() ;
Results in:
ERROR: record "new" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.

I would recommend the VALUES() syntax:
CREATE OR REPLACE FUNCTION inserttrigger()
RETURNS TRIGGER AS
$func$
BEGIN
INSERT INTO final_table VALUES(NEW.id, NEW.value);
RETURN NEW;
END;
$func$ language plpgsql;
CREATE TRIGGER final_table_update BEFORE INSERT ON table_1
FOR EACH ROW EXECUTE PROCEDURE inserttrigger();
Note that you could also get the same behavior with a common-table-expression and the returning syntax, which avoids the need for a trigger:
with t1 as (
insert into table_1(id, value_fin) values(4, 215)
returning id, value_fin
)
insert into final_table(id, value) select id, value_fin from t1

Related

Trigger insert of data into table with result from another select query

Starting my first steps in SQL.
I'm trying to insert the last row of a timestamped table to another table.
I've written this trigger:
CREATE TRIGGER update_analysis()
AFTER INSERT ON data
FOR EACH ROW
EXECUTE PROCEDURE insert_to_analysis();
I've defined a function, which I know is wrong, but don't understand how to write it correctly. (the target table has these columns)
CREATE FUNCTION UPDATE_ANALYSIS() RETURNS TABLE
AS
$$ BEGIN
INSERT INTO ANALYSIS (TIME, CYCLE_NUMBER,CAT1,CAT2,CAT3)
SELECT (TIME, CYCLENO , I1 , I2 * 2 ,I3*3)
FROM DATA
ORDER BY TIME DESC
LIMIT 1;)
RETURN
END;
$$ LANGUAGE 'plpgsql';
Thanks in advance
If you are referencing the same data you just inserted into data, you could simply refer to what you inserted instead of SELECT:ing, like so:
CREATE FUNCTION UPDATE_ANALYSIS() RETURNS TABLE
AS
$$ BEGIN
INSERT INTO ANALYSIS (TIME, CYCLE_NUMBER,CAT1,CAT2,CAT3)
VALUES (NEW.TIME, NEW.CYCLENO , NEW.I1 , NEW.I2 * 2 ,NEW.I3 * 3);
RETURN NEW;
END;
$$ LANGUAGE 'plpgsql';
Since it's a function for a trigger, it should return a trigger.
And it's for each row, so an insert from values is possible.
Example
CREATE FUNCTION FN_INSERT_TO_ANALYSIS() RETURNS TRIGGER
AS $ins_analysis$
BEGIN
INSERT INTO ANALYSIS (TIME, CYCLE_NUMBER, CAT1, CAT2, CAT3)
VALUES (NEW.TIME, NEW.CYCLENO, NEW.I1, NEW.I2 * 2 , NEW.I3 * 3);
RETURN NEW;
END $ins_analysis$ LANGUAGE 'plpgsql';
CREATE TRIGGER trg_data_ins_analysis
AFTER INSERT ON data
FOR EACH ROW
EXECUTE PROCEDURE fn_insert_to_analysis();
insert into data values (current_timestamp,1,1,1,1)
select * from data
time
cycleno
i1
i2
i3
2021-12-27 14:53:17.822649
1
1
1
1
select * from ANALYSIS
time
cycle_number
cat1
cat2
cat3
2021-12-27 14:53:17.822649
1
1
2
3
Demo on db<>fiddle here

Replace insert with rule in PostgreSQL

I want to replace INSERT into a table with a PostgreSQL rule.
Here is my two tables:
Resource:
uid (PK): UUID
type (NOT_NULL): ENUM
SpecificResource:
uid (PK, FK on resource.uid): UUID
content (NOT_NULL): JSONB
I want any user to the database to be able to make insert/update/delete on SpecificResource directly without the need to insert/update/delete on Resource.
Here is my unsuccessful try that triggers an infinite recursion loop (indeed because I try to re-insert in specific_resource table with a RULE (...) DO INSTEAD :
CREATE OR REPLACE RULE insert_specific_resource
AS ON INSERT TO specific_resource
DO INSTEAD (
INSERT INTO resource (uid, type)
VALUES (NEW.uid, 'SPECIFIC_RESOURCE');
INSERT INTO specific_resource (uid)
VALUES (new.uid)
);
create or replace function specific_resource_tf()
returns trigger language plpgsql as
$$
begin
insert into resource(uid, type) VALUES (new.uid, 'SPECIFIC_RESOURCE');
return new;
end;
$$;
create trigger specific_resource_t
before insert on specific_resource
for each row
execute procedure specific_resource_tf();
Explanation
After creating trigger specific_resource_t it will be executed before each insert in table specific_resource. The trigger invokes function specific_resource_tf which does what your rule was intended to - inserts a record into resource before proceeding with the insert in table specific_resource.
Illustration (with temporary tables and function)
-- drop table if exists specific_resource; drop table if exists resource;
create temp table resource (uid integer primary key, type text);
create temp table specific_resource (uid integer references resource(uid), content JSONB);
create or replace function pg_temp.specific_resource_tf()
returns trigger language plpgsql as $$
begin
insert into resource(uid, type) VALUES (new.uid, 'SPECIFIC_RESOURCE');
return new;
end;
$$;
create trigger specific_resource_t
before insert on specific_resource
for each row execute procedure pg_temp.specific_resource_tf();
insert into specific_resource values (22, '"test"');
-- does insert in both tables
CREATE OR REPLACE FUNCTION add_specific_plus_resource()
RETURNS TRIGGER LANGUAGE PLPGSQL AS
$$
BEGIN
INSERT INTO resource(uid, type)
VALUES (uid, 'SPECIFIC');
RETURN NEW;
END;
$$;
CREATE OR REPLACE TRIGGER add_specific_resource
BEFORE INSERT ON specific_resource
FOR EACH ROW EXECUTE PROCEDURE add_specific_plus_resource();
The following INSERT works well to add a row into resource but not into specific_resource.
INSERT INTO specific_resource(uid, content)
VALUES ('123e4567-e89b-12d3-a456-426614174000', '"test"');

Use returned values from a query in the same procedure

I'm quite new to pgpsql. I have two tables - product and product_category - with a one to many relationship. I'm creating a procedure to insert a value into the category table if it doesn't exist and I would like to use the id for the category to insert a product.
I've tried using an alias for the first insert and then returning the id value so I can select it, but I get an error.
What's the right way for using returned in the same procedure values in pgpsql?
CREATE OR REPLACE PROCEDURE insert_product(name VARCHAR, category VARCHAR)
LANGUAGE plpgsql
as $$
BEGIN
WITH ins_category AS(
INSERT INTO product_category(name)
VALUES('laptops')
ON CONFLICT (name) DO NOTHING
RETURNING id
)
INSERT INTO product(name, category)
VALUES(name, ins_category.id); // HERE IS THE ERROR
COMMIT;
END;
$$
Use a SELECT instead of VALUES.
WITH ins_category
AS
(
...
)
INSERT INTO product (name,
category)
SELECT name,
ins_category.id
FROM ins_category;

Change number of Rows Affected by Update

I am trying to achieve here is to basically override 0 rows Updated, when UPDATE is issued in-case the actual PK/UK value doesn't exist in the table. This is what I have done:
Actual Table:
CREATE TABLE fdrgiit.vereine(
team numeric(10) primary key,
punkte int not null,
serie int not null
);
Dummy Table:
CREATE TABLE fdrgiit.dummyup
(
id numeric(1) PRIMARY KEY,
datetest timestamp
);
Inserted records in both the tables:
insert into vereine(team,punkte,serie) values(1, 50, 1);
insert into vereine(team,punkte,serie) values(2, 30, 1);
insert into vereine(team,punkte,serie) values(3, 25, 1);
insert into vereine(team,punkte,serie) values(4, 37, 2);
insert into dummyup values(1, now());
Created the following function and trigger:
create or replace function updateover()
returns trigger as
$BODY$
begin
if EXISTS (select 1 FROM vereine WHERE team = new.team ) then
RETURN NEW;
else
UPDATE fdrgiit.dummyup set datetest=now() where id=1;
RETURN NULL;
end if;
end;
$BODY$
LANGUAGE plpgsql;
create trigger update_redundancy
before update on vereine
for each row
execute procedure updateover() ;
But when I execute an UPDATE like this on the , I am still get 0 rows affected
update vereine set punkte=87 where team=5;
Kindly review and please suggest if this is something that can be done.
You cannot trigger anything with an UPDATE that does not affect row as triggers are only fired for affected rows.
But you could wrap your alternative UPDATE into a function:
CREATE OR REPLACE FUNCTION updateover()
RETURNS int AS
$func$
UPDATE dummyup
SET datetest = now()
WHERE id = 1
RETURNING 2;
$func$ LANGUAGE sql;
... and run your UPDATE nested like this:
WITH upd AS (
UPDATE vereine
SET punkte = 87
WHERE team = 5 -- does not exist!
RETURNING 1
)
SELECT 1 FROM upd
UNION ALL
SELECT updateover()
LIMIT 1;
db<>fiddle here
If no row qualifies for an UPDATE, then 1st outer SELECT 1 FROM upd returns no row and Postgres keeps processing the 2nd SELECT updateover(). But if at least one row is affected, the final SELECT is never executed. Exactly what you want.
This updates dummyup one time if the UPDATE on vereine does not affect any rows; never several times. But that's ok, since now() is STABLE for the duration of the transaction.
Related:
Return a value if no record is found

Postgresql insert trigger does not work

New row is added to table A and i need trigger which will automatically insert row in table B after row has been inserted in table A.
CREATE FUNCTION insertblocked (
)
RETURNS trigger AS
$body$
BEGIN
INSERT INTO tableB (blocked.id,blocked.number,blocked.date)
VALUES (new.id,new.prefix,now())
RETURN NEW;
END
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;
CREATE TRIGGER insertblocked
AFTER INSERT
ON public.tableA FOR EACH ROW
EXECUTE PROCEDURE insertblocked();
Please help and advise, why is sql compiler returning and error
QUERY: INSERT INTO blocked (blocked.id,blocked.number,blocked.date) VALUES ( $1 , $2 ,now()) RETURN $3
You're missing a semi-column ; at the end of your insert statement.