开发者

MySQL: Query all records that have a given set of relations to another table

Yeah, kinda dumb title, b开发者_如何学Pythonut I found it hard to describe my problem. I have the following tables:

properties

id | name
1  | color
2  | material

options

id | property_id | name
1  | 1           | yellow
2  | 1           | blue
3  | 2           | wood
4  | 2           | stone

substances

id | name
1  | orange juice
2  | cheese

relations

id | substance_id | option_id
1  | 2            | 1
2  | 2            | 3
3  | 1            | 1

Now, I have a list of options and want to know which substances are related to all those options. (For example, which substances are yellow and made of wood?) Is this possible with one query?

I'm trying to do this in Rails.


(SELECT s.name FROM substances s, relations r, options o
WHERE r.substance_id = s.id and r.option_id = o.id and o.name='yellow')
INTERSECT
(SELECT s.name FROM substances s, relations r, options o
WHERE r.substance_id = s.id and r.option_id = o.id and o.name='wood')

Or

SELECT s.name FROM substances s
WHERE exists(SELECT * from relations r, options o 
      WHERE r.substance_id = s.id and r.option_id = o.id and o.name='yellow')
AND   exists(SELECT * from relations r, options o 
      WHERE r.substance_id = s.id and r.option_id = o.id and o.name='wood')

Having a single options table with the property_id to tell apart different kinds of options is not such a good idea and really makes this much harder. I'd suggest breaking out the different kinds of options into different tables

MATERIALS(id,name)
COLORS(id,name)

and using separate relations for each type of table. In this case, you wouldn't need separate tables for each relation, since it appears that it's a many (substance) to one (color) relation.

SUBSTANCES(id, name, material_id, color_id)

Then your query is much simpler

SELECT s.name FROM substances s, materials m, colors c
WHERE s.color_id = c.id AND m.material_id = m.id
AND m.name = 'wood'
AND c.name = 'yellow'

ActiveRecord should easily be able to handle this last query much more easily than the first two.


Just out of my head, you can try:

SELECT DISTINCT s.name FROM substances s, relation r 
WHERE r.substance_id = s.id 
      AND r.option_id IN ( 1, 3) 


SELECT s.name
FROM   substances AS s
       LEFT JOIN relations AS r1
         ON s.id = r1.substance_id
       INNER JOIN relations AS r2
         ON r1.substance_id = r2.substance_id
            AND r1.option_id < r2.option_id
       LEFT JOIN options AS o1
         ON o1.id = r1.option_id
       LEFT JOIN options AS o2
         ON o2.id = r2.option_id
WHERE  o1.name = 'yellow'
       AND o2.name = 'wood'  

I'm not very knowledgable about optimizing SQL for performance. You may want to benchmark the above versus this (below) and well as the other solutions posted here.

SELECT s.name
FROM   substances AS s
       LEFT JOIN relations AS r1
         ON s.id = r1.substance_id
       INNER JOIN relations AS r2
         ON r1.substance_id = r2.substance_id
            AND r1.option_id < r2.option_id
       LEFT JOIN options AS o1
         ON o1.id = r1.option_id
     AND o1.name = 'wood'
       LEFT JOIN options AS o2
         ON o2.id = r2.option_id
     AND o2.name = 'yellow'
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