I have a table in mySQL to store product attributes like this:
------------------------------------
| product_id | att_val1 | att_val2 |
------------------------------------
| 1001001 | x202 | 2200 |
| 1001002 | t100 | 180000 |
| 1001003 | T100 | 1080 |
| 1001001 | T100 | 700 |
| 1001005 | SS120 | 1080 |
| 1001001 | SS120 | 1080 |
| 1001004 | SS120 | 1080 |
I want to get all product_id
s that have all the asked properties, I came up with this query
SELECT product_id FROM product_attributes
WHERE product_id EXISTS (SELECT product_id from product_attributes WHERE att_val1 = 'x202' AND att_val2 = '2200')
AND product_id EXISTS (SELECT product_id from product_attributes WHERE att_val1 = 'SS120' AND att_val2 = '1080')
Is there a better way to achieve this result?
UPDATED (better sample)
I'm going to change the sample data to string values for better clarification
-------------------------------------------
| product_id | attribute | value |
-------------------------------------------
| iPhone 6s | Screen Size | 4.7 |
| iPhone 7 | Screen Size | 4.7 |
| iPhone 8 | Screen Size | 4.7 |
| iPhone 6s | Storage | 64GB |
| iPhone 7 | Storage | 128GB |
| iPhone 8 | Storage | 64GB |
| iPhone 6s | NFC | FALSE |
| iPhone 7 | NFC | FALSE |
| iPhone 8 | NFC | TRUE |
I want to get the iPhone 8 which has Screen Size = 4.7
AND Storage = 64GB
AND NFC = TRUE
. There could be more than 3 parameters to filter.
SELECT product_id FROM product_attributes
WHERE product_id IN (SELECT product_id from product_attributes
WHERE attribute = 'Screen Size' AND value = '4.7')
AND product_id IN (SELECT product_id from product_attributes
WHERE attribute = Storage AND value = '64GB')
AND product_id IN (SELECT product_id from product_attributes
WHERE attribute = NFC AND value = 'TRUE')
GROUP BY product_id
If I use OR in WHERE statement I will end up having iPhone 6s, 7 and 8.
SELECT product_id FROM product_attributes
WHERE product_id IN (SELECT product_id from product_attributes
WHERE (attribute = 'Screen Size' AND value = '4.7')
OR (attribute = Storage AND value = '64GB')
OR (attribute = NFC AND value = 'TRUE')
So I think I've to repeat the AND product_id IN ...
part for as many filters as I have.
I hope I'm wrong! and there is a better way to achieve this result.
Best Answer
Your example has syntax errors. Were you hoping to get just
1001001
from that sample data?One way:
The best way:
Both ways will benefit from
INDEX(att_val1, att_val2, product_id)
.The schema is called EAV (Entity-Attribute-Value) or Key-Value. It is a nuisance for writing queries and for performance. More discussion: http://mysql.rjweb.org/doc.php/eav