Reputation: 591
I have the following tables:
Widgets table is a simple product/listing table with an id, user_id, added/updated, etc.
Fields table lists field names for widgets. There can be any number of fields (e.g., height, width, price, year, colour, etc.). Fields table:
Each widget can have any number of fields and values associated with it. The Widgets_Fields table will look like so:
If you simply want to get all of the fields for a widget or group of widgets, it is pretty straight forward:
select widget_id from widgets_fields where widget_id = xxx
But what if you are searching for widgets and want to find only those widgets that have certain values for their fields? Also doable
select widget_id from widgets_fields where field_id = xxx and value = xxx
My problem arises when I want to select widgets based upon multiple fields. For example, all widgets that are either 'red' or 'orange' AND also have a year of 2012 or 2011
select widget_id from widgets_fields where
(field_id = xxx and (value = 'red' or value = 'orange')
and
(field_id = xxy and (value = 2012 or value = 2011)
This produces an impossible where. Alternatively I could do something like this
select widget_id from widgets_fields where
field_id in (xxx, xxy)
and value in ('red', 'orange', 2012, 2011)
but it won't necessarily return what I want as it will be looking for the given values in any of the provided field ids.
So, finally, my question is whether or not there is any way to properly build a query to accomplish this? I'm certain there must be, but I've been working on it for a while and have yet to come up with a solution.
Upvotes: 1
Views: 92
Reputation: 23300
all widgets that are either 'red' or 'orange'
AND also have a year of 2012 or 2011
can be accomplished via subquerying and INNER JOINing the results together
SELECT widget_id FROM
(SELECT widget_id FROM widgets_fields
WHERE field_id = 'color' and value in ('red', 'orange')) ResultSetA
INNER JOIN
(SELECT widget_id FROM widgets_fields
WHERE field_id = 'year' and value in (2011, 2012)) ResultSetB
ON ResultSetA.widget_id = ResultSetB.widget_id
in plain words, this would be "get the id of all red and orange widgets, then get the id of all widgets from 2011 or 2012, then give me the ids which appear in both lists.
Upvotes: 1
Reputation: 106920
Two ways come instantly to mind. One is to use EXSISTS
subqueries:
select * from widgets where
exists (select * from widgets_fields where field_id = xxx and (value = 'red' or value = 'orange') and widget_id=widgets.id)
and exists (select * from widgets_fields where field_id = xxy and (value = 2012 or value = 2011) and widget_id=widgets.id)
Another is to use JOIN
s with subqueries:
select a.widget_id from
(select widget_id from widgets_fields where field_id = xxx and (value = 'red' or value = 'orange')) a
join (select widget_id from widgets_fields where field_id = xxy and (value = 2012 or value = 2011)) b
on (a.widget_id=b.widget_id)
I'm not sure about the performance, and it probably depends on how much data you have and what indexes there are.
Upvotes: 1