Postgres ignore insert error and carry on - postgresql

I have a PostgeresDB with the following constraint:
CONSTRAINT "Car_Data_3PM_pkey" PRIMARY KEY ("F_ID", "Date"),
CONSTRAINT "Car_Data_3PM_F_ID_fkey" FOREIGN KEY ("F_ID")
REFERENCES "Bike_Data" ("F_ID") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
When I try to insert multiple values using:
INSERT INTO "Car_Data_3PM" ("F_ID","Date","Price_Type","O","H","L","LT","EQ","V","NAD") VALUES (38,'2016-10-02 08:19:40.056679','x',0,0,0,112.145,0,0,112.145),(14,'2016-10-02 08:19:40.056679','x',0,0,0,5476,0,0,5476),(13,'2016-10-02
I get this error:
ERROR: insert or update on table "Car_Data_3PM" violates foreign key
constraint "Car_Data_3PM_F_ID_fkey" SQL state: 23503 Detail: Key
(F_ID)=(38) is not present in table "Bike_Data".
NO ROWS are inserted.
How can I make Postgres ONLY miss out the rows where the constraint is an issue? i.e Insert most of them?

You can't make Postgres ignore the values, but you can rewrite your statement to not insert those rows:
INSERT INTO "Car_Data_3PM" ("F_ID","Date","Price_Type","O","H","L","LT","EQ","V","NAD")
select *
from (
VALUES
(38,'2016-10-02 08:19:40.056679','x',0,0,0,112.145,0,0,112.145),
(14,'2016-10-02 08:19:40.056679','x',0,0,0,5476,0,0,5476),
... -- all other rows
) as x (id, date, price_type, o, h, l, lt, eq, v nad)
where exists (select 1
from "Bike_Data" bd
where bd."F_ID" = x .id)

One way is to write a trigger that filters out the bad values, like this:
CREATE FUNCTION car_insert_filter() RETURNS trigger
LANGUAGE plpgsql AS
$$BEGIN
IF EXISTS(SELECT 1 FROM "Bike_Data" WHERE "F_ID" = NEW."F_ID")
THEN
RETURN NEW;
ELSE
RAISE NOTICE 'Skipping row with "F_ID"=% and "Date"=%',
NEW."F_ID", NEW."Date";
RETURN NULL;
END IF;
END;$$;
CREATE TRIGGER car_insert_filter
BEFORE INSERT ON "Car_Data_3PM" FOR EACH ROW
EXECUTE PROCEDURE car_insert_filter();

Related

Add unique if constraints does not exist check in one line?

I am autogenerated SQL queries based on some condition of the autogenerated resulted into this query
DO
$$
BEGIN
IF NOT EXISTS (SELECT FROM pg_attribute
WHERE attrelid = 'public.registration'::regclass -- table name here
AND attname = 'price' -- column name here
AND NOT attisdropped
) THEN
ALTER TABLE public.registration
ADD COLUMN price text UNIQUE NULL;
ELSE
ALTER TABLE public.registration
ALTER COLUMN price TYPE text,
ADD CONSTRAINT IF NOT EXISTS registration_price_key UNIQUE (price);
END IF;
END
$$
So the table is is already there by if the column is not there it will be added and a unique constraint will be added to that column problem if it does not already exist
I get that an syntax error on this line `near NOT
ADD CONSTRAINT IF NOT EXISTS registration_price_key UNIQUE (price);
but why?

How to stop the "insert or update on table ...violates foreign key constraint"?

How to construct an INSERT statement so that it would not generate the error "insert or update on table ... violates foreign key constraint" in case if the foreign key value does not exist in the reference table?
I just need no record created in this case and success response.
Thank you
Use a query as the source for the INSERT statement:
insert into the_table (id, some_data, some_fk_column
select *
from (
values (42, 'foobar', 100)
) as x(id, some_data, some_fk_column)
where exists (select *
from referenced_table rt
where rt.primary_key_column = x.some_fk_column);
This can also be extended to a multi-row insert:
insert into the_table (id, some_data, some_fk_column
select *
from (
values
(42, 'foobar', 100),
(24, 'barfoo', 101)
) as x(id, some_data, some_fk_column)
where exists (select *
from referenced_table rt
where rt.primary_key_column = x.some_fk_column);
You didn't show us your table definitions so I had to make up the table and column names. You will have to translate that to your names.
You could create a function with plpgsql, which inserts a row and catches the exception:
CREATE FUNCTION customInsert(int,varchar) RETURNS VOID
AS $$
BEGIN
INSERT INTO foo VALUES ($1,$2);
EXCEPTION
WHEN foreign_key_violation THEN --do nothing
END;
$$ LANGUAGE plpgsql
You can then call this function by this:
SELECT customInsert(1,'hello');
This function tries to insert the given parameters into the table foo and catches the foreign_key_violation error if occurs.
Of course you can generalise the function more, to be able to insert in more than one table, but your question sounded like this was only needed for one specific table.

Postgres INSERT ON CONFLICT behavior with INSTEAD OF trigger

Suppose I have the following tables and view:
CREATE TABLE table_a(
field_x INTEGER PRIMARY KEY,
id SERIAL UNIQUE
);
CREATE TABLE table_b(
a_id INTEGER PRIMARY KEY REFERENCES table_a(id),
field_y INTEGER NOT NULL
);
CREATE VIEW v AS SELECT * FROM table_a JOIN table_b ON table_a.id=table_b.a_id;
I want to be able to insert into the view, so I created the following function and trigger:
CREATE FUNCTION insert_into_view()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $function$
DECLARE new_id INTEGER;
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO table_a (field_x) VALUES (NEW.field_x) ON CONFLICT DO NOTHING RETURNING id INTO new_id;
IF new_id IS NULL THEN
SELECT id FROM table_a WHERE field_x=NEW.field_x INTO new_id;
END IF;
INSERT INTO table_b (a_id, field_y) VALUES (new_id, NEW.field_y);
END IF;
RETURN NEW;
END;
$function$;
CREATE TRIGGER view_insert_trigger
INSTEAD OF INSERT ON
v FOR EACH ROW EXECUTE PROCEDURE insert_into_view();
Now I want to insert values into the view only if there does not exist a row for field_x yet in the view, e.g.:
INSERT INTO v (field_x, field_y) VALUES (5,6);
INSERT INTO v (field_x, field_y) VALUES (5,8) ON CONFLICT DO NOTHING;
Where I want the second insert to silently do nothing. However, I get this:
ERROR: duplicate key value violates unique constraint "table_b_pkey"
DETAIL: Key (a_id)=(2) already exists.
CONTEXT: SQL statement "INSERT INTO table_b (a_id, field_y) VALUES (new_id, NEW.field_y)"
I know why I'm getting this error: the function insert_into_view does not specify ON CONFLICT behavior when inserting into table_b and by default the query fails. Thus my question: can I make the ON CONFLICT behavior to ripple from the view insert into the table insert? (I may want to specify different conflict behavior at a later time, so I don't want to hard-code this in the trigger function if I can avoid it.)
Thanks!
I'm still not sure If I understand you right. But I try:
if you change
INSERT INTO table_b (a_id, field_y) VALUES (new_id, NEW.field_y)
to
INSERT INTO table_b (a_id, field_y) VALUES (new_id, NEW.field_y) ON CONFLICT DO NOTHING
in function it will start working silently.
Regarding
INSERT INTO v (field_x, field_y) VALUES (5,8) ON CONFLICT DO NOTHING;
I think you can use ON CONFLICT only on tables with unique constraint, so and foreign table, and instead rule will ignore ON CONFLICT DO NOTHING and fail when you specify target_name of constraint_name

After using a trigger - ERROR: null value in column "group_id" violates not-null constraint

I'm using PostgreSQL 8.1.23 on x86_64-redhat-linux-gnu
I have to write a database for reserving seats on language courses and there's a requirement there should be a trigger, which will check whether lector, we're trying to write into new group, has any other group at the same time. I have such table:
CREATE TABLE groups (
group_id serial PRIMARY KEY,
lang varchar(3) NOT NULL,
level varchar(3),
seats int4,
lector int4,
start time,
day varchar(3),
FOREIGN KEY (language) REFERENCES languages(lang) ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (lector) REFERENCES lectors(lector_id) ON UPDATE CASCADE ON DELETE SET NULL);
and such trigger:
CREATE FUNCTION if_available () RETURNS trigger AS '
DECLARE
r groups%rowtype;
c groups%rowtype;
BEGIN
FOR r IN SELECT * FROM groups WHERE r.lector=NEW.lector ORDER BY group_id LOOP
IF (r.start = NEW.start AND r.day = NEW.day) THEN
RAISE NOTICE ''Lector already has a group at this time!'';
c = NULL;
EXIT;
ELSE
c = NEW;
END IF;
END LOOP;
RETURN c;
END;
' LANGUAGE 'plpgsql';
CREATE TRIGGER if_available_t
BEFORE INSERT OR UPDATE ON grupy
FOR EACH ROW EXECUTE PROCEDURE if_available();
After inserting the new row to a table groups, eg.:
INSERT groups (lang, level, seats, lector, start, day) values ('ger','A-2',12,2,'11:45','wed');
I get an error like this:
ERROR: null value in column "group_id" violates not-null constraint
Without this trigger everything is OK. Could anybody help me how to make it work?
Finally, I have solved it! After BEGIN there should be c = NEW;, because when table groups is empty at the beginning, FOR loop doesn't run and NULL is returned. Also I have changed the condition in FOR loop for: ...WHERE lector = NEW.lector.... And finally, I have changed the condition in IF for IF (r.group_id <> NEW.group_id AND r.start = NEW.start AND r.day = NEW.day) THEN..., because I haven't wanted to run this trigger before one particular update. Maybe this will be helpful for someone :)

Need foreign key as array

CREATE TABLE test ( id int PRIMARY KEY , name );
CREATE TABLE test1 ( id integer[] REFERENCES test , rollid int );
ERROR: foreign key constraint "test3_id_fkey" cannot be implemented
DETAIL: Key columns "id" and "id" are of incompatible types: integer[] and integer.
after that I try to another way also
CREATE TABLE test1 ( id integer[] , rollid int);
ALTER TABLE test1 ADD CONSTRAINT foreignkeyarray FOREIGN KEY (id) REFERENCES test;
ERROR: foreign key constraint "fkarray" cannot be implemented
DETAIL: Key columns "id" and "id" are of incompatible types: integer[] and integer.
so I try create a foreign key array means it say error. please tell me anyone?
postgresql version is 9.1.
What you're trying to do simply can't be done. At all. No ifs, no buts.
Create a new table, test1_test, containing two fields, test1_id, test_id. Put the foreign keys as needed on that one, and make test1's id an integer.
Using arrays with foreign element keys is usually a sign of incorrect design. You need to do separate table with one to many relationship.
But technically it is possible. Example of checking array values without triggers. One reusable function with paramethers and dynamic sql. Tested on PostgreSQL 10.5
create schema if not exists test;
CREATE OR REPLACE FUNCTION test.check_foreign_key_array(data anyarray, ref_schema text, ref_table text, ref_column text)
RETURNS BOOL
RETURNS NULL ON NULL INPUT
LANGUAGE plpgsql
AS
$body$
DECLARE
fake_id text;
sql text default format($$
select id::text
from unnest($1) as x(id)
where id is not null
and id not in (select %3$I
from %1$I.%2$I
where %3$I = any($1))
limit 1;
$$, ref_schema, ref_table, ref_column);
BEGIN
EXECUTE sql USING data INTO fake_id;
IF (fake_id IS NOT NULL) THEN
RAISE NOTICE 'Array element value % does not exist in column %.%.%', fake_id, ref_schema, ref_table, ref_column;
RETURN false;
END IF;
RETURN true;
END
$body$;
drop table if exists test.t1, test.t2;
create table test.t1 (
id integer generated by default as identity primary key
);
create table test.t2 (
id integer generated by default as identity primary key,
t1_ids integer[] not null check (test.check_foreign_key_array(t1_ids, 'test', 't1', 'id'))
);
insert into test.t1 (id) values (default), (default), (default); --ok
insert into test.t2 (id, t1_ids) values (default, array[1,2,3]); --ok
insert into test.t2 (id, t1_ids) values (default, array[1,2,3,555]); --error
If you are able to put there just values from test.id, then you can try this:
CREATE OR REPLACE FUNCTION test_trigger() RETURNS trigger
LANGUAGE plpgsql AS $BODY$
DECLARE
val integer;
BEGIN
SELECT id INTO val
FROM (
SELECT UNNEST(id) AS id
FROM test1
) AS q
WHERE id = OLD.id;
IF val IS NULL THEN RETURN OLD;
ELSE
RAISE 'Integrity Constraint Violation: ID "%" in Test1', val USING ERRCODE = '23000';
RETURN NULL;
END IF;
END; $BODY$;
-- DROP TRIGGER test_delete_trigger ON test;
CREATE TRIGGER test_delete_trigger BEFORE DELETE OR UPDATE OF id ON test
FOR EACH ROW EXECUTE PROCEDURE test_trigger();