PostreSQL, string_agg() join through association table - sql

I have 2 tables linked together via 3rd association table:
TABLE NAME: lot
id | description | <other multiple columns> |
1 | descr_string_1 | ... |
2 | descr_string_2 | ... |
TABLE NAME: id_class
id | code | name |
1 | 01 | class_1 |
2 | 02 | class_2 |
3 | 03 | class_3 |
TABLE NAME: association_lot_id_class
lot_id | class_id |
1 | 1 |
1 | 2 |
2 | 3 |
I'm trying to make a new table based on lot containing concatenated data on related classes:
TABLE NAME: new_table_lot
id | description | <other multiple columns> | class_codes | class_names |
1 | descr_string_1 | ... | "01, 02" | "class_1, class_2" |
2 | descr_string_2 | ... | "03" | "class_3" |
I've tried to use string_agg with different (definitely, wrong) variations based on other SOF answers (e.g. PostgreSQL - JOIN on string_agg) but no luck
SELECT alic.id_class_id, alic.lot_id, ic.code
FROM association_lot_id_class alic
JOIN id_class ic
JOIN (
SELECT id_class_id, string_agg(id.code, ',') AS codes
FROM codes
GROUP BY id_class)

Related

PostgreSQL check if value exists in another table

I'm trying to find a solution in PostgreSQL of how I can add to the output of the query extra column with value if id exists in another table or not:
I need several things:
Do a join between two tables
Add a new column into the result output where I check if exists in the third table or not
My tables:
announcement
author
chapter
announcement table
| id | author_id | date | group_id | ... |
author table
| id | name | email | ... |
chapter table
| id | announcement_id | ... |
This is what I have now. I did a left outer join and it works as I expected:
select announcement.id, announcement.date, author.id as publisher_id, author.name as publisher_name
from announcement
left outer join author
on announcement.author_id = author.id
where announcement.group_id = 123 and announcement.date >= '2022-06-01'::date;
with output:
| id | date | publisher_id | publisher_name |
| 1 | 2020-07-01 | 12 | John |
| 2 | 2020-07-04 | 123 | Arthur |
Now I can't find a solution of how to add an extra column with_chapters to the query response, where I will check if announcement.id exists in chapter table under announcement_id column.
For example, chapter table can have such data:
| id | announcement_id |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
So we see that some announcements can appear in chapters several times (so i'm looking for at least 1 match). And some announcements doesn't have chapters at all.
Output finally should be like that:
| id | date | publisher_id | publisher_name | with_chapters |
| 1 | 2020-07-01 | 12 | John | true |
| 2 | 2020-07-04 | 123 | Arthur | false |
Thanks a lot for any help :)
While EXISTS (subquery) is usually used in the WHERE clause, it returns an ordinary Boolean and so can be used in the select list.
SELECT blah1, blah2,
EXISTS (select 1 from chapter where chapter.announcement_id=announcement.id) as with_chapter
FROM ...

How to get values from multiple tables based on Id and create new based on ids

