Using rules in postgresql to insert several rows - postgresql

I need to insert several rows using a rule.
My problem is:
I draw a poligon and save on a table z0_poligono_corte, then I use a rule to get the intersection between the polygon in z0_poligono_corte and polygons in other table, but the result is not inserted in the table I created to save the results. Also there are no errors in the postgres log.
CREATE RULE cortar_anp AS ON INSERT TO z0_poligono_corte
DO also insert into z0_a_n_p_13 (id_poligono, folio, obs, anp, geometria) select pol.id, pol.folio, pol.obs, capa.anp, ST_Multi(ST_Intersection(capa.geom, pol.geometria))
from a_n_p_13 capa, z0_poligono_corte pol
where pol.id=NEW.id
and capa.geom && pol.geometria
and ST_IsEmpty(ST_Intersection(capa.geom, pol.geometria)) != TRUE;
I don't know if my rule is correct. If I run the insert into sentence using for example pol.id=1 then the result saves correctly.
image
I have not 10 reputation to put the image :(
Here the image
Thanks.

Here is how solved my problem with a trigger.
First i created a table
CREATE TABLE public.z0_poligono_corte (
id serial,
nombre character varying(255),
giro text,
geometria geometry(MultiPolygon,32614),
CONSTRAINT z0_poligono_corte_pkey PRIMARY KEY (id) )
After that I created a trigger function that makes the slice
CREATE OR REPLACE FUNCTION public.cortes_interseccion_amias()
RETURNS trigger AS
$BODY$
declare var_sql text;
begin
var_sql := 'insert into z0_amias08 (id_poligono, nombre, giro, tipo, geometria)
select pol.id, pol.nombre, pol.giro, capa.tipo, ST_Intersection(capa.geom, pol.geometria)
from amias08 capa, z0_poligono_corte pol
where pol.id=$1
and capa.geom && pol.geometria
and ST_IsEmpty(ST_Intersection(capa.geom, pol.geometria)) != TRUE';
execute var_sql using NEW.id;
return NEW;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Then the trigger.
CREATE TRIGGER interseccion_amias
AFTER INSERT
ON public.z0_poligono_corte
FOR EACH ROW
EXECUTE PROCEDURE public.cortes_interseccion_amias();
So when I insert a polygon on that table, the trigger function will cut on the table with data, then insert on the new table

Related

How to DELETE/INSERT rows in the same table using a UPDATE Trigger?

I want to create a trigger function, which copies certain columns of an recent updated row and deletes the old data. After that I want to insert the copied columns in exact the same table in the same row (overwrite). I need the data to be INSERTED because this function will be embedded in an existing program, with predefined Triggers.
That's what I have so far:
CREATE OR REPLACE FUNCTION update_table()
RETURNS TRIGGER AS
$func$
BEGIN
WITH tmp AS (DELETE FROM table
WHERE table.id = NEW.id
RETURNING id, geom )
INSERT INTO table (id, geom) SELECT * FROM tmp;
END;
$func$ language plpgsql;
CREATE TRIGGER T_update
AFTER UPDATE OF geom ON table
EXECUTE PROCEDURE update_table();
But I get the Error message:
ERROR: cannot perform DELETE RETURNING on relation "table"
HINT: You need an unconditional ON DELETE DO INSTEAD rule with a RETURNING clause.
Why I should use a rule here?
I'm using PostgreSQL 9.6
UPDATE:
A little bit of clarification. When I have two columns in my table (id, geom), after I updated geom I want to make a copy of this (new)row and insert it into the same table, while overwriting the updated row. (I'm not interested in any value before the update) I know that this is odd but I need this row to be inserted again because the program i embed this function in, listens to a INSERT statement and cannot be changed by me.
Right after you update a row, its old values will no longer be available. So, if you simply want to preserve the old row in case of an update you need to create a BEFORE UPDATE trigger, so that you can still access the OLD values and create a new row, e.g.
CREATE TABLE t (id int, geom geometry(point,4326));
CREATE OR REPLACE FUNCTION update_table() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO t (id, geom) VALUES (OLD.id,OLD.geom);
RETURN NEW;
END; $$ LANGUAGE plpgsql;
CREATE TRIGGER t_update
BEFORE UPDATE OF geom ON t FOR EACH ROW EXECUTE PROCEDURE update_table();
INSERT INTO t VALUES (1,'SRID=4326;POINT(1 1)');
If you update the record 1 ..
UPDATE t SET geom = 'SRID=4326;POINT(2 2)', id = 2 WHERE id = 1;
UPDATE t SET geom = 'SRID=4326;POINT(3 3)', id = 3 WHERE id = 2;
.. you get a new record in the same table as you wished
SELECT id, ST_AsText(geom) FROM t;
id | st_astext
----+------------
1 | POINT(1 1)
2 | POINT(2 2)
3 | POINT(3 3)
Demo: db<>fiddle
Unrelated note: consider upgrading your PostgreSQL version! 9.6 will reach EOL in November, 2021.
First thanks to #JimJones for the answer. I´d like to post his answer modified for this purpose. This code "overwrites" the updated row by inserting a copy of itself and then deleting the old duplicate. That way I can Trigger on INSERT.
CREATE TABLE t (Unique_id SERIAL,id int, geom geometry(point,4326));
CREATE OR REPLACE FUNCTION update_table() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO t (id, geom) VALUES (NEW.id,NEW.geom);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER t_update
BEFORE UPDATE OF geom ON t FOR EACH ROW EXECUTE PROCEDURE update_table();
CREATE OR REPLACE FUNCTION delete_table() RETURNS TRIGGER AS $$
BEGIN
DELETE FROM t a
USING t b
WHERE a.Unique_id < b.Unique_id
AND a.geom = b.geom;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER t_delete
AFTER UPDATE OF geom ON t FOR EACH ROW EXECUTE PROCEDURE delete_table();
INSERT INTO t VALUES (1,1,'SRID=4326;POINT(1 1)');
UPDATE t SET geom = 'SRID=4326;POINT(2 2)' WHERE id = 1;

Fill field based on column other table

I have a really simple problem and I am probably overthinking this way too much. But here it goes:
I want the fields of a column in one of my tables to be filled automatically whenever I make a new record. The value should be the same (UUID) as the specified (UUID) value from a column in another table. These two columns are joined via a foreign key. So far I have tried making a trigger function but with no results so far:
Create or replace function project_id()
returns trigger
as $$ begin
if new.project_id is null then
insert into sporen (project_id)
select project_id
from project_info
where project_code = 'ant0001';
end if;
return new;
end;
$$ language plpgsql;
CREATE TRIGGER
project_id_default
BEFORE update ON
sporen
FOR EACH ROW EXECUTE PROCEDURE project_id();
Do I need to specify something as a default in my table? Or am I going about it completely wrong?
You only need to assign project_info.project_id to NEW.project_id in your trigger function. No INSERT is needed. Here is an illustration.
Create or replace function project_id() returns trigger as
$$
begin
if new.project_id is null then
new.project_id :=
(
select pi.project_id
from project_info pi
where pi.project_code = NEW.project_code
);
end if;
return new;
end;
$$ language plpgsql;
You do not need to specify a default value for project_id in your table.

postgres trigger creates index: BEFORE INSERT ON hides one row

I have a trigger AFTER INSERT ON mytable that calls a function
CREATE OR REPLACE FUNCTION myfunction() RETURNS trigger AS
$BODY$
DECLARE
index TEXT;
BEGIN
index := 'myIndex_' || NEW.id2::text;
IF to_regclass(index::cstring) IS NULL THEN
EXECUTE 'CREATE INDEX ' || index || ' ON mytable(id) WITH (FILLFACTOR=100) WHERE id2=' || NEW.id2|| ';';
RAISE NOTICE 'Created new index %',index;
END IF;
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
SECURITY DEFINER
COST 100;
ALTER FUNCTION myfunction()
OWNER TO theadmin;
This works wonderfully. For each distinct id2 I create an index. Speeds up relevant queries by a lot.
As mentioned above I trigger this AFTER INSERT ON. Before doing that however I had the trigger set to BEFORE INSERT ON. And the function did some strange things. (Yes, I had changed the RETURN NULL to RETURN NEW)
insert of a new row insert into mytable VALUES(1391, 868, 0.5, 0.5);
creates the corresponding index myIndex_868
the inserted row does not appear in mytable when doing a select :(
trying to insert the same row results in ERROR: duplicate key value violates unique constraint "mytable_pkey" because of course DETAIL: Key (id, id2)=(1391, 868) already exists.
inserting other rows for the same id2 works as expected :)
DELETE FROM mytable WHERE id = 1391 and id2 = 868 does nothing
DROP INDEX myIndex_868; drops the index. And suddenly the initial row that never appeared in the table is suddenly there!
Why does BEFORE INSERT ON behave so differently? Is this a bug in postgres 9.4 or did I overlook something?
Just for completeness' sake:
CREATE TRIGGER mytrigger
AFTER INSERT ON mytable
FOR EACH ROW EXECUTE PROCEDURE myfunction();
vs.
CREATE TRIGGER mytrigger
BEFORE INSERT ON mytable
FOR EACH ROW EXECUTE PROCEDURE myfunction();
I'd argue that this is a bug in PostgreSQL. I could reproduce it with 9.6.
It is clear that the row is not contained in the index as it is created in the BEFORE trigger, but the fact that the index is not updated when the row is inserted is a bug in my opinion.
I have written to pgsql-hackers to ask for an opinion.
But apart from that, I don't see the point of the whole exercise.
Better than creating a gazillion indexes would be to create a single one:
CREATE INDEX ON mytable(id2, id);

How to create trigger to insert a sequence of numbers in postgresql; but the insert statement validated before ..?

Good morning everyone, I have a question about the following case:
I have a trigger and a function that inserts a land code, but when it works very well when inserting a row.
But when an insert statement fails to execute for any problems in the expression, the sequence function generates a value before inserting the row, losing the order in the numeration.
There is a way to make a change in the trigger or function, to validate me before the INSERT expression before moving to the sequence function and thereby avoid those jumps of numeration.
Deputy code (triger and function) and images of the tables.
CODE:
CREATE TRIGGER trigger_codigo_pech
BEFORE INSERT ON independizacion
FOR EACH ROW
EXECUTE PROCEDURE codigo_pech();
CREATE OR REPLACE FUNCTION codigo_pech()
RETURNS trigger
AS $$
DECLARE
incremento INTEGER;
cod_inde text;
BEGIN
IF (NEW.cod_inde IS NULL OR NEW.cod_inde = '''' ) THEN
incremento = nextval ('codigo_pech');
NEW.cod_inde = 'PECH' || '-' || incremento;
END IF;
RETURN NEW;
END;
$$ LANGUAGE 'plpgsql';
CAPTURE QUERY RESULT
As you can see, it would also be necessary to make a trigger on the primary key to prevent jumps in the numeration.
I hope your help. Thank you
You can make incremento.cod_inde DEFERRABLE and INITIALLY DEFERRED:
ALTER TABLE incremento ALTER COLUMN cod_inde SET DEFAULT 0;
ALTER TABLE incremento
ALTER CONSTRAINT incremento_cod_inde_key
DEFERRABLE INITIALLY DEFERRED;
Then assign the nextval('codigo_pech') in a AFTER INSERT trigger:
CREATE OR REPLACE FUNCTION codigo_pech_after() RETURNS trigger AS $$
BEGIN
UPDATE incremento SET
cod_inde = 'PECH-' || (nextval('codigo_pech'))::text
WHERE id = NEW.id; -- replace id with your table's primary key
END;
$$ LANGUAGE plpgsql;

Trigger insert into another table only if unique value

I have a trigger function that copy row of unique values to another table on update or insert that ALMOST work.
The trigger should only insert a new row to the sample table if the number don't exist in it before. Atm. it insert a new row to the sample table with the value NULL if the number already exist in the table. I dont want it to do anything if maintbl.number = sample.nb_main
EDIT: sample table and sample data
CREATE TABLE schema.main(
sid SERIAL NOT NULL,
number INTEGER,
CONSTRAINT sid_pk PRIMARY KEY (sid)
)
CREATE TABLE schema.sample(
gid SERIAL NOT NULL,
nb_main INTEGER,
CONSTRAINT gid_pk PRIMARY KEY (gid)
Example and desired result
schema.main schema.sample
number nb_main
234233 234233
234234 555555
234234
555555
555555
CREATE OR REPLACE FUNCTION schema.update_number()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO schema.sample(
nb_main)
SELECT DISTINCT(maintbl.number)
FROM schema.maintbl
WHERE NOT EXISTS (
SELECT nb_main FROM schema.sample WHERE maintbl.number = sample.nb_main);
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION schema.update_number()
OWNER TO postgres;
CREATE TRIGGER update_number
AFTER INSERT OR UPDATE
ON schema.maintbl
FOR EACH ROW
EXECUTE PROCEDURE schema.update_number();
I just found out that my select query is probably wrong, if I run SELECT query by itself it return one row 'NULL' but i should not?
SELECT DISTINCT(maintbl.number)
FROM schema.maintbl
WHERE NOT EXISTS (
SELECT nb_main FROM schema.sample WHERE maintbl.number = sample.nb_main);
Any good advice?
Best
If I understood correctly, you wish to append to schema.sample a number that has been inserted or updated in schema.maintbl, right?
CREATE OR REPLACE FUNCTION schema.update_number()
RETURNS trigger AS
$BODY$
BEGIN
IF (SELECT COUNT(*) FROM schema.sample WHERE number = NEW.number) = 0 THEN
INSERT INTO schema.sample(nb_main) VALUES (NEW.number);
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;