How to add value if exists in another table - postgresql

I have table_a which has a foreign key column referencing table_b. I'd like to fill the fkey column with values from column_a if the value exists in table_b's pkid column but null if it doesn't exist.
Without looking for whether it exists, I was using the following query:
UPDATE table_a SET fkey = column_a;
That fails sometimes due to the foreign key constraint if column_a's value doesn't exist in table_b so I think I need to use some combination of CASE and EXISTS but I'm struggling with the syntax.
The constraint looks like:
ALTER TABLE table_a ADD CONSTRAINT
constraint_fkey FOREIGN KEY (fkey) REFERENCES table_b(pkid);

Assuming the column contains null in all rows, you can join table_b on a.column_a = b.pkid:
update table_a a
set fkey = column_a
from table_b b
where a.column_a = b.pkid
or check in a subquery whether b.pkid exists:
update table_a
set fkey = column_a
where exists (
select pkid
from table_b
where pkid = column_a)
If you have to set null in appropriate rows you can use exists in case:
update table_a
set fkey = (select case
when exists (
select pkid
from table_b
where pkid = column_a)
then column_a
end)

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: change primary key in existing table

I have a situation with two tables where one has a foreign key pointing to the other table (simplified) schema:
CREATE TABLE table1 (
name VARCHAR(64) NOT NULL,
PRIMARY KEY(name)
);
CREATE TABLE table2 (
id SERIAL PRIMARY KEY,
table1_name VARCHAR(64) NOT NULL REFERENCES table1(name)
);
Now I regret using the name column as primary key in table1 - and would like to add integer serial key instead. Since I already have data in the database I guess I need to do this carefully. My current plan is as follows:
Drop the foreign key constraint: table2(name) with ALTER TABLE table2 DROP CONSTRAINT table2_table1_name_fkey;
Drop the primary key constraint on table1(name) with ALTER TABLE table1 DROP CONSTRAINT name_pkey;.
Add a unique constraint on table1(name) with ALTER TABLE table1 ADD UNIQUE(name);
Add a automatic primary key to table1 with ALTER TABLE table1 ADD COLUMN ID SERIAL PRIMARY KEY;.
Add a new column table1_id to table2 with ALTER TABLE table2 ADD COLUMN table1_id INT;
Update all rows in table2 - so that the new column (which will be promoted to a foreign key) gets the correct value - as inferred by the previous (still present) foreign key table1_name.
I have completed steps up to an including step 5, but the UPDATE (with JOIN?) required to complete 6 is beyond my SQL paygrade. My current (google based ...) attempt looks like:
UPDATE
table2
SET
table2.table1_id = t1.id
FROM
table1 t1
LEFT JOIN table2 t2
ON t2.table1_name = t1.name;
You do not need JOIN in UPDATE.
UPDATE
table2 t2
SET
table1_id = t1.id
FROM
table1 t1
WHERE
t2.table1_name = t1.name;

I'm getting column "my_column" contains null values' when adding a composite primary key

Is it not supposed to delete null values before altering the table? I'm confused...
My query looks roughly like this:
BEGIN;
DELETE FROM my_table
WHERE my_column IS NULL;
ALTER TABLE my_table DROP CONSTRAINT my_table_pk;
ALTER TABLE my_table ADD PRIMARY KEY (id, my_column);
-- this is to repopulate the data afterwards
INSERT INTO my_table (name, other_table_id, my_column)
SELECT
ya.name,
ot.id,
my_column
FROM other_table ot
LEFT JOIN yet_another ya
ON ya.id = ot."fileId"
WHERE NOT EXISTS (
SELECT
1
FROM my_table mt
WHERE ot.id = mt.other_table_id AND ot.my_column = mt.my_column
) AND my_column IS NOT NULL;
COMMIT;
sorry for naming
There are two possible explanations:
A concurrent session inserted a new row with a NULL value between the start of the DELETE and the start of ALTER TABLE.
To avoid that, lock the table in SHARE mode before you DELETE.
There is a row where id has a NULL value.

