How to merge tables in PostgreSQL? - postgresql

I want to merge two tables from different schemas in the same PostgreSQL database but I could not get a query to work.
The two tables have lots of columns and samples, I want to select A and B from table 1, and I want to select C, D, E from table 2, where B and C items are exactly the same thing but numbers contained are not totally the same. Thus I want to merge and get A (B/C) D E.
I tried to use UNION but I got an error:
[42601]: ERROR: each UNION query must have the same number of columns.
And when I used LEFT JOIN it shows mistake around '.'.
In the last try my code looked like:
select A from table1 left join
table2.D, table2.E using B=C

You can use this kind of query:
Table
create table table1 (
A text,
B int
);
insert into table1 values ('test-a', 123);
create table table2 (
C int,
D text,
E text
);
insert into table2 values (3456, 'test-d', 'test-e');
Query
select A::text, B::text as BC, '' as D, '' as E from table1
union all
select '' as A, C::text as BC, D::text, E::text from table2
Result
a bc d e
test-a 123
3456 test-d test-e
That'll take all records from table1 (columns A, B, dummy column D and dummy column E) and add to it records from table2 (dummy column A, column C, D and E)
Example: https://rextester.com/NWSEP53051
If you are using SQLite
Tables
create table table1 (A, B);
insert into table1 values ('test-a', 123);
create table table2 (C, D, E);
insert into table2 values (3456, 'test-d', 'test-e');
Query
select A, B as BC, '' as D, '' as E from table1
union all
select '' as A, C as BC, D, E from table2
Result
| A | BC | D | E |
| ------ | ---- | ------ | ------ |
| test-a | 123 | | |
| | 3456 | test-d | test-e |
Example: https://www.db-fiddle.com/f/rE1MeJQpjGH4FZVwWmTpEX/0

You can implement merge using a temporary table
lock table test_tbl in exclusive mode;
data delete
update
insert
https://parksuseong.blogspot.com/2019/07/postgresql-insert-merge-olap.html

Related

How to reference a jsonb coulmn value from a value map in postgres

I want to be able to reference the error table for msg and description based on the err_id on the results table for err_map jsonb column, I'd also want to be able relate which error occurred against which column whether the independent columns c1,c2 or val_map jsonb column c3, c4)
the only reason the val_map stores data(with .) as "val_map.c3": 3 so we can identify that these columns were from val_map when mapping errors to columns.
I have a result table
here the err_map column values 1,3 reference to below error table
id | c1 | c2 | val_map | err_map
----------------------------------------------------------------
1 | chk1 | chk2 | {"c3":3, "c4":4} | {"c1": 1, "val_map.c3": 3}
Error Table
id | msg | description
----------------------------------------------------------------
1 | msg1 | an error1 occurred
----------------------------------------------------------------
3 | msg3 | an error3 occurred
I looked at jsonb_each and jsonb_object_keys but can't really figure out how to use it to join these tables. Any help/hints will be appreciated.
Pardon if something is unclear, please ask to provide more detail.
[Edit 1]: removed foreign key reference as it was misleading
[Edit 2]: I've got it working but it's quite inefficient
select
e.error_key,
e.error_message,
T2.key as key
from result.error e
inner join (
select
substring(T1.key, 11) as key,
T1.value
from (
select em.key, em.value
from result rd, jsonb_each(rd.error_map) as em
) as T1
where T1.key like '%value_map%'
union all
select T1.key , T1.value
from (
select em.key, em.value
from result rd, jsonb_each(rd.error_map) as em
) as T1
where T1.key not like '%value_map%'
) as T2 on T2.value::bigint = e.id;
You can simplify that UNION ALL to just
select
e.error_key,
e.error_message,
T2.key as key
from result.error e
inner join (
select
case when T1.key like 'val_map.%'
then substring(T1.key, 9)
else T1.key
end as key,
T1.value
from result rd, jsonb_each(rd.error_map) as T1
) as T2 on T2.value::bigint = e.id;

How to join two tables with nested field?

