1

I want to retrieve multiple rows using same id's. Therefore having this table "component_property", I would like to have as results 2 records, id's: 8 and 9 according to my SQL query (check below), but of course and retrieving nothing since I'm checking if cp.property_id = 9102 and later and checking if cp.property_id = 8801 which at the same time is impossible.

ID;type_id;name;desc;property_id,value
--------------------------------------
8;3832;"amplifier1";"";8801;"3"
8;3832;"amplifier1";"";9102;"4015"
9;3832;"amplifier2";"";8801;"3"
9;3832;"amplifier2";"";9102;"4016"

This is the query I have at this moment which retrieves nothing.

SELECT c.id, c.type_id, cp.property_id, cp.value 
FROM components_component AS c 
INNER JOIN components_componentproperty AS cp 
ON c.id = cp.component_id 
WHERE 
(cp.property_id = 9102  AND cp.value IN ('4015', '4016')) 
OR
(cp.property_id = 8801  AND cp.value = '3') 
AND c.type_id = 3832

component ===> component_property <=== property

component

id serial NOT NULL,
type_id integer NOT NULL,
name character varying(50) NOT NULL,
description character varying(255),

component_property

id serial NOT NULL,
component_id integer NOT NULL,
property_id integer NOT NULL,
value character varying(255),

property

id serial NOT NULL,
code character varying(10),
preferred_name character varying(50),

My expected result would be:

id;name
-------
8;amplifier1
9;amplifier2
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Ruben
  • 1,065
  • 5
  • 18
  • 44
  • 1
    show your expected output – Vivek S. Mar 18 '15 at 08:49
  • 2
    show also structure / sample data extract from `components_component` - i see also that you have `c.type_id = 3832` maybe you meant `cp.type_id ...` – Ian Kenney Mar 18 '15 at 08:54
  • @unique_id uhmmm, I get 4 records, I should get just 2 records which represent the 2 components which match with the values of the properties. – Ruben Mar 18 '15 at 09:01

1 Answers1

1

This is a case of relational division:

SELECT c.id, c.name
FROM   components_componentproperty cp1
JOIN   components_componentproperty cp2 USING (component_id)
JOIN   components_component         c   ON c.id = cp1.component_id
WHERE  cp1.property_id = 9102  AND cp1.value IN ('4015', '4016')
AND    cp2.property_id = 8801  AND cp2.value = '3'
AND    c.type_id = 3832
GROUP  BY c.id;

We have assembled an arsenal of relevant techniques here:

Check for a large number of properties

You can expand the above query and for a hand full of properties it will be among the fastest possible solutions. For a bigger number it will be more convenient (and also starting to be faster) to go this route:

Example for 5 properties, expand as needed:

SELECT c.id, c.name
FROM  (
   SELECT id
   FROM  (
      SELECT component_id AS id, property_id  -- alias id just to shorten syntax
      FROM   components_componentproperty
      WHERE  property_id IN (9102, 8801, 1234, 5678, 9876)  -- expand as needed
      GROUP  BY 1,2
      ) cp1
   GROUP  BY 1
   HAVING count(*) = 5  -- match IN expression
   ) cp2
JOIN   components_component c USING (id);

The extra step of the inner subquery cp1 is only necessary, because you obviously have multiple entries per (component_id, property_id) in components_componentproperty. We could fold cp1 and cp2 into one and check

HAVING count(DISTINCT property_id) = 5

But I expect that to be more expensive, since count(DISTINCT col) needs one sort operation per row.

For very long lists IN is a bad choice. Consider:

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • what about if there are more properties to compare. Properties are dynamic so, I can compare 2,3 or 10. – Ruben Mar 18 '15 at 09:27
  • 1
    @Ruben: I added a solution for that. – Erwin Brandstetter Mar 18 '15 at 09:50
  • It did the job: SELECT c.id, c.name FROM components_component AS C WHERE c.id IN (SELECT component_id FROM components_componentproperty WHERE property_id = 9102 AND value IN ('4015', '4016')) AND c.id IN (SELECT component_id FROM components_componentproperty WHERE property_id = 8801 AND value IN ('3')) – Ruben Mar 18 '15 at 09:50
  • 1
    @Ruben: Yes, should work, too. Test performance with `EXPLAIN ANALYZE`. Joins are typically faster than `IN`. For many duplicate matches `EXISTS` is typically faster. In short: `IN` is only competitive for small sets. – Erwin Brandstetter Mar 18 '15 at 09:58