3

I have a table that has a column with strings formatted like this: {1,4,5}. They can be any length and I'd like to join an ID table against any value that has its ID in that string.

This is the first table

name     id         count 
apple    {1,3,6}    5
orange   {5,3,1}    3
potato   {8,1,9}    3

This is the second table -

id2     category
1      foo
2      foobar
3      candy
4      candybar
5      oreo
6      pistachio

I'd like a row for every ID listed in the first table that has the category from the second table. I'd like them to look like this -

id2 name     id         count 
1 apple    {1,3,6}    5
1 orange   {5,3,1}    3
1 potato   {8,1,9}    3
3 apple    {1,3,6}    5
3 orange   {5,3,1}    3
8 potato   {8,1,9}    3
9 potato   {8,1,9}    3

This is what I've got so far. Can I have a join filter that says join if the value is included?

select id2, name, id, count
from table2 as t2 
left join table1 as t1 
on t2.id2 %in% t1.id
The Impaler
  • 45,731
  • 9
  • 39
  • 76
tadon11Aaa
  • 400
  • 2
  • 11

3 Answers3

5

1) Unsolicited advice

  • I think it's worth considering if you database design (i.e. the way you cut your tables) is really beneficial to your cause. The way the tables are currently set up, is violating Codd's 1st Normal Form of database design. Consider changing your design to express an n:m relationship between the objects in FirstTable and SecondTable

  • Have names valid in the context of the table. Instead of having id2 in one table and id in another, just name both id. In your queries you can refer to them as firsttable.id and secondtable.id to distinguish them.

2) Actual answer

Yes, it is possible but (as also pointed out by the commentors) depends on the database system you use.

If firststable.id is an array in PostgreSQL, the following query should work:

SELECT
    *
FROM
    first
JOIN
    second
ON
    second.id = ANY(first.ids);
    -- Took the liberty to change the column names   

This SQLFiddle provides a working example.

If firsttable.id is a string then you can cast the string to an array using '{42, 23, 17}'::int[] as described here:

SELECT
    *
FROM
    first
JOIN
    second
ON
    second.id = ANY(first.ids::int[]);

This SQLFiddle gives a working example in case it's a string.

Maximilian C.
  • 967
  • 5
  • 22
0

I didn't see the PostgreSQL when I first started to solve this.

You can try the following, but no guarantees if Postgre does not have all the functions.

SELECT * FROM (
     SELECT 
         Split.a.value('.', 'VARCHAR(100)') AS ID2  
         ,A.Name, A.ID, A.[Count]
     FROM  
     (
         SELECT Name, [Count], ID,  
             CAST ('<M>' + REPLACE(REPLACE(REPLACE(ID,'{',''),'}',''), ',', '</M><M>') + '</M>' AS XML) AS Data  
         FROM [StackOver].[dbo].[SplitKey]
     ) AS A CROSS APPLY Data.nodes ('/M') AS Split(a)
 ) as B  
 Left Join [StackOver].[dbo].[SplitKeyID2] as C
 On B.ID2 = C.ID2
  Where C.Category > ''
 Order By B.ID2, B.name
donPablo
  • 1,937
  • 1
  • 13
  • 18
0

I'm pretty convinced there's a better solution that doesn't involve the GROUP BY and ARRAY_AGG(), but since you are already there, I think this query may help you:

select
  t2.id2,
  t2.category,
  t1.id,
  t1.count
from table1 t1
join table2 t2 on (
     position ('{' || t2.id2 || '}' in t1.id) <> 0
  or position ('{' || t2.id2 || ',' in t1.id) <> 0
  or position (',' || t2.id2 || ',' in t1.id) <> 0
  or position (',' || t2.id2 || '}' in t1.id) <> 0
)
The Impaler
  • 45,731
  • 9
  • 39
  • 76