简体   繁体   中英

MySQL several inner joins vs subquery

i have a problem with making a complicated query easier without the need to build the query with PHP.

My problem:

A product can have several properties eg. a color, a size and a state. To get a product which has all 3 properties i can:

products p

INNER JOIN product_propeties p1 on p.pid = p1.pid AND p1.property = 1 (color)

INNER JOIN product_propeties p2 on p.pid = p2.pid AND p2.property = 2 (size)

INNER JOIN product_propeties p3 on p.pid = p3.pid AND p3.property = 3 (state)

This works fine. I will get all products which have all this 3 properties.

My problem is now that i dont want to generate p1,p2,p3 with PHP. The properties are listed in a table "property_groups". In this table i can group properties.

proberty|title|group_name

1|color|winterspecial

2|size|winterspecial

3|state|winterspecial

I want to join the "property_groups" table with "winterspecial" and my example from above i dont know how. Problem is that each property needs to exists. Several single joins do the job. But how to do it in a single MySQL Query.

With PHP i select all "winterspecial" and then i build the query with p1,p2...

There must be a better way. Beware that the properties must be AND connectet. OR is easy this would be a simple subselect.

INNER JOIN product_propeties p1 on p.pid = p1.pid AND product_propeties IN (
    SELECT * FROM property_groups WHERE "winterspecial"
)

This may look a bit clumsy, but is the only thing I can come up with at the moment...

In order to know whether a product has all winterspecial properties, we could count all existing winterspecial properties and the product's winterspecial properties and then compare the two numbers.

Then we can select from products and product_properties where the product ID is in the found set:

select ...
from products p
join product_properties pp on pp.pid = p.pid and pp.property in
  (select property from property_groups where group_name = 'winterspecial')
where p.pid in
(
  select pid
  from product_properties
  where property in 
    (select property from property_groups where group_name = 'winterspecial')
  group by pid
  having count(*) =
    (select count(*) from property_groups where group_name = 'winterspecial')
);

Ahhh, I think I get the problem now. You seem to want all products that have the properties in the property_groups table, for a given group.

Here is an approach. Do a cross join to generate the list of products and properties. Then do a left join to match to product_properties . With an aggregation, you can easily tell if all the desired properties match an existing property:

select p.*
from products p cross join
     property_groups g left join
     product_properties pp 
     on pp.pid = p.id and pp.property = g.property
where g.group_name = 'winterspecial'
group by p.id
having count(distinct pp.property) = count(distinct g.property)

You can actually simply the having clause to one of these:

having sum(pp.property is null) = 0           -- no null values
having count(pp.property) = count(g.property) -- all match

These should all be equivalent.

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