Postgres 13 join from another table on JSONB array of String - postgresql

I have a table with JSONB column. In this column we store identifiers of another table as json array of strings. How can I join the tables
Table Customer:
CustomerID
Name
Campaigns (JSONB)
1
John
[ "rxuatoak", "vsnxcvdsl", "jkiasokd" ]
2
Mick
[ "jdywmsks", "nxbsvwios", "jkiasokd" ]
Table Campaign:
CampaignID
Identifier
CampaignName
1
rxuatoak
Alpha
2
vsnxcvdsl
Bravo
3
jkiasokd
Charlie
4
jdywmsks
Delta
5
nxbsvwios
Echo
Result something like:
CustomerID
Name
CampaignNames
1
John
Alpha, Bravo, Charlie
2
Mick
Delta, Echo, Charlie
I tried many ways, and could only find online help with json objects inside the jsonb column. My jsonb column has simple array of strings.
Using POSTGRES 13

You can apply a JOIN operation between the two tables on condition that an identifier is found within a campaign (using ? operator). Then apply aggregation with STRING_AGG, with respect to the "CustomerID" and "Name"
SELECT customer.CustomerID,
customer.Name_,
STRING_AGG(campaign.CampaignName, ',') AS CampaignNames
FROM customer
INNER JOIN campaign
ON customer.Campaigns ? campaign.Identifier
GROUP BY customer.CustomerID,
customer.Name_
Check the demo here.

Related

How to create an inline table from an existing table

I have a table in qlik Sense loaded from the database.
Example:
ID
FRUIT
VEG
COUNT
1
Apple
5
2
Figs
10
3
Carrots
20
4
Oranges
12
5
Corn
10
From this I need to make a filter that will display all the Fruit/Veg records along with records from other joined tables, when selected.
The filter needs to be something like this :
|FRUIT_XXX|
|VEG_XXX |
Any help will be appreciated.
I do not know how to do it in qlicksense, but in SQL it's like this:
SELECT
ID
CASE WHEN FRUIT IS NULL THEN VEG ELSE FRUIT END as FruitOrVeg,
COUNT
FROM tablename
Not sure if its possible to be dynamic. Usually I solve these by creating a new field that combines the values from both fields into one field
RawData:
Load * Inline [
ID , FRUIT ,VEG , COUNT
1 , Apple , , 5
2 , Figs , , 10
3 , ,Carrots , 20
4 , Oranges , , 12
5 , ,Corn , 10
];
Combined:
Load
ID,
'FRUIT_' & FRUIT as Combined
Resident
RawData
Where
FRUIT <> ''
;
Concatenate
Load
ID,
'VEG_' & VEG as Combined
Resident
RawData
Where
VEG <> ''
;
This will create new table (Combined) which will be linked to the main table by ID field:
The new Combined field will have the values like this:
And the UI:
P.S. If further processing is needed you can join the Combined table to the RawData table. This way the Combined field will become part of the RawData table. To achieve this just extend the script a bit:
join (RawData)
Load * Resident Combined;
Drop Table Combined;

Cross tab function in Postgres for a complex sql statement

I'm having the following select:
SELECT t.groupid,
yk.short_name AS kind,
yg.short_name,
COALESCE(p.price, 0::numeric) AS price,
p.changed_by_username,
p.changed_date,
p.market_id
FROM prices p
RIGHT JOIN ( SELECT pos.supply_item_output_group_id AS groupid,
pos.credit_item_output_kind_id AS kindid
FROM optimization_standards pos
UNION
SELECT pos2.output_group_id,
pos2.output_kind_id
FROM optimization_standards pos2) t ON p.output_kind_id = t.kindid AND p.output_group_id = t.groupid
JOIN output_kinds yk ON yk.output_kind_id = p.output_kind_id
JOIN output_group yg ON yg.output_group_id = p.output_group_id
GROUP BY t.groupid, yk.short_name, yg.short_name, p.price, p.changed_by_username, p.changed_date, p.market_id
ORDER BY t.groupid;
that produces the following result:
groupid
kind
short_name
price
changed_by_username
changed_date
market_id
1001
Prime
shrt_1001
1.3777
1
1001
Prime
shrt_1001
1.3777
2
1001
Filata
shrt_1001
2.3123
1
1001
Filata
shrt_1001
2.3123
2
And I want to use crosstab function in order to produce a result like the following:
groupid kind short_name
price_market_us
changed_by_username_us
changed_date_us
price_market_eu
changed_by_username_eu
changed_date_eu
1001 - Prime - shrt_1001
1.3777
1.3777
1001 - Filata -shrt_1001
2.3123
2.3123
Basically I want to group the data based on markets and create the pivot table by joining data from the markets.
Will it be possible taking into consideration that I don't have a surogate key just a composite key (groupid and kindid). Should I transform the price table into a new one that has the information needed based on merging rows based in market. How the merge could be done? Should I create a view instead of using crosstab.
I new to cross_tab function and my use case seems a little complex for cross_tab.
Thank you in advance for your help,