Getting a list value for every row (list of ids from another table)

I have a quite ordinary table with a foreign key to another table; for example:
CREATE TABLE table_a (
id serial NOT NULL,
name text,
CONSTRAINT table_a_pkey PRIMARY KEY (id)
);
CREATE TABLE table_b (
id serial NOT NULL,
a_id integer, -- The foreign key
CONSTRAINT table_b_pkey PRIMARY KEY (id),
CONSTRAINT table_b_a_id_fkey FOREIGN KEY (a_id)
REFERENCES table_a (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
INSERT INTO table_a
VALUES (1, 'First row');
-- 2 entries in table_b which refer to the existing table_a row:
INSERT INTO table_b
VALUES (11, 1), (12, 1);
Now I'd like to have a view which gives me a list of all ids of table_b rows which refer to the current table_a row:
SELECT a.name,
(SELECT b.id
FROM table_b b
WHERE b.id = a.id) AS b_ids
FROM table_a a;
However, the b_ids column is empty; I'd like to have some kind of list there, containing the values 11 and 12.
Somewhere I read that subselects can only yield one column (ok for me in this case) and only one row (which would explain that the above query doesn't work for me). If this is true - how can this be done instead? Or do I really need to issue SELECT requests for every single table_a row in my program?
I'd like this to work with PostgreSQL 9.1 and 9.3.
You can use the array_agg function :
SELECT table_a.name, array_agg(table_b.id)
FROM table_a
LEFT OUTER JOIN table_b
ON table_a.id = table_b.a_id
GROUP BY table_a.name;
┌───────────┬───────────┐
│ name │ array_agg │
├───────────┼───────────┤
│ First row │ {11,12} │
└───────────┴───────────┘
(1 row)
select name
,string_agg(b.id::text,',') b_ids
from table_a join table_b b on table_a.id= b.a_id
group by name

PostgreSQL - properly change ID of table row

How to change id of some table's row?
Like:
UPDATE table SET id=10 WHERE id=5;
But in way that it would cascade changes to every other table that references this table with that id?
I want to do this, because I need to import data from another database which has most of the same tables, but ids are different. So if ids would match old database, it would be easier to import data correctly.
Suppose you have these two tables:
create table referenced (id integer primary key);
create table referencer (a integer references referenced (id));
Table referencer references table referenced:
=> \d referencer
Table "public.referencer"
Column | Type | Modifiers
--------+---------+-----------
a | integer |
Foreign-key constraints:
"referencer_a_fkey" FOREIGN KEY (a) REFERENCES referenced(id)
Then you insert a value in both:
insert into referenced values (1);
insert into referencer values (1);
select *
from
referenced rd
inner join
referencer rr on rd.id = rr.a
;
id | a
----+---
1 | 1
Now you want to change the reference to on update cascade:
alter table referencer
drop constraint referencer_a_fkey,
add foreign key (a) references referenced (id) on update cascade;
And update it:
update referenced set id = 2;
select *
from
referenced rd
inner join
referencer rr on rd.id = rr.a
;
id | a
----+---
2 | 2
Now you will have another problem in the referenced table primary key if the updated id already exists. But that would make another question.
UPDATE
This is dangerous so backup the db first. It must be done as superuser:
update pg_constraint
set confupdtype = 'c'
where conname in (
select
c.conname
from
pg_constraint c
inner join
pg_class referenced on referenced.oid = c.confrelid
where
referenced.relname = 'referenced'
and
c.contype = 'f'
);
It will change all the foreign key constraints on the referenced table to on update cascade
You will need to change your foreign key and set ON UPDATE action to CASCADE. When you change a value, all associated values will be changed too.
This is an example how to define it:
CREATE TABLE order_items (
product_no integer REFERENCES products ON UPDATE CASCADE,
order_id integer REFERENCES orders ON UPDATE CASCADE,
quantity integer,
PRIMARY KEY (product_no, order_id)
);
For more information see http://www.postgresql.org/docs/current/static/ddl-constraints.html