I need some help, I looked for a solution in other posts but I couldn't make those solutions work.
I have a table that contains a bunch of id's...
table1
customerId | adsId | campaignId | contextId | Date | amoutDue
------------------------------------------------------------------------
1234341233 | 4r4w3 | cp123sifs | cbgvss23r23dd | 03-09-2021 | 50
4556666666 | 35533 | cp453f44f | cb2d3d23dd33d | 04-06-2021 | 25
3444468766 | 12345 | cpef4f44r | ccascasca333s | 23-04-2021 | 13
6346522452 | qw324 | cpfe4r333 | c23ddsasssd2f | 12-07-2021 | 71
3463466346 | 23423 | cp1vt5rfr | kjhcbwiytcubp | 25-02-2021 | 41
4534634346 | 534f3 | cp4343ff4 | apisuchha9shh | 14-05-2021 | 36
7743646346 | wer32 | cp4ffwefe | cq98hd98ladse | 12-03-2021 | 53
2424233335 | r4w4r | cpwerewff | q09jucidnionc | 05-06-2021 | 65
In another table I have the adsId and adsName:
table2(adsId/adsName)
adsName | adsId
--------------------
ad-google1 | 4r4w3
ad-fbtests | 35533
ad-bingpro | 12345
ad-maillan | qw324
ad-cp-e312 | 23423
ad-gads2-4 | 534f3
ad-adProsk | wer32
ad-cpcB151 | r4w4r
On the 3rd table I have campaingName and campaingId:
table3(campaingId/campaignName)
campaignName | campaignId
------------------------
cp-adWords | cp123sifs
cp-looki12 | cp453f44f
cp-cvccp53 | cpef4f44r
cp-Pliiltr | cpfe4r333
cp-Yellinf | cp1vt5rfr
cp-Iuliucs | cp4343ff4
cp-Ieventx | cp4ffwefe
cp-Shoofab | cpwerewff
Finally the 4th table I have contextId and contextName:
table4(contextId/contextName)
contextId | contextName
-----------------------------
cbgvss23r23dd | ctx-Okkels
cb2d3d23dd33d | ctx-Yumeis
ccascasca333s | ctx-Niehok
c23ddsasssd2f | ctx-Sluiru
kjhcbwiytcubp | ctx-Strogi
apisuchha9shh | ctx-Slucha
cq98hd98ladse | ctx-Epuent
q09jucidnionc | ctx-Igeegs
What I'm looking for is the 5th table that gets all names from other tables (that contains the id's) and give the names:
table5
customerId | adsId | campaignId | contextId | Date | amoutDue | adsName |campaignName| contextName
---------------------------------------------------------------------------------------------------------------------
1234341233 | 4r4w3 | cp123sifs | cbgvss23r23dd | 03-09-2021 | 50 | ad-google1 | cp-adWords | ctx-Okkels
4556666666 | 35533 | cp453f44f | cb2d3d23dd33d | 04-06-2021 | 25 | ad-fbtests | cp-looki12 | ctx-Yumeis
3444468766 | 12345 | cpef4f44r | ccascasca333s | 23-04-2021 | 13 | ad-bingpro | cp-cvccp53 | ctx-Niehok
6346522452 | qw324 | cpfe4r333 | c23ddsasssd2f | 12-07-2021 | 71 | ad-maillan | cp-Pliiltr | ctx-Sluiru
3463466346 | 23423 | cp1vt5rfr | kjhcbwiytcubp | 25-02-2021 | 41 | ad-cp-e312 | cp-Yellinf | ctx-Strogi
4534634346 | 534f3 | cp4343ff4 | apisuchha9shh | 14-05-2021 | 36 | ad-gads2-4 | cp-Iuliucs | ctx-Slucha
7743646346 | wer32 | cp4ffwefe | cq98hd98ladse | 12-03-2021 | 53 | ad-adProsk | cp-Ieventx | ctx-Epuent
2424233335 | r4w4r | cpwerewff | q09jucidnionc | 05-06-2021 | 65 | ad-cpcB151 | cp-Shoofab | ctx-Igeegs
I tried to use join but I couldn't select all tables... My SQL in words would be:
get campaingName from table 2 and create a row with the name where campaingId from table1 = to campaingId from table 2 the same for all fields...
Thanks in advance!
You would use join like this
select *
from table1 t1 join
table2 t2
using (adsid) join
table3 t3
using (campaignid) join
table4 t4
using (contextid);
The above works because the join keys have the same names in the reference table and the original table (a best practice). You can also use the more verbose:
select t1.*, t2.adsName, t3.campaignName, t4.contextName
from table1 t1 join
table2 t2
on t2.adsid = t1.adsid join
table3 t3
on t3.campaignid = t1.campaignid join
table4 t4
on t4.contextid = t1.contextid;
If some of the ids are missing, then use left join instead of join.
you can use multiple with clause within big query to achieve this.
WITH TEST1 AS
(select a1.*,a2.adsName
from `<project_name.<dataset_name>.table1` a1, `<project_name.<dataset_name>.table2` a2
where a1.adsId=a2.adsId),
TEST2 as
(
select TEST1.*,a3.campaignName
from TEST1, `<project_name.<dataset_name>.table3` a3
WHERE TEST1.campaignId=a3.campaignId)
select TEST2.*, a4.contextName
from TEST2,`<project_name.<dataset_name>.table4` a4
WHERE TEST2.contextId=a4.contextId

