Question

I need to build a query in Rails that returns people that have a certain set of attributes. I simplfied my example. So here are the two tables:

Table: people
+----+----------+
| id |   name   |
+----+----------+
|  1 | Person A |
|  2 | Person B |
|  3 | Person C |
+----+----------+

Table: attributes
+----+-----------+--------+--------+
| id | person_id |  name  | value  |
+----+-----------+--------+--------+
|  1 |         1 | age    | 32     |
|  2 |         1 | gender | male   |
|  3 |         2 | age    | 16     |
|  4 |         2 | gender | male   |
|  5 |         3 | gender | female |
+----+-----------+--------+--------+

person_id is a reference to a person in table people.

In my query I want to (for example) ask the following questions:

  1. Get me all male people who are older than 25!

    name = 'gender' AND value = 'male' and name = 'age' AND value > '25' should return 1 record (person_id=1)

  2. Get me all male people or people who are older than 25!

    name = 'gender' AND value = 'female' or name = 'age' AND value > '25' should return 2 records (person_id=1 and 3)

Example 2 is not so hard to do. But I have my problems with example 1. I don't know how to handle the AND here. And not to forget: The WHERE statements are dynamic. Means there can be heaps of them, or just one.

Basically I am looking for the right SQL statement to do this. I already played around a little bit and the best thing I got until now was this:

SELECT people.* 
FROM people 
INNER JOIN attributes ON attributes.person_id = people.id 
WHERE
  attributes.name = 'gender' AND attributes.value = 'male' OR 
  attributes.name = 'age' AND attributes.value > '25' 
GROUP BY people.id 
HAVING count(*) = 2

I don't like this solution because I have to specify the number of matches in the HAVING clause. There must be a more elegant and flexible solution to do this.

Here is a more complex example that doesn't work:

SELECT people.* 
FROM people 
INNER JOIN attributes ON attributes.person_id = people.id 
WHERE
  (attributes.name = 'gender' AND attributes.value = 'male') OR 
  (attributes.name = 'age' AND attributes.value > '25') AND
  (attributes.name = 'bodysize' AND attributes.value > '180')
GROUP BY people.id

Any ideas and help will be appreciated. Thanks!

Was it helpful?

Solution

Consider something like

SELECT people.* 
FROM people
LEFT JOIN attributes AS attr_gender
    ON attr_gender.person_id = people.id 
    AND attr_gender.name = 'gender'
LEFT JOIN attributes AS attr_age
    ON attr_age.person_id = people.id 
    AND attr_age.name = 'age'

combined with:

1)

SELECT ...
WHERE attr_gender.value = 'male'
AND attr_age.value::int > 25

2)

SELECT ...
WHERE attr_gender.value = 'male'
OR attr_age.value::int > 25

Note: casting is necessary - '9' > '25'.

OTHER TIPS

Did you tried this :

(attributes.name = 'gender' AND attributes.value = 'male') OR
(attributes.name = 'age' AND attributes.value > '25')

without the "GROUP BY"/"HAVING" parts ?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top