简体   繁体   中英

MySQL query filter only rows containing certain value, after join

I'm trying to get something done, basically it comes down to this: I want to retrieve all products and show all categories this product is in. But then, I want to filter out only the products which exists in categories x and y.

So, this is my query:

    SELECT p.id, p.name,GROUP_CONCAT(distinct(pc.category_id) SEPARATOR ", ") as category
    FROM products p
    LEFT JOIN product_category pc ON p.id = pc.productid
    GROUP BY p.id;

This works great, I get result like this:

p.id | p.name | category
10   | example| 15,16,17
11   | example| 15,20
12   | example| 39,40

Obviously the '15,16,16' are the categories the product is in. However, now I want to filter the resultset on products only containing category 15 or 16. So the resultset I want to get is:

p.id | p.name | category
10   | example| 15,16,17
11   | example| 15,20

So, what I tried is adding a WHERE to my MySQL statement like this:

WHERE category IN (15,16)

This works as for the filtering, but the problem is, in the resultset I don`t see which other categories the product is also in. So the result I see is:

p.id | p.name | category
10   | example| 15,16
11   | example| 15

Note the difference with the desired result is I just see the filtered cats and not all the cats.

I do get why this is behaving as it is, since obviously the 'category' column in my resultset is based on the values after filtering. However, I don`t know how to work around this or if what I want is even possible.

PS: this query will run on huge databases so the faster the query, the better.

Try this untested query:

select * p.id, p.name,GROUP_CONCAT(distinct(pc.category_id) SEPARATOR ", ") as category 
from FROM products p
LEFT JOIN product_category pc ON p.id = pc.productid
where p.id in (
   SELECT pc.productid
   product_category pc ON p.id = pc.productid
   where  category IN (15,16)
   GROUP BY  pc.productid
)
GROUP BY  p.id

One (admittedly weird) option is to use find_in_set on the aggregate result:

SELECT    p.id, 
          p.name, 
          GROUP_CONCAT(DISTINCT(pc.category_id) SEPARATOR ", ") AS category
FROM      products p
LEFT JOIN product_category pc ON p.id = pc.productid
GROUP BY  p.id
HAVING    FIND_IN_SET('15', GROUP_CONCAT (pc.category_id)) > 0 OR 
          FIND_IN_SET('16', GROUP_CONCAT (pc.category_id)) > 0

I was just wondering how would I resolved this task:

SET @needle = '15,16';

SELECT p.id, p.name, GROUP_CONCAT(pc.category_id SEPARATOR ', ') AS 'category' FROM products p 
    LEFT JOIN product_category pc ON p.id = pc.productid GROUP BY id
HAVING REGEXP_INSTR(GROUP_CONCAT(pc.category_id), CONCAT('([[:<:]])(',REPLACE(@needle,',','|'),')([[:>:]])'));

The groups before passing the HAVING clause:

p.id | p.name | category
10   | example| 15,16,17
11   | example| 15,20
12   | example| 39,40

Then we check the groups with the HAVING clause and REGEXP:

HAVING REGEXP_INSTR(t3.category, CONCAT('[[:<:]](15|16)[[:>:]]'))
/* REGEX: START_WORD_BOUNDARY(15 OR 16)END_WORD_BOUNDARY */

And here it is:

p.id | p.name | category
10   | example| 15,16,17
11   | example| 15,20

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM