views:

33

answers:

2

I have a mysql table that stores relationships. Items can be related to another item in one direction, or both items can be related to each other.

I want to return all items related to my primary item - but I also want to check to see if the related item has a 'reverse relationship' to the current item and show this as a boolean

 |--------------|---------------|
 |    SKU       |  related_SKU  |
 |--------------|---------------|
 | 0001         |  0099         |
 | 0002         |  0099         |
 | 0099         |  0001         |
 |--------------|---------------|

If I want to get all relationships for SKU=0001

SELECT related_SKU from relationships where SKU='0001'

returns

 |--------------|
 | related_SKU  |
 |--------------|
 | 0099         |
 |--------------|

but what I want is

 |--------------|---------------|
 | related_SKU  |   reciprocal  |
 |--------------|---------------|
 | 0099         |      1        |
 |--------------|---------------|

or

 SELECT related_SKU from relationships where SKU='0002'

 |--------------|---------------|
 | related_SKU  |   reciprocal  |
 |--------------|---------------|
 | 0099         |      0        |
 |--------------|---------------|

What's the best way to do this?

+5  A: 

You may want to try something like this:

SELECT r1.related_SKU,
       IF(( SELECT COUNT(*) 
            FROM   relationships r2 
            WHERE  r2.SKU = r1.related_SKU AND r2.related_SKU = r1.SKU
       ) > 0, 1, 0) AS reciprocal
FROM   relationships r1
WHERE  r1.SKU = '0001';

Test Case:

CREATE TABLE relationships (SKU int, related_SKU int);

INSERT INTO relationships VALUES (1, 99);
INSERT INTO relationships VALUES (2, 99);
INSERT INTO relationships VALUES (99, 1);

Results with reciprocal:

SELECT r1.related_SKU,
       IF(( SELECT COUNT(*) 
            FROM   relationships r2 
            WHERE  r2.SKU = r1.related_SKU AND r2.related_SKU = r1.SKU
       ) > 0, 1, 0) AS reciprocal
FROM   relationships r1
WHERE  r1.SKU = '0001';

+-------------+------------+
| related_SKU | reciprocal |
+-------------+------------+
|          99 |          1 |
+-------------+------------+
1 row in set (0.00 sec)

Results without reciprocal:

SELECT r1.related_SKU,
       IF(( SELECT COUNT(*) 
            FROM   relationships r2 
            WHERE  r2.SKU = r1.related_SKU AND r2.related_SKU = r1.SKU
       ) > 0, 1, 0) AS reciprocal
FROM   relationships r1
WHERE  r1.SKU = '0002';

+-------------+------------+
| related_SKU | reciprocal |
+-------------+------------+
|          99 |          0 |
+-------------+------------+
1 row in set (0.00 sec)
Daniel Vassallo
Wanted to post it right now but you were faster.
Thanks for your answer - While this would work - it returns the *number* of relationships. Is there an easy way to say '1' if true '0' if false (or is this overly expensive). I can do this in the code just as easily - but my it's my coding style to have sql return exactly the information i'm looking for as opposed to interpreting it with code afterwards. I can see my example doesn't make it obvious that this is what I'm looking for. Thanks
calumbrodie
@calumbrodie: Yes there is a way :) Let me update my answer...
Daniel Vassallo
@Daniel - Sorry to be overly pedantic. I just realised that because SKU is my primary Key, there is no way that I can ever have more than one match for your count condition. Hence it will always return either 1 or 0 (which is what I'm looking for). Still, it would be useful for me to be able to query the database and return a boolean value (as a technique for using in other circumstances) - if you still want to update your answer. I've marked your answer as correct. Thanks for the help.
calumbrodie
@calumbrodie: No problem. It was an interesting challenge :) ... I had just updated the answer using an [`IF()`](http://dev.mysql.com/doc/refman/5.0/en/control-flow-functions.html#function_if) function. If you prefer not to use it, you can always [see the previous revision](http://stackoverflow.com/posts/2956526/revisions).
Daniel Vassallo
+2  A: 
select r.related_sku, recip.sku is not null as reciprocal
from relationships r
  left join relationships recip on (r.related_sku = recip.sku && recip.related_sku = r.sku)
where r.sku = '0001'
Senseful
This is a good answer as well... +1
Daniel Vassallo
@eagle If I could mark two answers as correct I would because this answer is perfect, and more succinct than Daniels. However he did give me the first correct answer. Thanks for taking the time to leave a reply. +1
calumbrodie