Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MySQL: Return all results where all conditions (but no other) are true

Tags:

sql

mysql

There are two tables:

Packages

id     name
1      red
2      blue
3      yellow

Contents

packageid      item           size
1              square         A
1              circle         B
1              triangle       C
2              square         A
2              circle         B
3              square         A

If we query for { item:square, size:A } we only want { packages.id:3 }

If we query for { item:square, size:A } and { item:circle, size:B } we only want { packages.id:2 }

If we query for { item:square, size:A } and { item:circle, size:B } and { item:triangle, size:C } we only want { packages.id:1 }

If there is more than one package that matches exactly, we would like all of them.

This seems to work, but it's not terribly elegant:

SELECT 
p.id,
p.name,
c.item,
c.size

FROM Packages p
LEFT JOIN Contents c ON (
  c.packageid=p.id
)

WHERE (
    ( p.id IN ( SELECT packageid FROM Contents WHERE item='square' AND size='A' ) 
    AND 
    ( p.id IN ( SELECT packageid FROM Contents WHERE item='circle' AND size='B' ) 
)
GROUP BY p.id 
HAVING ( SELECT COUNT(*) FROM Contents WHERE packageid=p.id ) = 2;
like image 372
Ben Coffin Avatar asked Dec 28 '25 21:12

Ben Coffin


1 Answers

With EXISTS, you will get your wanted results

Exist is the fastest way to check if values with the wanted values are there.

an INDEX on poacage_id, item and size will increase the perfomace

SELECT 
    p.id, p.name, c.item, c.size
FROM
    Packages p
        LEFT JOIN
    Contents c ON (c.packageid = p.id)
WHERE

(SELECT 
        1
   FROM
        Contents
    WHERE
        packageid = p.id AND (item = 'square' AND size = 'A'))
AND (SELECT 
        1
   FROM
        Contents
    WHERE
        packageid = p.id AND (item = 'circle' AND size = 'B')) 
  AND  (SELECT 
        1
   FROM
        Contents
    WHERE
        packageid = p.id AND (item = 'triangle' AND size = 'C')) 
  AND (SELECT COUNT(*) FROM Contents WHERE packageid = p.id) = 3;

id name item size
1 red triangle C
1 red circle B
1 red square A
SELECT
p.id, p.name, c.item, c.size
FROM
    Packages p
        LEFT JOIN
    Contents c ON (c.packageid = p.id)
WHERE

(SELECT 
        1
   FROM
        Contents
    WHERE
        packageid = p.id AND (item = 'square' AND size = 'A'))
AND (SELECT 
        1
   FROM
        Contents
    WHERE
        packageid = p.id AND (item = 'circle' AND size = 'B')) 
  AND (SELECT COUNT(*) FROM Contents WHERE packageid = p.id) = 2;

id name item size
2 blue circle B
2 blue square A

fiddle

like image 120
nbk Avatar answered Dec 31 '25 13:12

nbk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!