【问题标题】:Query on join table with multiple AND and OR conditions查询具有多个 AND 和 OR 条件的连接表
【发布时间】:2014-04-03 13:31:05
【问题描述】:

我需要在 Rails 中构建一个查询,返回具有特定属性集的人。我简化了我的例子。所以这里有两张表:

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 是对表people 中一个人的引用。

在我的查询中,我想(例如)提出以下问题:

  1. 把所有 25 岁以上的男性都给我!

    name = 'gender' AND value = 'male' 和 name = 'age' AND value > '25' 应该返回 1 条记录 (person_id=1)

  2. 把所有男性或 25 岁以上的人都给我!

    name = 'gender' AND value = 'female' 或 name = 'age' AND value > '25' 应该返回 2 条记录 (person_id=1 and 3)

示例 2 并不难做到。但是我对示例 1 有疑问。我不知道如何处理这里的 AND。不要忘记:WHERE 语句是动态的。意味着可以有很多,也可以只有一个。

基本上,我正在寻找正确的 SQL 语句来执行此操作。我已经玩了一点,到目前为止我得到的最好的东西是:

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

我不喜欢这个解决方案,因为我必须在HAVING 子句中指定匹配数。必须有更优雅、更灵活的解决方案才能做到这一点。

下面是一个更复杂但不起作用的例子:

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

我们将不胜感激任何想法和帮助。谢谢!

【问题讨论】:

    标签: ruby-on-rails postgresql activerecord ruby-on-rails-4 arel


    【解决方案1】:

    考虑类似

    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'
    

    结合:

    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
    

    注意:强制转换是必要的 - '9' > '25'。

    【讨论】:

    • 我真的很喜欢你的解决方案!刚试了一下。奇迹般有效。我会将您的帖子标记为答案。只是想等待其他解决方案,如果有的话。非常感谢你!你拯救了这一天。注意:我使用CAST(value AS integer) > 25 进行了投射。是一样的吗?
    • 同理,而且CAST()符合ansi sql。
    【解决方案2】:

    你试过了吗:

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

    没有“GROUP BY”/“HAVING”部分?

    【讨论】:

    • 是的,试过了。 OR 案例不是问题所在。如果我用AND 替换OR,它就不起作用。我会用一个更复杂的例子来更新我的帖子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多