Update new value on conflict - postgresql

I'd like to update a row with an insert statement. Indeed, I can insert rows but if the unique attribute already exists, I'd like to update the content.
So I got these tables
CREATE TABLE dtest (
"id" text,
followers_count int4,
someuniq int4
);
CREATE UNIQUE INDEX dtest_idx ON dtest USING btree (someuniq);
CREATE TABLE temp_data (
tid text,
tfo int4,
tuniq int4);
Let's consider that I have a temp tab, and I insert/update data from this table
INSERT INTO temp_data VALUES ('id1',4,1);
INSERT INTO temp_data VALUES ('id2',0,2);
INSERT INTO temp_data VALUES ('id3',40,3);
INSERT INTO dtest("id","followers_count","someuniq")
SELECT t.tid, t.tfo, t.tuniq
FROM temp_data t
ON CONFLICT DO NOTHING;
Instead of doing an insert and then an update, I'd like to know if it's possible to update the values with something like this
INSERT INTO dtest("id","followers_count","someuniq")
SELECT tid, tfo, tuniq
FROM temp_data
ON CONFLICT ("someuniq")
DO UPDATE SET followers_count =
(SELECT tfo FROM temp_data where tid = EXCLUDED.tid)
WHERE EXCLUDED.id = tid;
Wich means, "Update some fields if the row already exists", what am I doing wrong ?

There is no need to use a sub-select. The excluded row will contain all target columns, including the followers_count:
INSERT INTO dtest (id, followers_count, someuniq)
SELECT tid, tfo, tuniq
FROM temp_data
ON CONFLICT (someuniq)
DO UPDATE
SET followers_count = excluded.followers_count;

Related

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.

Duplicate rows on insert in Firebird DB and Python

I have a database with a DDL like this:
CREATE TABLE table(
ID INTEGER NOT NULL,
COLUMN1 VARCHAR(50),
PRIMARY KEY (ID)
);
and with ID autoincrementing on insert like this:
CREATE TRIGGER table_BI FOR table BEFORE INSERT AS
BEGIN
NEW.ID = GEN_ID(generator, 1);
END
My problem is when I add a new row with the same value in the column1 as in an already existing row I get two rows with the same values but different ID.

Copying records in a table with self referencing ids

I have a table with records which can reference another row in the same table so there is a parent-child relationship between rows in the same table.
What I am trying to achieve is to create the same data for another user so that they can see and manage their own version of this structure through the web ui where these rows are displayed as a tree.
Problem is when I bulk insert this data by only changing user_id, I lose the relation between rows because the parent_id values will be invalid for these new records and they should be updated as well with the newly generated ids.
Here is what I tried: (did not work)
Iterate over main_table
copy-paste the static values after each
do another insert on a temp table for holding old and new ids
update old parent_ids with new ids after loop ends
My attempt at doing such thing(last step is not included here)
create or replace function test_x()
returns void as
$BODY$
declare
r RECORD;
userId int8;
rowPK int8;
begin
userId := (select 1)
create table if not exists id_map (old_id int8, new_id int8);
create table if not exists temp_table as select * from main_table;
for r in select * from temp_table
loop
rowPK := insert into main_table(id, user_id, code, description, parent_id)
values(nextval('hibernate_sequence'), userId, r.code, r.description, r.parent_id) returning id;
insert into id_map (old_id, new_id) values (r.id, rowPK);
end loop;
end
$BODY$
language plpgsql;
My PostgreSQL version is 9.6.14.
DDL below for testing.
create table main_table(
id bigserial not null,
user_id int8 not null,
code varchar(3) not null,
description varchar(100) not null,
parent_id int8 null,
constraint mycompkey unique (user_id, code, parent_id),
constraint mypk primary key (id),
constraint myfk foreign key (parent_id) references main_table(id)
);
insert into main_table (id, user_id, code, description, parent_id)
values(0, 0, '01', 'Root row', null);
insert into main_table (id, user_id, code, description, parent_id)
values(1, 0, '001', 'Child row 1', 0);
insert into main_table (id, user_id, code, description, parent_id)
values(2, 0, '002', 'Child row 2', 0);
insert into main_table (id, user_id, code, description, parent_id)
values(3, 0, '002', 'Grand child row 1', 2);
How to write a procedure to accomplish this?
Thanks in advance.
It appears your task is coping all data for a given user to another while maintaining the hierarchical relationship within the new rows. The following accomplishes that.
It begins creating a new copy of the existing rows with the new user_id, including the old row parent_id. That will be user in the next (update) step.
The CTE logically begins with the new rows which have parent_id and joins to the old parent row. From here it joins to the old parent row to the new parent row using the code and description. At that point we have the new id along with the new parent is. At that point just update with those values. Actually for the update the CTE need only select those two columns, but I've left the intermediate columns so you trace through if you wish.
create or replace function copy_user_data_to_user(
source_user_id bigint
, target_user_id bigint
)
returns void
language plpgsql
as $$
begin
insert into main_table ( user_id,code, description, parent_id )
select target_user_id, code, description, parent_id
from main_table
where user_id = source_user_id ;
with n_list as
(select mt.id, mt.code, mt.description, mt.parent_id
, mtp.id p_id,mtp.code p_code,mtp.description p_des
, mtc.id c_id, mtc.code c_code, mtc.description c_description
from main_table mt
join main_table mtp on mtp.id = mt.parent_id
join main_table mtc on ( mtc.user_id = target_user_id
and mtc.code = mtp.code
and mtc.description = mtp.description
)
where mt.parent_id is not null
and mt.user_id = target_user_id
)
update main_table mt
set parent_id = n_list.c_id
from n_list
where mt.id = n_list.id;
return;
end ;
$$;
-- test
select * from copy_user_data_to_user(0,1);
select * from main_table;
CREATE TABLE 'table name you want to create' SELECT * FROM myset
but new table and myset column name should be equal and you can also
use inplace of * to column name but column name exist in new table
othwerwise getting errors

