SQL计算连接表上的唯一真值

时间:2014-07-01 19:20:36

标签: sql postgresql join count aggregate-functions

我有users

Table "public.users"
Column  |  Type   | Modifiers 
--------+---------+-----------
user_id | integer | 
style   | boolean | 
id      | integer | 

access_rights

Table "public.access_rights"
Column  |  Type   | Modifiers 
--------+---------+-----------
user_id | integer | 
id      | integer | 

我有一个查询在访问权限上加入用户,我想计算样式列中值为true的值。

从这个回答:postgresql - sql - count of `true` values,我尝试了

SELECT COUNT( CASE WHEN style THEN 1 ELSE null END )
from users
join access_rights on access_rights.user_id = users.user_id
;

但是,当用户有多个access_rights行时,它会计算重复值。如何在使用连接时仅计算一次值?

3 个答案:

答案 0 :(得分:1)

如果您对style IS TRUE至少 1行(access_rights)的用户数感兴趣,请在加入前汇总access_rights

SELECT count(style OR NULL) AS style_ct
FROM   users
JOIN  (
   SELECT user_id, bool_or(style) AS style
   FROM   access_rights
   GROUP  BY 1
   ) u USING (user_id);

使用JOIN,因为access_rights中没有任何条目的用户在这种情况下不会计数。
使用聚合函数bool_or()

更简单:

SELECT count(*) AS style_ct
FROM   (
   SELECT user_id
   FROM   access_rights
   GROUP  BY 1
   HAVING bool_or(style)
   );

这假设外键强制引用完整性,因此access_rights.user_id中没有users没有相应的行。
同时假设NULL中没有access_rights.user_id值,这会使计数增加1 - 并且可以使用count(user_id)代替count(*)来抵消。

(如果该假设不属实)请使用EXISTS半连接:

SELECT count( EXISTS (
              SELECT  1
              FROM    access_rights
              WHERE   user_id = u.user_id
              AND     style  -- boolean value evaluates on its own
              ) OR NULL
            )
FROM   users u;

我正在使用true boolean值的功能来简化计数和WHERE子句。详细信息:
Compute percents from SUM() in the same SELECT sql query

答案 1 :(得分:1)

你可以这样做:

尝试这样的事情(每the documentation

select sum( case style when TRUE then 1 else 0 end ) as style_count
from public.users         u
join public.access_rights ar on ar.user_id = u.user_id

或者,考虑到您的问题陈述," 我想计算样式列中值为true的值",您可以这样做:

select count(*) as style_count
from public.users         u
join public.access_rights ar on ar.user_id = u.user_id
where u.style = TRUE

编辑注意:

在重新阅读您的问题时,听起来您真正想要的是style属性为true并拥有访问权限的不同用户。你可以通过这个来达到目的:

select count(distinct u.user_id)
from public.users u
join public.access_rights ar on ar.user_id = u.user_id
where u.style = TRUE
;

到达那里的另一种方式是这样的:

select count(*)
from public.users u
where u.style = TRUE
  and exists ( select *
               from public.access_rights ar
               where ar.user_id = u.user_id
             )

我会投票支持后者,因为它更清楚地显示了你的意图。

答案 2 :(得分:1)

似乎每个人对这个问题都有不同的理解。这是我的

select
    count(case when style then 1 end) as classic_case,
    count(style or null) as boolean_count
from users u
where exists (
    select 1
    from access_rights
    where user_id = u.user_id
)

它将计算具有access_rights的用户的真实总数。