Select from a concatenation of two columns after a left join

Problem description
Let the tables C and V have those values
>> Table V <<
| UnID | BillID | ProductDesc | Value | ... |
| 1 | 1 | 'Orange Juice' | 3.05 | ... |
| 1 | 1 | 'Apple Juice' | 3.05 | ... |
| 1 | 2 | 'Pizza' | 12.05 | ... |
| 1 | 2 | 'Chocolates' | 9.98 | ... |
| 1 | 2 | 'Honey' | 15.98 | ... |
| 1 | 3 | 'Bread' | 3.98 | ... |
| 2 | 1 | 'Yogurt' | 8.55 | ... |
| 2 | 1 | 'Ice Cream' | 7.05 | ... |
| 2 | 1 | 'Beer' | 9.98 | ... |
| 2 | 2 | 'League of Legends RP' | 40.00 | ... |
>> Table C <<
| UnID | BillID | ClientName | ... |
| 1 | 1 | 'Alexander' | ... |
| 1 | 2 | 'Tom' | ... |
| 1 | 3 | 'Julia' | ... |
| 2 | 1 | 'Tom' | ... |
| 2 | 2 | 'Alexander' | ... |
Table C have the values of each product, which is associated with a bill number. Table V has the relationship between the client name and the bill number. However, the bill number has a counter that is dependent on the UnId, which is the store unity ID. That being said, each store has it`s own Bill number 1, number 2, etc. Also, the number of bills from each store are not equal.
Solution description
I'm trying to make select between the C left join V without sucess. Because each BillID is dependent on the UnID, I have to make the join considering the concatenation between those two columns.
I've used this script, but it gives me an error.
SELECT
SUM(C.Value),
V.ClientName
FROM
C
LEFT JOIN
V
ON
CONCAT(C.UnID, C.BillID) = CONCAT(V.UnID, V.BillID)
GROUP BY
V.ClientName
and SQL server returns me this 'CONCAT' is not a recognized built-in function name.
I'm using Microsoft SQL Server 2008 R2
Is the use of CONCAT wrong? Or is it the way I tried to SELECT? Could you give me a hand?
[OBS: The tables I've present you are just for the purpose of explaining my difficulties. That being said, if you find any errors in the explanation, please let me know to correct them.]
You should be joining on the equality of the UnID and BillID columns in the two tables:
SELECT
c.ClientName,
COALESCE(SUM(v.Value), 0) AS total
FROM C c
LEFT JOIN V v
ON c.UnID = v.UnID AND
c.BillID = v.BillID
GROUP BY
c.ClientName;
In theory you could try joining on CONCAT(UnID, BillID). However, you could run into problems. For example, UnID = 1 with BillID = 23 would, concatenated together, be the same as UnID = 12 and BillID = 3.
Note: We wrap the sum with COALESCE, because should a given client have no entries in the V table, the sum would return NULL, which we then replace with zero.
concat is only available in sql server 2012.
Here's one option.
SELECT
SUM(C.Value),
V.ClientName
FROM
C
LEFT JOIN
V
ON
cast(C.UnID as varchar(100)) + cast(C.BillID as varchar(100)) = cast(V.UnID as varchar(100)) + cast(V.BillID as varchar(100))
GROUP BY
V.ClientName

Select all rows where rows in another joined table match condition

So I want to select all rows where a subset of rows in another table match the given values.
I have following tables:
Main Profile:
+----+--------+---------------+---------+
| id | name | subprofile_id | version |
+----+--------+---------------+---------+
| 1 | Main 1 | 4 | 1 |
| 2 | Main 1 | 5 | 2 |
| 3 | Main 2 | ... | 1 |
+----+--------+---------------+---------+
Sub Profile:
+---------------+----------+
| subprofile_id | block_id |
+---------------+----------+
| 4 | 6 |
| 4 | 7 |
| 5 | 8 |
| 5 | 9 |
+---------------+----------+
Block:
+----------+-------------+
| block_id | property_id |
+----------+-------------+
| 7 | 10 |
| 7 | 11 |
| 7 | 12 |
| 7 | 13 |
| 8 | 14 |
| 8 | 15 |
| 8 | 16 |
| 8 | 17 |
| ... | ... |
+----------+-------------+
Property:
+----+--------------------+--------------------------+
| id | name | value |
+----+--------------------+--------------------------+
| 10 | Description | XY |
| 11 | Responsible person | Mr. Smith |
| 12 | ... | ... |
| 13 | ... | ... |
| 14 | Description | XY |
| 15 | Responsible person | Mrs. Brown |
| 16 | ... | ... |
| 17 | ... | ... |
+----+--------------------+--------------------------+
The user can define multiple conditions on the property table. For example:
Description = 'XY'
Responsible person = 'Mr. Smith'
I need all 'Main Profiles' with the highest version which have ALL matching properties and can have more of course which do not match.
It should be doable in JPA because i would translate it into QueryDSL to build typesafe, dynamic queries with the users input.
I already searched trough all questions regarding similar problems but couldn't project the answer onto my problem.
Also, I've already tried to write a query which worked quite good but retrieved all rows with at least one matching condition. Therefore i need all properties in my set but it only fetched (fetch join, which is missing in my code examplte) the matching ones.
from MainProfile as mainProfile
left join mainProfile.subProfile as subProfile
left join subProfile.blocks as block
left join block.properties as property
where mainProfile.version = (select max(mainProfile2.version)from MainProfile as mainProfile2 where mainProfile2.name = mainProfile.name) and ((property.name = 'Description' and property.value = 'XY') or (property.name = 'Responsible person' and property.value = 'Mr. Smith'))
Running my query i got two rows:
Main 1 with version 2
Main 2 with version 1
I would have expected to get only one row due to mismatch of 'responsible person' in 'Main 2'
EDIT 1:
So I found a solution which works but could be improved:
select distinct mainProfile
from MainProfile as mainProfile
left join mainProfile.subProfile as subProfile
left join subProfile.blocks as block
left join block.properties as property
where mainProfile.version = (select max(mainProfile2.version)from MainProfile mainProfile2 where mainProfile2.name = mainProfile.name)
and ((property.name = 'Description' and property.content = 'XY') or (property.name = 'Responsible person' and property.content = 'Mr. Smith'))
group by mainProfile.id
having count (distinct property) = 2
It actually retrieves the right 'Main Profiles'. But the problem is, that only the two found properties are getting fetched. I need all properties though because of further processing.

SQL Query to Work out Every Product Combination

I require a SQL query to work out every product combination.
I have three product categories (game, accessory, upgrade) and products assigned to each of these three categories:
+----+------------+-----------+------------+
| id | category | product | prod_code |
+----+------------+-----------+------------+
| 1 | game | GTA | 100 |
| 2 | game | GTA1 | 200 |
| 3 | game | GTA2 | 300 |
| 4 | accessory | Play Pad | 400 |
| 5 | accessory | Xbox Pad | 500 |
| 6 | upgrade | Memory | 600 |
| 6 | upgrade | drive | 700 |
+----+------------+-----------+------------+
I want to take one product from each of the categories and work out every single combination:
+----+--------------+
| id | combinations |
+----+--------------+
| 1 | 100,400,600 |
| 2 | 100,500,600 |
| 3 | 100,400,700 |
| 4 | 100,500,700 |
| ? | etc |
+----+--------------+
How would I go about doing this?
Thanks in advance, Stuart
Use a CROSS JOIN:
SELECT CONCAT(t1.[prod_code], ',',
t2.[prod_code], ',',
t3.[prod_code])
FROM (
SELECT [prod_code]
FROM mytable
WHERE category = 'game') AS t1
CROSS JOIN (
SELECT [prod_code]
FROM mytable
WHERE category = 'accessory') AS t2
CROSS JOIN (
SELECT [prod_code]
FROM mytable
WHERE category = 'upgrade') AS t3
ORDER BY t1.[prod_code], t2.[prod_code], t3.[prod_code]
CROSS JOIN of derived tables, one for each category, produces the following cartesian product: 'game' products x 'accessory' products x 'upgrade' products
Demo here