不带子查询的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 |
+------------+---------+