Home > Software design >  SQL - Find common rows
SQL - Find common rows

Time:11-06

I have a table Wallets:

[id]   [address]

1      ABC
2      ABC
3      DEF
4      GHI
5      JKL

I have a table Cards

[company] [color] [wallet_id]

Facebook   blue     1
Facebook   blue     2
Youtube    red      3
Facebook   blue     3
Orkut      pink     4 
Microsoft  green    5
Facebook   blue     5  

I want to get all the different wallet addresses that have the same cards, so if i pass wallet id 1, it should return:

 [id]   [address]
  3      DEF // Because wallet with id 1 and 3 have same blue Facebook card
  5      JKL // Because wallet with id 1 and 5 have same blue Facebook card

In this case it should not return Wallet with ID 2, even having the same card, because it is the same address (ABC) that we are doing the lookup.

I've tried a bunch of different solutions, but im confused on how to organize the SQL to do this.

I tried going with:

  • First select the wallet we want to lookup
    • SELECT id, address FROM wallets w WHERE w.id = 1
  • Select all the cards of this wallet
    • SELECT company, color FROM cards c WHERE c.wallet_id = w.id
  • Merge these two queries with INNER JOIN
    • SELECT id, address FROM wallets w INNER JOIN cards c ON w.id = c.id WHERE w.id = 1 GROUP BY id

Now i need to merge the result of the query above with other wallets that have the same cards

... Here's where i cant proceed, im confused on how to do this :c

CodePudding user response:

You can join 2 copies of each table like this:

SELECT w2.*
FROM Wallets w1
INNER JOIN Cards c1 ON c1.wallet_id = w1.id
INNER JOIN Cards c2 ON c2.company = c1.company AND c2.color = c1.color
INNER JOIN Wallets w2 ON w2.id = c2.wallet_id AND w2.address <> w1.address  
WHERE w1.id = 1;

See the demo.

CodePudding user response:

Not the most intuitive of solutions but does the following work for you?

Since you only need rows from Wallets we can use a semi-join, first exclude the rows not required from Wallets based on the source Id, then correlate with Company and Color that exist when joined with Wallets for the source Id

A more elegant solution might be possible by using a lateral join, if we knew your specific RDBMS, however a semi-join will likely perform pretty well.

select * 
from wallets w
where not exists (
  select * from wallets w2 
  where w2.address = w.address and w2.id = 1
) and exists (
    select * from cards c
    where c.Wallet_id = w.id and exists (
      select * 
      from cards c2 
      join wallets w2 on w2.id = c2.Wallet_id 
      where w2.id = 1 
        and c.company = c2.company 
        and c.color = c2.color
    )
);
  • Related