PostgreSQL input bulk data While Skipping Relation Error - postgresql

Im doing a bulk insert to PostgreSQL using PG_Admin tools, the table field anamnesa_id contain ForeignKey relation to other table pasien_anamnesa,
Is there a way to ignore or skip (which is not exist in other table) the error while inserting all the query? because deleting the error query 1 by 1 is just impossible for this many data (25.000 records),
i've trying:
INSERT INTO "pasien_item" ("id", "anamnesa_id") VALUES (1, 2) ON CONFLICT ON CONSTRAINT pasien_item_pkey DO NOTHING;
resulting error:
ERROR: insert or update on table "pasien_item" violates foreign key constraint "pasien_item_anamnesa_id_dc66b31b_fk_pasien_anamnesa_id"
DETAIL: Key (anamnesa_id)=(2) is not present in table "pasien_anamnesa".
SQL state: 23503
from that error i also tried:
INSERT INTO "pasien_item" ("id", "anamnesa_id") VALUES (1, 2) ON CONFLICT ON CONSTRAINT pasien_item_anamnesa_id_dc66b31b_fk_pasien_anamnesa_id DO NOTHING;
resulting error:
ERROR: constraint in ON CONFLICT clause has no associated index
SQL state: 42809

ON CONFLICT can only deal with unique constraints, not foreign key or check constraints.
You need to rewrite your query to use a SELECT that only returns the rows where the foreign keys exist:
INSERT INTO pasien_item(id, anamnesa_id)
select v.id, v.anamnesa_id
from (
VALUES (1, 2), ...
) v(id, anamnesa_id)
WHERE EXISTS (select *
from pasien_anamnesa pa
where pa.anamnesa_id = v.anamnesa_id)
ON CONFLICT ON CONSTRAINT pasien_item_pkey DO NOTHING;
Online example

Related

PostgreSQL insert multiple values if foreign key exists

Given:
Table A with multiple rows and attributes: (A_attr1 (key) , A_attr2).
Table B with attributes (B_attr1 (key) , A_attr1 (foreign key), B_attr2).
How do I insert some values in the table B only if the foreign key exists?
In Postgres, we can use the Where Exists to implement your use case.
Here is an example of using it.
Insert Into Table_B Select 'Value 1', 'Foreign Value', 'Value 2' Where Exists
(Select 1 From Table_A Where A_attr1 = 'Foreign Value');
This will insert only if the "Foreign Value" is present in Table_A.
Hope this helps
First, we need to consider the fact that the condition (existence of foreign key in table A) is fundamental, in fact, if we try to add values in Table_B with an A_attr1 that it doesn't exist in Table_A we get an error of this type:
ERROR: the INSERT or the UPDATE on the TABLE table_B violates the foreign key constraint
"_A_attr1_"
DETAIL: the key (A_attr1)=(wrong_value) it's not present in the table "Table_A"
This is a possible solution:
INSERT INTO Table_B(B_attr1, A_attr1, B_attr2)
SELECT x.*
FROM (VALUES
(something,something_else, something_else2),
(something_else3,something_else4, something_else5),
...
(something_else20, something_else21,something_else22)
) x(B_attr1, A_attr1, B_attr2)
WHERE EXISTS(
SELECT FROM TABLE_A y
WHERE (y.A_attr1)=(x.A_attr1)
FOR SHARE);
The result is the addition in B of all the tuples that are acceptable (that is the one with the existing foreign keys).
This post is an extension of the following question:
PostgreSQL insert if foreign key exists
The solution is based on the comments on this post:
https://dba.stackexchange.com/questions/252875/how-to-make-on-conflict-work-for-compound-foreign-key-columns/252925#252925

Postgres Insert if not exists, Update if exists on non-unique column?

