You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL窗口函数实现用户任务平均耗时统计问题求助

问题解答

你之前的SQL写法出现重复行的核心原因是未添加GROUP BY子句:窗口函数的特性是不会压缩结果行数,只会在当前结果集的每一行上计算对应分区内的聚合值,因此每个用户关联了多少个任务就会返回多少行结果,自然出现重复。

这个场景不需要使用窗口函数,直接在你原有正确的分组统计SQL基础上,新增普通AVG聚合函数即可实现需求,实现逻辑更简单、执行效率也更高。
另外注意你原有单任务时长的计算存在运算优先级错误:需要先计算两个时间戳的秒数差值,再整体除以3600才能得到正确的小时数,原写法是结束时间秒数减去(开始时间秒数/3600),计算结果不符合预期。

最优实现写法(普通聚合函数)

SELECT 
    users.id AS u_id,
    concat(users.last_name, ', ', users.first_name) AS u_name, 
    COUNT(*) AS occurrences,
    -- 修正时长计算逻辑,差值整体除以3600得到小时数,可按需调整ROUND的小数保留位数
    ROUND(AVG((EXTRACT(EPOCH FROM tasks.closed_at) - EXTRACT(EPOCH FROM tasks.started_at)) / 3600), 1) AS average_duration
FROM tasks
INNER JOIN task_users ON task_users.task_id = tasks.id
INNER JOIN users ON users.id = task_users.user_id
WHERE tasks.closed_at IS NOT NULL 
-- 分组字段补充u_name适配所有数据库规范,避免部分数据库因非聚合字段未分组报错
GROUP BY u_id, u_name
ORDER BY occurrences DESC

窗口函数实现写法(不推荐,仅作原理参考)

如果你需要用窗口函数实现也可以,需要搭配DISTINCT去重,本质是过滤掉同用户重复的计算行:

SELECT DISTINCT
    users.id AS u_id,
    concat(users.last_name, ', ', users.first_name) AS u_name, 
    COUNT(*) OVER(PARTITION BY users.id) AS occurrences,
    ROUND(AVG((EXTRACT(EPOCH FROM tasks.closed_at) - EXTRACT(EPOCH FROM tasks.started_at)) / 3600) OVER(PARTITION BY users.id), 1) AS average_duration
FROM tasks
INNER JOIN task_users ON task_users.task_id = tasks.id
INNER JOIN users ON users.id = task_users.user_id
WHERE tasks.closed_at IS NOT NULL 
ORDER BY occurrences DESC

该写法需要先关联全量任务数据再去重,执行效率远低于直接分组聚合,因此仅作窗口函数的用法参考,生产环境优先使用第一种写法。

内容的提问来源于stack exchange,提问作者cndev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 13:36:04