Postgres get null if row doesn't exist in where clause

I've a postgres table with data like
Name
Attendance
Jackie
2
Jade
5
Xi
10
Chan
15
In my query I want all present by name, and if name doesn't exist return "null" instead of no row for that particular name
Eg
query where Name in ('Jackie', 'Jade', 'Cha', 'Xi')
Should return
Name
Attendance
Jackie
2
Jade
5
NULL
NULL
Xi
10
To produce the desired rows, you need to join with a table or set of rows which has all those names.
You can do this by inserting the names into a temp table and joining on that, but in Postgres you can turn an array of names into a set of rows using unnest. Then left join with the table to return a row for every value in the array.
select attendances.*
from
unnest(ARRAY['Jackie','Jade','Cha','Xi']) as names(name)
left join attendances on names.name = attendances.name;

PostgreSQL, JsonB from a column with multiple keys pass it into multiple rows

I have a column in the DB which stores data from an API but it's as the following:
ID
jsonb column
1
{{"code1": "AB", "code2": "BC", "code3": "CD"}, {"code1": "DE", "code2": "EF"}}
2
{{"code1": "AB", "code2": "BC", "code3": "CD"}}
And I want to transform it like this:
ID
Code 1
Code 2
Code 3
1
AB
BC
CD
1
DE
EF
2
AB
BC
CD
OR
ID
Code 1
Code 2
Code 3
1
AB~DE
BC~EF
CD
2
AB
BC
CD
Thanks for you help!
I need help on building a SQL code for above queries
Assuming those values are in reality JSON arrays (e.g. [{"code1": "AB", "code2": "BC", "code3": "CD"}]), you can use jsonb_to_recordset()
select t.id,
r.*
from the_table t
cross join jsonb_to_recordset(t.data) as r("code1" text, "code2" text, "code3" text)
order by t.id

How to join tables by overlapping jsonb values? (PostgreSQL)

To clarify the question, here's an example:
Table venues
id match_ids (jsonb)
---------------------
1 []
2 [112]
3 ["25", 112]
4 [25, 112]
5 ["112"]
6 ["999"]
Table sports
id object (jsonb)
--------------------
1 {"match_ids": [25, 112]}
2 {}
3
To join venues and sports so that the joined table has at least one element in common, how does the SQL statement have to look like?
Here's the expected outcome:
sports.id venue.id venue.match_ids
-------------------------------
1 2 [112]
1 3 ["25", 112]
1 4 [25, 112]
1 5 ["112"]
I wrote the following...
select * from venues
join sports on venues.match_ids <# sports.object::jsonb->'match_ids';
... but this resulted in
ERROR: operator does not exist: boolean -> unknown
LINE 4: sports.object::jsonb->'match_ids';
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
(Here's a fiddle: https://dbfiddle.uk/?rdbms=postgres_13&fiddle=d5263069f490828288c30b6a51a181c8)
Any advice will be appreciated!
PS: I use PostgreSQL v13.
I managed to solve the issue by moving from jsonb to array and working with array intersect &&
select * from venues
join sports on ARRAY(SELECT jsonb_array_elements(match_ids)::varchar)
&&
ARRAY(SELECT jsonb_array_elements(sports.object::jsonb->'match_ids')::varchar);
https://dbfiddle.uk/?rdbms=postgres_13&fiddle=5143f236235e4411c729f41a60c69012
EDIT from the original poster
Thank you so much #Ftisiot for this brilliant solution.
However, I found that this approach works if match_ids in venues has integers only. If the column has texts, it may not work. To select both texts and integers, jsonb_array_elements_text should be used.
e.g.
select * from venues
join sports on ARRAY(SELECT jsonb_array_elements_text(match_ids))
&&
ARRAY(SELECT jsonb_array_elements_text(sports.object::jsonb->'match_ids'));
https://dbfiddle.uk/?rdbms=postgres_13&fiddle=64f8fac38ccf73d38834c22e90ba4b2c
Change the precedence with parentheses:
... on venues.match_ids <# (sports.object::jsonb->'match_ids')