I have a table like this:
id | ciaps
1 | a|b|c
An have a second table like:
cod | desc
a | item a
b | item b
c | item c
I need a code to join this tables like:
id | ciaps
1 | item a|item b|item c
Use array_agg for concatenating string separated by '|' and convert it array_to_string to get the value expected format.
-- PostgreSQL (v11)
SELECT t1.id, t2.descr ciaps
FROM test1 t1
INNER JOIN (SELECT array_to_string(array_agg(cod), '|') cod
, array_to_string(array_agg(descr), '|') descr
FROM test2) t2
ON t1.ciaps = t2.cod;
Please check from url https://dbfiddle.uk/?rdbms=postgres_11&fiddle=6fffc7f1da6a02a48018b3691c99ad17

Postgresql table name or alias in SELECT and WHERE clauses without specifying column name

I have two tables:
CREATE TABLE a (id INT NOT NULL);
CREATE TABLE b (id INT NOT NULL);
INSERT INTO a VALUES (1), (2);
INSERT INTO b VALUES (1);
If I try to get records from a for which there are records in b (query 1):
SELECT a.id, b FROM a LEFT JOIN b on a.id = b.id WHERE b is NOT NULL;
I get:
id | b
----+-----
1 | (1)
If I try to get records from a for which there are NO records in b (query 2):
SELECT a.id, b FROM a LEFT JOIN b on a.id = b.id WHERE b IS NULL;
I get:
id | b
----+---
2 |
It seems OK.
Then I alter b:
ALTER TABLE b ADD COLUMN s TEXT NULL;
then query 1 does not return any rows, query 2 returns the same rows and
SELECT a.id, b FROM a LEFT JOIN b on a.id = b.id;
returns
id | b
----+------
1 | (1,)
2 |
My questions are:
Why does Postresql allow to use table name or alias in WHERE clause without specifying column name?
What is (1,) in column b of resulting rows?
Why does (1,) not satisfy IS NULL and IS NOT NULL in query 1 and query 2?
P.S. If I alter table b as ALTER TABLE b ADD COLUMN s TEXT NOT NULL DEFAULT '' instead then queries 1 and 2 return the same rows.
Answering by questions:
This is row constructor, so every value from a column builds up a row value (composite value) using values from your columns for its member fields
(1,) is a row constructor with first member being 1 and second member (your text field) which has a null value, thus no value is shown.
You're comparing entire row constructor which actually satisfies both of comparison (is null and is not null)
More on point 3:
select *, b is not null as b_not_null, b is null as b_null from b;
Reult:
id | b_not_null | b_null
----+------------+--------
1 | t | f
A row IS NULL when all of its members have NULL values, otherwise it IS NOT NULL. Reproduce:
create table rowtest ( col1 int, col2 int);
insert into rowtest values (null,null), (1,1), (null,1);
select
col1, col2, rowtest,
case when rowtest is null then true else false end as rowtest_null
from rowtest;
Result:
col1 | col2 | rowtest | rowtest_null
------+------+---------+--------------
| | (,) | t
1 | 1 | (1,1) | f
| 1 | (,1) | f
Actually, for your queries they both could be rewritten to:
Query1: Get records from a with matching records from b
Using INNER JOIN which actually is the same as JOIN:
SELECT a.id, b FROM a JOIN b on a.id = b.id;
Query2: Get records from a with no matching records from b
Using NOT EXISTS instead of LEFT JOIN:
SELECT a.id
FROM a
WHERE NOT EXISTS (
SELECT 1
FROM b
WHERE a.id = b.id
);
For the last query if you really need the second empty column you can add a static value to select list like that:
SELECT a.id, null as b
The table name can be used in the SELECT or WHERE to refer to a record value containing the entire row of the table. In the output of psql a record will appear like (1) (if it has one field), or (1,2) (if it has two fields), etc. The (1,) that you see is a record with two fields that contain the values 1 and NULL. A value of record type can be null, e.g. in a left join if there is no matching row for the second table.

Comparing tables and getting non matching values

