繁体   English   中英

Postgresql 查看聚合数据

[英]Postgresql View aggregating data

我有一个问题要使它“好”和高效,并且易于阅读。 不幸的是,它缺乏这两个属性。

给定一个包含date,transaction_type,usernamecredits的表,我想生成一个汇总到这些字段的视图: date_from,date_next,username,credits_total,credits_total

解释:

  • date_from 和 date_to 是一个月的第一天和下一个月的第一天。 (例如 2022-06-01 和 2022-07-01)
  • 用户名是分组的,因此每个 date_from/date_next 对只有一个相同的用户名
  • credits_total 是 credit_change 的总和,其中 transaction = 'charge'
  • credits_left 是 credits_total - sum(credits_change where transaction_type = 'usage')

我发现了多个问题,并且部分能够解决它们:

  • date_from/_next 很容易使用date_trunc('month', date)和 `date_trunc('month', date) + interval '1 month''
  • 组用户名/日期可以使用 group by
  • 制作不重复的 credits_total 很难。 还是子查询是唯一的解决方案?
  • 剩余的积分几乎相同,但使用其他 transaction_type 并从 credits_total 中减去。 如何重复使用 credits_total?

我想出的(并且非常不满意)

源表:

create table usage  -- simplified
(
    datetime timestamp default now() not null,
    transaction_type varchar(16) not null,
    user varchar(128) not null,
    credits_change int not null,
);

我的视图代码:

CREATE MATERIALIZED VIEW token_usage
AS
SELECT 
       -- trivial:
       user,
       date_trunc('month', datetime) as date_from,
       date_trunc('month', datetime) + interval '1 month' as date_next,

       -- sum of credits_change with requirement + duplication
       (    -- see here. first time
            SELECT sum(credits_change)
            FROM usage
            WHERE transaction_type = 'charge'
            AND datetime BETWEEN date_trunc('month', datetime) AND date_trunc('month', datetime) + interval '1 month'
       ) as credits_total,

       -- sum of credits change minus other sum and more duplication
       (   -- see here. using the same again
           SELECT sum(credits_change)
           FROM usage
           WHERE transaction_type = 'charge'
           AND datetime BETWEEN date_trunc('month', datetime) AND date_trunc('month', datetime) + interval '1 month'
       ) - (  -- see here. using the same again, but with different transaction_type
           SELECT sum(credits_change)
           FROM usage
           WHERE transaction_type = 'usage'
           AND datetime BETWEEN date_trunc('month', datetime) AND date_trunc('month', datetime) + interval '1 month'
       ) as credits_left
    FROM usage
    GROUP BY user_name, datetime, datetime_next_start
WITH DATA;

看来我只是缺少一些 postgresql 工具,以使其变得更好。

谢谢您的帮助 :)

在不知道一些示例数据和预期输出来尝试查询的情况下,以下内容肯定可以作为您完整解决方案的草图。 我想,这里的重点是了解聚合函数的FILTER子句(*):

CREATE MATERIALIZED VIEW token_usage AS

SELECT
    user,
    date_trunc('month', datetime) as date_from,
    date_trunc('month', datetime) + interval '1 month' as date_next,
    
    SUM(credits_change) FILTER (WHERE transaction_type = 'charge') as credits_total,
    SUM(credits_change) FILTER (WHERE transaction_type = 'charge')
        - SUM(credits_change) FILTER (WHERE transaction_type = 'usage') as credits_left
FROM usage
GROUP BY 1, 2, 3

由于子查询,重复较少但可读性较差的替代方案:

CREATE MATERIALIZED VIEW token_usage AS

SELECT 
    user,
    date_from,
    date_from + interval '1 month' as date_next,
    credits_total,
    credits_total - credits_usage as credits_left
FROM (
    SELECT
        user,
        date_trunc('month', datetime) as date_from,
    
        SUM(credits_change) FILTER (WHERE transaction_type = 'charge') as credits_total,
        SUM(credits_change) FILTER (WHERE transaction_type = 'usage') as credits_usage
    FROM usage
    GROUP BY 1, 2 
) s

*) 您也可以使用CASE子句代替FILTER

SUM(abc) FILTER (WHERE condition)

-- generally the same as

SUM(
    CASE WHEN condition THEN 
        abc 
    END
)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM