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

PostgreSQL中按日期展开并统计每人每日重复次数的实现方法

PostgreSQL实现用户每日出现次数的转置表

嘿,我来帮你搞定这个行转列的需求!你想要把原始的带用户和时间的表,转成以用户名为行、日期为列,单元格显示用户当天出现次数的格式,这在PostgreSQL里用crosstab函数就能实现,咱们一步步来:

步骤1:安装tablefunc扩展

crosstab函数不属于PostgreSQL的默认内置函数,需要先安装tablefunc扩展才能使用:

CREATE EXTENSION IF NOT EXISTS tablefunc;

步骤2:静态日期范围的转置查询

如果你的日期范围是固定的(比如你明确知道要包含哪些日期),可以先统计每个用户每天的出现次数,再用crosstab转置。

先做基础统计

首先,我们先获取每个用户在每一天的出现次数:

SELECT 
    name,
    DATE(time) AS day,
    COUNT(*) AS count
FROM your_table_name  -- 替换成你的实际表名
GROUP BY name, DATE(time)
ORDER BY name, day;

转置成目标格式

接下来用crosstab把上面的结果转成列,同时用COALESCE把空值(用户当天无数据)替换成0:

SELECT 
    name,
    COALESCE("2018-05-13", 0) AS "2018-05-13",
    COALESCE("2018-05-14", 0) AS "2018-05-14",
    COALESCE("2018-05-16", 0) AS "2018-05-16",
    COALESCE("2018-05-18", 0) AS "2018-05-18"
    -- 继续添加你需要的日期列
FROM crosstab(
    -- 第一个参数:源数据查询(必须是三列:行分组键、列分组键、值)
    'SELECT name, DATE(time), COUNT(*) 
     FROM your_table_name 
     GROUP BY name, DATE(time) 
     ORDER BY 1,2',
    -- 第二个参数:指定要转成列的所有日期(按顺序)
    'SELECT DISTINCT DATE(time) FROM your_table_name ORDER BY 1'
) AS ct(
    name text,
    "2018-05-13" int,
    "2018-05-14" int,
    "2018-05-16" int,
    "2018-05-18" int
    -- 这里的列定义要和上面的日期列一一对应
);

注意:日期作为列名时需要用双引号括起来,否则PostgreSQL会把它当成无效标识符报错。

步骤3:动态处理日期范围(可选)

如果你的日期范围不固定,或者需要自动包含所有出现过的日期,手动写列名太麻烦,可以用动态SQL来自动生成查询:

CREATE OR REPLACE FUNCTION get_user_daily_counts()
RETURNS SETOF record AS $$
DECLARE
    col_defs text;
BEGIN
    -- 自动生成所有日期对应的列定义
    SELECT string_agg(DISTINCT '"' || DATE(time) || '" int', ', ')
    INTO col_defs
    FROM your_table_name
    ORDER BY DATE(time);

    -- 构建并执行动态crosstab查询
    RETURN QUERY EXECUTE format(
        'SELECT * FROM crosstab(
            ''SELECT name, DATE(time), COUNT(*) 
             FROM your_table_name 
             GROUP BY name, DATE(time) 
             ORDER BY 1,2'',
            ''SELECT DISTINCT DATE(time) FROM your_table_name ORDER BY 1''
        ) AS ct(name text, %s)',
        col_defs
    );
END;
$$ LANGUAGE plpgsql;

调用这个函数时,需要指定返回的列结构(或者用json格式接收),比如:

SELECT * FROM get_user_daily_counts()
AS (name text, "2018-05-13" int, "2018-05-14" int, "2018-05-16" int, "2018-05-18" int);

如果需要把空值替换成0,可以在动态SQL里进一步处理每个列的COALESCE逻辑。

额外提示

如果需要包含某个时间段内的所有日期(即使当天没有用户数据),可以先用generate_series生成日期序列,再和用户表做笛卡尔积,左连接统计结果后再转置,这样就能保证所有日期都出现在列中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:24:28