I'm pretty new to SQL and I can't get this to work I've got these two tables below
Table A Table B
_________________ _________________
| A | 2015-10-4 | B | 2015-11-6
| B | 2015-11-4 | C | 2015-05-4
| C | 2015-05-6 | D | 2015-05-8
| D | 2015-05-7 | C | 2015-05-5
I'm trying to write a stored procedure that will get all letters from table B that has a date less than table A and any letter that doesn't exist in table B.
This is what I have so far
SELECT *
FROM A q JOIN
B c ON q.Letter = c.Letter AND q.Date > c.Date OR c.Letter IS NULL
This returns C but I can't have it return A also. It's confusing to me trying to join and compare tables still.
I do not want duplicate rows, the results I would be expecting would return
| A | 2015-10-4
| C | 2015-05-6
EDIT
I'm running into an issue now where if I have a case like this
Table A Table B
_________________ _________________
| A | 2015-10-4 | B | 2015-11-6
| B | 2015-11-4 | C | 2015-05-4
| C | 2015-05-6 | D | 2015-05-8
| D | 2015-05-7 | C | 2015-05-5
| C | 2015-05-7
It will still return C for some reason. Using a.date > max(b.date) doesn't work because max can't used that way. And I want to assume the max date can be anywhere in the table in table B.
So now my new results would be
| A | 2015-10-4
But I am getting A and C still.
You should use a LEFT JOIN:
SELECT DISTINCT A.letter, A.[Date]
FROM dbo.TableA A
LEFT JOIN dbo.TableB B
ON A.letter = B.letter
WHERE B.[Date] < A.[Date] OR B.letter IS NULL;
UPDATE
You should have explained your requirements as: "get all letters from table B in which every date is lesser than...."
SELECT DISTINCT A.letter, A.[Date]
FROM dbo.TableA A
LEFT JOIN (SELECT letter, MAX([Date]) [Date]
FROM dbo.TableB
GROUP BY letter) B
ON A.letter = B.letter
WHERE B.[Date] < A.[Date] OR B.letter IS NULL;
I would go for a UNION / UNION ALL, so that you get the result subset for the first condition + the ones for the second one.
Something similar to this should do the job:
sqlite> create table A (letter, my_date);
sqlite> create table B (letter, my_date);
sqlite> insert into A values ('A', '2015-10-04');
sqlite> insert into A values ('B', '2015-11-04');
sqlite> insert into A values ('C', '2015-05-06');
sqlite> insert into A values ('D', '2015-05-07');
sqlite> insert into B values ('B', '2015-11-06');
sqlite> insert into B values ('C', '2015-05-04');
sqlite> insert into B values ('D', '2015-05-08');
sqlite> insert into B values ('C', '2015-05-05');
A 2015-10-04
sqlite> select B.* from A, B where A.letter = B.letter and B.my_date < A.my_date UNION ALL select A.* from A where not exists (select 1 from B where B.letter=A.letter);
letter my_date
---------- ----------
C 2015-05-04
C 2015-05-05
A 2015-10-04

Convert columns to row in Postgres

My Table name is osk,st,item. osk fields are stid,stock,free,npr,itemno. st fields are stid,name. items fields are id,name. And i have multiple schema. Every schemas have this table
select i.name,st.name,stock,freestock from osk,st,items i where i.id=osk.itemno
and st.stid=osk.stid
this query return result like this
Name St.Name Stock FreeStock
A B 10 20
D B 10 10
C E 12 10
But I want
Name B (stock) B(Free) E(Stock) E (Free Stock)
A 10 20 - -
D 10 10 - -
C - - 12 10
How to acheive this. (i have multiple schema. all schemas have this table i want retrive from all schema)
am using postgresql 9.3. IF possible to use CrossTab? How to use it?
Assuming you have a table like this (had to guess since you didn't provide the exact table definitions):
create table some_table (
name text,
stname text,
stock int,
freestock int
);
insert into some_table values
('A', 'B', 10, 20),
('D', 'B', 10, 10),
('C', 'E', 12, 10);
Now you can use the crosstab function like this as documented here: http://www.postgresql.org/docs/current/static/tablefunc.html
create extension if not exists tablefunc;
select coalesce(stock.name, freestock.name) as name,
stock.b as "B (stock)",
freestock.b as "B (free)",
stock.e as "E (stock)",
freestock.e as "E (free)"
from
crosstab('
select name,
stname,
stock
from some_table
', '
select distinct stname
from some_table
order by stname
') as stock(name text, b int, e int)
full outer join
crosstab('
select name,
stname,
freestock
from some_table
', '
select distinct stname
from some_table
order by stname
') as freestock(name text, b int, e int)
on stock.name = freestock.name;
Which results in:
name | B (stock) | B (free) | E (stock) | E (free)
------+-----------+----------+-----------+----------
A | 10 | 20 | |
C | | | 12 | 10
D | 10 | 10 | |
(3 rows)