How to use WHILE EXISTS in a loop

CREATE TABLE [CandidateDocsAssociation](
[Row_ID] [bigint] IDENTITY(1,1) NOT NULL,
[Doc_ID] [bigint] NOT NULL,
[Candidate_ID] [bigint] NOT NULL,
) ON [PRIMARY]
GO
I have the above table structure to store the association between documents and candidates. Row_ID is an auto generated primary key. Doc_ID is a foreign key referencing the documents table. Candidate_ID is also a foreign key referencing the Candidates table.
A candidate can be associated with more than one document and one document can be associated with multiple candidates.
What i want to achieve is insert a default common document (Doc_ID) for all candidates(DISTINCT) if a Candidate_ID row with a DOC_ID of 2 does not already exist.
Below is what i'm trying but it ain't working
WHILE EXISTS (SELECT DISTINCT Candidate_ID from CandidateDocsAssociation
WHERE Doc_ID <> (SELECT Doc_ID FROM Doc_Table WHERE Doc_Name = N'Default'))
BEGIN
INSERT CandidateDocsAssociation (Doc_ID, Candidate_ID) VALUES ((SELECT Doc_ID FROM Doc_Table WHERE Doc_Name = N'Default'),Candidate_ID)
END
GO
Forget the loop and do a set-based operation. Assuming you have a Candidates table:
INSERT INTO CandidateDocsAssociation (Doc_ID, Candidate_ID)
SELECT dt.Doc_ID, c.Candidate_ID
FROM Doc_Table dt
CROSS JOIN Candidates c
WHERE dt.Doc_Name = N'Default'
AND NOT EXISTS(SELECT * FROM CandidateDocsAssociation cda
WHERE cda.Candidate_ID=c.Candidate_ID
AND cda.Doc_ID=dt.Doc_ID)
try with this (use NOT IN Clause)
WHILE EXISTS (SELECT DISTINCT Candidate_ID from CandidateDocsAssociation
WHERE Doc_ID NOT IN (SELECT Doc_ID FROM Doc_Table WHERE Doc_Name = N'Default'))
BEGIN
INSERT CandidateDocsAssociation (Doc_ID, Candidate_ID) VALUES ((SELECT Doc_ID FROM Doc_Table WHERE Doc_Name = N'Default'),Candidate_ID)
END
GO

insert into and select

how would the query on:
Update the field total_horas with the hours worked on each project
I have:
insert into proyecto(total_horas)
select trabaja.nhoras
from trabaja;
But it's trying to insert in the first firld of "proyecto" instead on the field "total_horas"
my table:
CREATE TABLE proyecto (
cdpro CHAR(3) NOT NULL PRIMARY KEY,
nombre VARCHAR(30),
coddep CHAR(2),
FOREIGN KEY (coddep)
REFERENCES departamento(cddep)
ON DELETE CASCADE
);
also altered with: alter table proyecto ADD total_horas char ;
You have to put a where condition in select statement.And please elaborate you question. trabaja.nhoras is the column name and you are selecting it from table trabaja
Example:
INSERT INTO proyecto
(total_horas)
SELECT trabaja.nhoras
FROM trabaja
WHERE 'condition' = 'some condition';