不带子查询的PostgreSQL中占总数的百分比

不带子查询的PostgreSQL中占总数的百分比,postgresql,Postgresql,我有一张有用户的桌子。每个用户都有一个国家/地区。我想得到的是所有国家的列表,包括用户数量和百分比/总数。到目前为止,我得到的是: SELECT country_id, COUNT(*) AS total, ((COUNT(*) * 100) / (SELECT COUNT(*) FROM users WHERE cond1 = true AND cond2 = true AND cond3 = true)::decimal) AS percent FROM users WHERE cond1

我有一张有用户的桌子。每个用户都有一个国家/地区。我想得到的是所有国家的列表,包括用户数量和百分比/总数。到目前为止,我得到的是:

SELECT
country_id,
COUNT(*) AS total,
((COUNT(*) * 100) / (SELECT COUNT(*) FROM users WHERE cond1 = true AND cond2 = true AND cond3 = true)::decimal) AS percent
FROM users
WHERE cond1 = true AND cond2 = true AND cond3 = true
GROUP BY contry_id
两个查询中的条件相同。我尝试在没有子查询的情况下执行此操作,但无法获得用户总数,而是每个国家的用户总数。有没有一种不用子查询就可以做到这一点的方法?我正在使用PostgreSQL。非常感谢您的帮助。
提前感谢

我不是PostgreSQL用户,但一般的解决方案是使用窗口函数

在网站上阅读如何使用此功能

我可以用来描述它的最好解释是:基本上,它允许您在一个字段上执行GROUPBY,而不需要GROUPBY子句

我相信这可能会奏效:

SELECT 
    country_id, 
    COUNT(*) OVER (country_id) 
    ((((COUNT(*) OVER (country_id)) * 100) / COUNT(*) OVER () )::decimal) as percent
FROM 
    users
WHERE
    cond1 = true AND cond2 = true AND cond3 = true

我猜您想要消除子查询的原因是为了避免扫描用户表两次。记住,总数是每个国家的计数总和

WITH c AS (
  SELECT
    country_id,
    count(*) AS cnt
  FROM users
  WHERE cond1=...
  GROUP BY country_id
) 
SELECT
  *,
  100.0 * cnt / (SELECT sum(cnt) FROM c) AS percent
FROM c;
此查询使用每个国家的统计信息构建一个小型CTE。它将只扫描用户表一次,并且每个国家只生成一行小结果集

在这个小的结果集上,c的总SELECT sumcnt只计算一次,因此它使用的时间可以忽略不计

您还可以使用窗口功能:

SELECT
  country_id,
  cnt,
  100.0 * cnt / (sum(cnt) OVER ()) AS percent 
FROM (
  SELECT country_id, count(*) as cnt from users group by country_id
) foo;
这与nightwolf的查询相同,错误已被删除lol


这两个查询占用的时间差不多相同。

这确实很旧,但上面两个选择的示例要么不起作用,要么过于复杂

SELECT
    country_id,
    COUNT(*),
    (COUNT(*) / (SUM(COUNT(*)) OVER() )) * 100
FROM
    users
WHERE
    cond1 = true AND cond2 = true AND cond3 = true
GROUP BY 
    country_id
第二个计数不是必需的,只是为了调试以确保获得正确的结果。诀窍是记录集上计数顶部的总和

希望这对别人有帮助

此外,如果有人想在Django中执行此操作,只需创建一个聚合:

class PercentageOverRecordCount(Aggregate):
    function = 'OVER'
    template = '(COUNT(*) / (SUM(COUNT(*)) OVER() )) * 100'

    def __init__(self, expression, **extra):
        super().__init__(
            expression,
            output_field=DecimalField(),
            **extra
        )

现在可以在注释中使用。

使用上一个PostgreSQL版本,查询可以是下一个:

CREATE TABLE users (
    id serial,
    country_id int
);

INSERT INTO users (country_id) VALUES (1),(1),(1),(2),(2),(3);

select distinct
    country_id,
    round(
        ((COUNT(*) OVER (partition by country_id )) * 100)::numeric 
        / COUNT(*) OVER () 
    , 2) as percent
from users 
order by country_id
;
结果


事实上,这个查询将在users表中每行生成一个输出行,因此您确实需要一个GROUPBY。看看我的答案。@peufeu:我以前从来没有写过窗口函数,也没有测试过窗口函数。似乎我需要阅读更多的语法。是的,想象一下,通过某种方式,比如排名超额分配,在分区中每行给你1个值,比如每一场比赛一名跑步者的排名;窗口函数允许您执行类似这样功能强大的操作,或根据顺序访问上一行/下一行,但它们不进行任何分组。在这种情况下,计数*结束将在所有行中重复。嗯。。。我得到一个错误:错误:语法错误在或接近于c第1行:使用c作为SELECT ^************错误*********错误:语法错误在或接近于c SQL状态:42601字符:1您需要8.4版本来使用cte的公共表表达式和窗口函数…我没有意识到外部查询上没有分组。很好!
+============+=========+
| country_id | percent |
+============+=========+
| 1          | 50.00   |
+------------+---------+
| 2          | 33.33   |
+------------+---------+
| 3          | 16.67   |
+------------+---------+