Reputation: 179
I have a table symptom_ratings
containing the columns id
, user_id
, review_id
, symptom_id
, rate
, and strain_id
.
Each review can have multiple entries in symptom_ratings
, one per symptom.
I would like to do a search for every strain_id
that has all of the symptom_id
's the user searches for.
That is, given the columns:
review: 2, strain_id: 3, symptom_id: 43
review: 2, strain_id: 3, symptom_id: 23
review: 2, strain_id: 3, symptom_id: 12
review: 6, strain_id: 1, symptom_id: 3
review: 6, strain_id: 2, symptom_id: 12
Searching for the symptom_id
's 43 and 12 should only return results for strain_id
3.
I currently use the following WHERE
condition:
Strain.id IN (SELECT strain_id
FROM symptom_ratings
WHERE symptom_id
IN ($symptoms))
where $symptoms
is a comma-separated list of symptom_id
values.
My problems is that this query currently performs an OR search (i.e. it finds strains that have any of the symptoms), where instead I'd prefer an AND search (i.e. finding strains that have all of the symptoms). How can I achieve that?
Upvotes: 1
Views: 56
Reputation: 50378
One way to do this would be to group the rows by the strain ID, count the number of distinct matching symptoms in each group, and return only those rows where the count equals the total number of symptoms searched for:
SELECT
strain_id,
COUNT(DISTINCT symptom_id) AS matched_symptoms
FROM symptom_ratings
WHERE symptom_id IN (43, 12)
GROUP BY strain_id
HAVING matched_symptoms = 2
One potentially useful feature of this method is that it's trivial to extend it to support both "all of these", "any of these" and "at least n of these" searches just by changing the condition in the HAVING
clause. For the latter cases, you can also sort the results by the number of matching symptoms (e.g. with ORDER BY matched_symptoms DESC
).
Upvotes: 1