We have a product table in MySQL where each row has a field with a comma separated value of options wich combine to become this product. In the scheme below you can see both tables. The field with comma separated values is option_ids, this can for example look like 2,5,6,23 or 4,2,76. These numbers are options wich exist in the option table.
Product:|product_id|name|description|option_ids|price|
Option:|option_id|type_id|label|group|pos|count|price|
The input we get is an array of unique options. We wanna find every product that has this set of options or a subset of it in option_ids.
The way we do this now is by generating a powerset of the array of options so that we have all possible combinations of the array of options. Then we generate a mysql query wich queries for all these combinations in 'OR WHERE' statements.
The problem here is that the powerset grows exponentially, so when we have an array of 16 unique options the powerset gives an array of 65536 possibilities. We had to increase the memory usage of php for this to work properly but now we run into MySQL problems because the query gets so enormous.
Is there another way to tackle this problem?
I hope I've given enough information, if not please do ask!
EDIT: A third table will get us the same problem because we need to find subsets to... It's not said that an array of options IS ONE product, it can be one product but it can also be 2 or 3 products. For example: options 1,2 make product A, options 2,3 make product B, and options 4,5,6 make product C. If we have option array [1,2,4,5,6] we would want to find product A and C.
Use a third table:
Product_Option_Assoc ( product_id, option_id )
For every option that a product should have, insert a row into that table. This gives you a real One-to-many relationship between products and options. The number of options can easily vary and it's easy to query for pulling data and updating (no comma parsing required).
Example query: get products that have given options associated:
$options = array(1, 55, 23); // hard coded but could come from a form (remember to validate/cast as ints)
$optionCommaList = implode(',', $options);
SELECT
p.name
FROM
Product_Option_Assoc a
LEFT JOIN
Products p ON p.product_id = a.product_id
WHERE
a.option_id IN ($optionCommaList);
Would using FIND_IN_SET(str,strlist) do the trick?
I'm not sure about the efficiency of it but would give you an easy way for searching for products with specific options.
By turning the option list into a single string, you're losing all the power of databases and forcing yourselves to use string matching and other kludges. Use a link table. It's The Right Way. Then figure out how to write queries for it.
Product_option ( product_id, option_id )
This is what you say you want:
example: options 1,2 make product A, options 2,3 make product B, and options 4,5,6 make product C. If we have option array [1,2,4,5,6] we would want to find product A and C.
To rephrase, you want to retrieve all products that do not have an option that's not in your query array. Here's how to do it:
SELECT * from Product WHERE NOT EXISTS (
SELECT product_id id, option_id opt from Product_option
WHERE id = Product.product_id AND opt NOT IN (1, 2, 4, 5, 6)
)
You could do this explicitly with a join, but an EXISTS
query gets the idea across more clearly in my opinion.