I am currently using ON CONFLICT SET to update if there's a duplicate value on a unique column.
INSERT INTO `table`(col1, col2) VALUES ('v1', 'v2')
ON CONFLICT (col1)
DO UPDATE SET
col2 = 'v3'
From the example, col1 is a unique field. But how do I do this if col1 is not unique?
I tried without the unique constraint now I'm getting:
Invalid column reference: 7 ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
By the very definition you cannot have a conflict on a non-unique column. But since you do not duplicates just make it unique.
Alter table "table" add constraint col1_uk unique(col1);
Ended up using 2 successive queries. (1) Try to update first, if no rows updated, (2) try to insert.
In php:
// Try update
$return = execute("UPDATE table SET ... WHERE ...col1='unique_value'...");
// If update returns no value, insert
if (!$return) {
execute("
INSERT INTO table (...)
SELECT ...values...
WHERE NOT EXISTS (SELECT 1 FROM table WHERE ...col1='unique_value'...)
");
}

Inserting data into a table by checking some constraint of another table in Postgresql

I have two tables;
route_info: (routeinfo_id(primary key),route_src,route_dest,driver_id(foreign key)
route_details: (routedetails_id (primary key), route_latitude,route_longitude,route_address, routeinfo_id (foreign key)).
I am facing an error while trying to insert data into route_details table by checking some constraint of route_info table.
My query:
INSERT INTO route_details (route_latitude,route_longitude,routeinfo_id) VALUES (78.23,27.54,1)
SELECT *
FROM route_details
WHERE route_address='CNG Station,Agartala'
AND routeinfo_id IN (SELECT routeinfo_id
FROM route_info
WHERE route_info.route_source='Udaipur'
AND route_info.route_destination='Agartala'
and route_info.driver_id=1);

UPSERT - INSERT ... ON CONFLICT fails with a function based index as 'lower()' unique constraint

I have a database table with a code column that uses a lowercase index to prevent code values that only differ in case (e.g. 'XYZ' = 'xYZ' = 'xyz'). The typical way in Postgresql is to create a function based index, like this: CREATE UNIQUE INDEX mytable_lower_code_idx ON mytable (lower(code)).
Now I have a case where I need upsert behaviour on that column:
-- first insert
INSERT INTO mytable (code) VALUES ('abcd');
-- second insert, with upsert behaviour
INSERT INTO mytable (code) VALUES ('Abcd')
ON CONFLICT (code) DO UPDATE
SET code='Abcd';
For the second insert I get a unique key violation: ERROR: duplicate key value violates unique constraint "mytable_lower_code_idx"
(I also tried to use ON CONFLICT ON CONSTRAINT mytable_lower_code_idx but Postgresql tells me that this constraint does not exist so maybe it doesn't treat the index as a constraint.)
My final question: Is there any way to make INSERT ... ON CONFLICT work together with indexes on expressions? Or must I introduce a physical indexed lowercase column to accomplish the task?
Use ON CONFLICT (lower(code)) DO UPDATE:
CREATE TABLE mytable (
code text
);
CREATE UNIQUE INDEX mytable_lower_code_idx ON mytable (lower(code));
INSERT INTO mytable VALUES ('abcd');
INSERT INTO mytable (code) VALUES ('Abcd')
ON CONFLICT (lower(code)) DO UPDATE
SET code='Abcd';
SELECT * FROM mytable;
yields
| code |
|------|
| Abcd |
Note that ON CONFLICT syntax
allows for the conflict target to be an index_expression (my emphasis):
ON CONFLICT conflict_target
where conflict_target can be one of:
( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
ON CONSTRAINT constraint_name
and index_expression:
Similar to index_column_name, but used to infer expressions on
table_name columns appearing within index definitions (not simple
columns). Follows CREATE INDEX format.
Try to add your index as follow:
CREATE UNIQUE INDEX CONCURRENTLY mytable_lower_code_idx
ON mytable (lower(code));
ALTER TABLE mytable
ADD CONSTRAINT unique_mytab_code
UNIQUE USING INDEX mytable_lower_code_idx ;
and then:
INSERT INTO mytable (code) VALUES ('abcd');
-- second insert, with upsert behaviour
INSERT INTO mytable (code) VALUES ('Abcd')
ON CONFLICT ON CONSTRAINT unique_mytab_code DO UPDATE
SET code='Abcd';

insert several row in order to satisfy a constraint

I have two table: deck(id) and card(deck,color,value)
deck have those constraints:
CHECK (fifty_two_cards_deck(id))
PRIMARY KEY (id)
CREATE FUNCTION fifty_two_cards_deck(deck integer) RETURNS boolean
LANGUAGE sql STABLE STRICT
AS $$ SELECT COUNT(*)=52 FROM card WHERE deck=$1 $$;
and card have those constraints:
FOREIGN KEY (deck) REFERENCES deck(id)
PRIMARY KEY (deck, color, value)
How can I insert a new deck?
I tried this:
begin transaction;
INSERT INTO "public"."deck" ("id") VALUES (nextval('deck_id_seq'::regclass));
INSERT INTO "public"."card" ("deck", "color", "value") VALUES ('1', enum_first(null::Suit), enum_first(null::Symbol));
end transaction
(i had edit fifty_two_cards_deck to be a one_card_deck for testing purpose)
but I got this error:
SQL error:
ERROR: new row for relation "deck"
violates check constraint
"fifty_two_cards_deck"
In statement: begin transaction;
INSERT INTO "public"."deck" ("id")
VALUES
(nextval('deck_id_seq'::regclass));
INSERT INTO "public"."card" ("deck",
"color", "value") VALUES ('1',
enum_first(null::Suit),
enum_first(null::Symbol));
end transaction
How can I solve this without removing the constraints?
EDIT: solution
thx to Magnus Hagander I got it working like this (after setting the foreign key deferrable):
begin transaction;
SET CONSTRAINTS ALL DEFERRED;
INSERT INTO "public"."deck-card" ("deck", "position", "color", "value") VALUES (1, 0, enum_first(null::suit), enum_first(null::Symbol));
INSERT INTO "public"."deck" ("id") VALUES (1);
end transaction
It might work if you make the FOREIGN KEY with DEFERRABLE, and then set it to DEFERRED. Then you insert into the "card" table first, and then into "deck". Check constraints execute at the time of insert (thus, well before the entries in "card" exist), and cannot be deferred to transaction end.
But that's not actually going to work around the fact that your constraint is broken and should be removed ;) That CHECK constraint will only check rows going into "deck". But once the row has been inserted there, you will still be able to add more rows to, or delete rows from, the "card" table and the CHECK constraint will not complain - until the next time you try to modify "deck".