Question

I have the following MySQL Query. This query is used on my shop to retrieve products after a customer has used the filtering to narrow down results.

I'd like to modify this query so only products are returned that meet ALL the filtering criteria, right now it returns any product that meets at least one of the selected criteria.

The line responsible for this is towards the end of the query: AND pf.filter_id IN (7,3,40)

Is it possible to modify this query to achieve my desired behavior?

What I have tried so far (that didn't work):

I changed

AND pf.filter_id IN (7,3,40)

to

AND pf.filter_id = 7 AND AND pf.filter_id = 3 AND AND pf.filter_id = 40

SELECT p.product_id,

  (SELECT AVG(rating) AS total
   FROM review r1
   WHERE r1.product_id = p.product_id
     AND r1.status = '1'
   GROUP BY r1.product_id) AS rating,

  (SELECT price
   FROM product_discount pd2
   WHERE pd2.product_id = p.product_id
     AND pd2.customer_group_id = '1'
     AND pd2.quantity = '1'
     AND ((pd2.date_start = '0000-00-00'
           OR pd2.date_start < NOW())
          AND (pd2.date_end = '0000-00-00'
               OR pd2.date_end > NOW()))
   ORDER BY pd2.priority ASC, pd2.price ASC LIMIT 1) AS discount,

  (SELECT price
   FROM product_special ps
   WHERE ps.product_id = p.product_id
     AND ps.customer_group_id = '1'
     AND ((ps.date_start = '0000-00-00'
           OR ps.date_start < NOW())
          AND (ps.date_end = '0000-00-00'
               OR ps.date_end > NOW()))
   ORDER BY ps.priority ASC, ps.price ASC LIMIT 1) AS special
FROM product_to_category p2c
LEFT JOIN product_filter pf ON (p2c.product_id = pf.product_id)
LEFT JOIN product p ON (pf.product_id = p.product_id)
LEFT JOIN product_description pd ON (p.product_id = pd.product_id)
LEFT JOIN product_to_store p2s ON (p.product_id = p2s.product_id)
WHERE pd.language_id = '1'
  AND p.status = '1'
  AND p.date_available <= NOW()
  AND p2s.store_id = '0'
  AND p2c.category_id = '124'
  AND pf.filter_id IN (7,3,40)
GROUP BY p.product_id
ORDER BY p.sort_order ASC,
         LCASE(pd.name) ASC LIMIT 0,1

Sample product in product_filter table that has multiple filters:

product_id filter_id
753        6
753        7
753        44
753        47

Result of SELECT filter_id, count(*) FROM product_filter GROUP BY filter_id (sample)

filter_id count(*)
1         146
2         644
3         421
4         171
5         90
6         46
7         80
8         82
Était-ce utile?

La solution

If you need to perform AND operation for the values in your IN() clause here is the trick

SELECT
.... your select expression here 
FROM product_to_category p2c
LEFT JOIN product_filter pf ON (p2c.product_id = pf.product_id)
LEFT JOIN product p ON (pf.product_id = p.product_id)
LEFT JOIN product_description pd ON (p.product_id = pd.product_id)
LEFT JOIN product_to_store p2s ON (p.product_id = p2s.product_id)
WHERE pd.language_id = '1'
  AND p.status = '1'
  AND p.date_available <= NOW()
  AND p2s.store_id = '0'
  AND p2c.category_id = '124'
  AND pf.filter_id IN (7,3,40)
GROUP BY p.product_id
HAVING COUNT(DISTINCT pf.filter_id) >=3
ORDER BY p.sort_order ASC,
         LCASE(pd.name) ASC LIMIT 0,1

Using HAVING COUNT(DISTINCT pf.filter_id) >=3 will satisfy the condition of having filters 7,3,40 and even more filter but with these 3 filters,if you the results with only with 3 three filter not even others with them then you can change your condition to HAVING COUNT(DISTINCT pf.filter_id) =3

Autres conseils

This is an example of what some people call "sets within sets query". You need to do

SELECT product_id, count(*) 
FROM yourResults 
GROUP BY product_id
HAVING count(*) = 3

Where yourResults is an alias for the resulting table of the query you posted (with filter_id IN)

This way, you'll get all the Ids of product who have one row where filter_id is 3, one row where it is 7, and one row where it is 40

The simplest way to do what you're asking is to not use IN at all, but rather use multiple inner joins:

JOIN product_filter pf1 ON pf1.product_id = p2c.productid AND pf1.filter_id = 7
JOIN product_filter pf2 ON pf2.product_id = p2c.productid AND pf2.filter_id = 3
JOIN product_filter pf3 ON pf3.product_id = p2c.productid AND pf3.filter_id = 40

This method ensures that only sets satisfying each of those predicates will be returned.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top