SQL insert new rows but keep old value in conflicting keys - postgresql

Say I have two tables:
tb1:
id name date
1 John 01/01/2012
1 John 01/02/2012
2 James 02/02/2020
tb2:
id name date
1 John 01/01/2013
1 John 01/01/2012
The uniqueness of both tb1 and tb2 comes from the combination of (id, name,date) columns. Therefore I would like to insert only values from tb2 that are new to tb1. In this case only (1,John,01/01/2013) would be inserted since the other row is already present in tb1.
My try is:
INSERT INTO tb1 (date) SELECT * FROM tb2 ON CONFLICT (id,name,date) DO NOTHING;

You did not tell us what the error is that you get. But just from a syntax check, it will result in the error:
ERROR: INSERT has more expressions than target columns
because you specify one target columns, but provide three columns from the SELECT.
Assuming you did specify a unique constraint or primary key on the three columns, adding the additional columns in the INSERT statement should work:
INSERT INTO tb1 (id,name,date)
SELECT id,name,date
FROM tb2 ON CONFLICT (id,name,date) DO NOTHING;

SQL language is a non procedural language, just a query language... You must do this with queries, like :
INSERT INTO tb1 (id,name,date)
SELECT *
FROM tb2
WHERE NOT EXISTS(SELECT *
FROM tb1 INNER JOIN tb2
ON ROW (tb1.id, tb1.name, tb1.date) =
ROW (tb2.id, tb2.name, tb2.date));

Related

add Exists operator in a union sql query

I have two tables that am using union to combine the result-set of them my problem here is Each SELECT statement within UNION must have the same number of columns and data types, I don't have the same number of columns so am creating null columns
select
d.deal_id as order_id,
EXISTS(select * from table1 c where d.user_id = c.user_id) as IsUser, --this returns boolean value
from table 1 c
union
select
cast(o.id as varchar) as order_id,
coalesce('No_user'::text,'0'::text) as IsUser, --i get an error :UNION types boolean and character varying cannot be matched
from table2 o
how can I create a null column in table2 that matches the boolean data type of the table1
how can I create a null column in table2 that matches the boolean data type of the table1
By putting NULL into the SELECT list of the second query.
Generally (in SQL) the datatype for the column is dictated by the first query in the union set and other queries must match. You don't need column aliases for subsequent queries either, but they may help make a query more readable:
select
d.deal_id as order_id,
EXISTS(select * from table1 c where d.user_id = c.user_id) as IsUser, --this returns boolean value
from table 1 c
union
select
cast(o.id as varchar) as order_id,
null --IsUser
from table2 o
If you have a case where the type of the column in the first query is different to what you want in the output, you cast the first column:
select 1::boolean as boolcol
UNION
select true
This will ensure that the column is boolean, rather than giving a "integer and bool cannot be matched". Remember also that, should you need to, NULL can be cast to a type, e.g. select null::boolean as bolcol

PostgreSQL - Append a table to another and add a field without listing all fields

I have two tables:
table_a with fields item_id,rank, and 50 other fields.
table_b with fields item_id, and the same 50 fields as table_a
I need to write a SELECT query that adds the rows of table_b to table_a but with rank set to a specific value, let's say 4.
Currently I have:
SELECT * FROM table_a
UNION
SELECT item_id, 4 rank, field_1, field_2, ...
How can I join the two tables together without writing out all of the fields and without using an INSERT query?
EDIT:
My idea is to join table_b to table_a somehow with the rank field remaining empty, then simply replace the null rank fields. The rank field is never null, but item_id can be duplicated and table_a may have item_id values that are not in table_b, and vice-versa.
I am not sure I understand why you need this, but you can use jsonb functions:
select (jsonb_populate_record(null::table_a, row)).*
from (
select to_jsonb(a) as row
from table_a a
union
select to_jsonb(b) || '{"rank": 4}'
from table_b b
) s
order by item_id;
Working example in rextester.
I'm pretty sure I've got it. The predefined rank column can be inserted into table_b by joining to the subset of itself with only the columns left of the column behind which you want to insert.
WITH
_leftcols AS ( SELECT item_id, 4 rank FROM table_b ),
_combined AS ( SELECT * FROM table_b JOIN _leftcols USING (item_id) )
SELECT * FROM _combined
UNION
SELECT * FROM table_a

A usually simple SQL join

I need advice on doing a JOIN with PostgreSQl. I want to take the sum (or number of times id 1 is entered) of a single id and place it into a new column in table b.
Table a
id username comment
1 Bob Hi
2 Sally Hello
1 Bob Bye
Table b
id something total_comments
1 null 2
Create a trigger for insert, update, delete on the Table a to select the sum and update in Table b
You could use SELECT INTO if table_b doesn't already exist.
SELECT
id
, NULL AS something
, COUNT(comment) AS total_comments
INTO table_B
FROM table_a
GROUP BY id
Or INSERT INTO if table_b does exist.
INSERT INTO table_b (id, something, total_comments)
SELECT
id
, NULL AS something
, COUNT(comment) AS total_comments
FROM table_a
GROUP BY id

Insert coulmns of 1 table into multiple tables as row all having same ID

I need to insert data from one table into three different tables (see example below):
before
source_table:
id row_1 row_2 row_3
after
table_1:
obj_id row_1
table_2:
obj_id row_2
table_3:
obj_id row_3
table_1, table_2 and table_3 have the same obj_id (type serial).
Any ideas on how to do that?
You should repeat the code bellow for each of the tables:
INSERT INTO table_1 (id, row_1)
SELECT id, row_1
FROM source_table
If you have serial ("auto increment") column, look at this answer: SET IDENTITY_INSERT postgresql
In SQL Server, you need to set identity_insert on first and turn it off at the end.

PostgreSQL compare with substring doesn't work

I want to select from 2 table where 2nd id is equal with first 4 character from first id
SELECT a.*, b.*, substring(a.my_id from 1 for 4)::integer as number
FROM table1 as a
INNER Join table2 as b ON(b.id_2=number) where my_id = 101
^
It produces an error here |
ERROR: column "number" does not exist
SQL state: 42703
Character: 189
You can't use an alias like that, you need a derived table:
select *
from (
SELECT t1.*,
substring(t1.my_id::text from 1 for 4)::integer as number
FROM table1 t1
) as a
inner join table2 as b ON (b.id_2 = a.number)
where my_id = 101
Storing a number that is used as a foreign key as a part in a varchar column is a really, really ugly design. That number should be a column of its own in table1.