PostgreSQL查询返回多行而不是一行

时间:2012-12-12 05:34:20

标签: sql postgresql greatest-n-per-group operator-precedence rails-postgresql

我有两张表:userprojects,两者之间有一对多关系。
projects表的字段status包含用户的项目状态。

status可以是以下之一:

launched, confirm, staffed, overdue, complete, failed, ended

我想将用户分为两类:

  1. 拥有launched阶段
  2. 项目的用户
  3. 拥有launched状态以外的项目的用户。
  4. 我使用以下查询:

    SELECT DISTINCT(u.*), CASE 
        WHEN p.status = 'LAUNCHED' THEN 1
        ELSE 2
        END as user_category
    FROM users u
    LEFT JOIN projects p ON p.user_id = u.id
    WHERE (LOWER(u.username) like '%%%'
        OR LOWER(u.personal_intro) like '%%%'
        OR LOWER(u.location) like '%%%'
        OR u.account_status != 'DELETED'
    AND system_role=10 AND u.account_status ='ACTIVE')
    ORDER BY set_order, u.page_hits DESC
    LIMIT 10
    OFFSET 0
    

    我正面临以下情景的重复记录

    如果用户的项目状态为launched以及overduecompletefailed,那么该用户将被记录两次{{1这个用户满意。

    请建议一个查询,其中具有CASE状态的任何项目的用户将其launched设置为user_category1不应重复使用同一个用户。

2 个答案:

答案 0 :(得分:3)

由于多种原因,查询可能没有按照您的想法进行操作

  • DISTINCTDISCTINCT ON (col1, col2)
    DISTINCT (u.*)DISTINCT u.*没有区别。括号只是噪音。

  • AND根据operator precedenceOR之前绑定。我怀疑你想在条件OR一起使用括号吗?或者你是否需要它的方式?但是在任何情况下,你都不需要围绕整个WHERE子句使用括号。

  • 您的表达式LOWER(u.username) LIKE '%%%'没有任何意义。每个非空字符串都符合条件。可以用u.username IS NOT NULL替换。我怀疑你想要不同的东西?

  • PostgreSQL在字符串处理中区分大小写。您写的是status被'启动'等,但在查询中使用'已启动'。它会是什么?

  • 问题中缺少一些表格资格,使读者感到含糊不清。我用锯子填写。

所有东西放在一起可能就像这样工作:

SELECT DISTINCT ON (u.set_order, u.page_hits, u.id)
       u.*
      ,CASE WHEN p.status = 'LAUNCHED' THEN 1 ELSE 2 END AS user_category
FROM   users         u
LEFT   JOIN projects p ON p.user_id = u.id
WHERE  LOWER(u.username)       LIKE '%%%' -- ???
    OR LOWER(u.personal_intro) LIKE '%%%'
    OR LOWER(u.location)       LIKE '%%%'

    OR u.account_status != 'DELETED'      -- with original logic
   AND u.system_role = 10
   AND u.account_status = 'ACTIVE'
ORDER  BY u.set_order, u.page_hits DESC, u.id, user_category
LIMIT  10

此相关问题的详细解释:

使用两个EXISTS semi-joins代替DISTINCT ONCASE ...

,这个替代方案可能会更快
SELECT u.*
      ,CASE WHEN EXISTS (
         SELECT 1 FROM projects p
         WHERE p.user_id = u.id AND p.status = 'LAUNCHED')
       THEN 1 ELSE 2 END AS user_category
FROM   users u
WHERE
    (  LOWER(u.username)       LIKE '%%%' -- ???
    OR LOWER(u.personal_intro) LIKE '%%%'
    OR LOWER(u.location)       LIKE '%%%'
    OR u.account_status != 'DELETED'      -- with alternative logic?
    )
AND    u.system_role = 10  -- assuming it comes from users ???
AND    u.account_status = 'ACTIVE'
AND    EXISTS (SELECT 1 FROM projects p WHERE p.user_id = u.id)
ORDER  BY u.set_order, u.page_hits DESC
LIMIT  10;

答案 1 :(得分:1)

您可以在MIN()结果上使用CASE,似乎放弃DISTINCT将是明智的选择:

SELECT u.*, MIN(CASE 
WHEN p.status = 'LAUNCHED' THEN 1
ELSE 2
END) as user_category
...
GROUP BY <list all columns in the users table>
...

由于“已启动”给出1,因此使用MIN()不仅会强制执行单个结果,还会优先于其他状态“启动”。