【问题标题】:Postgres recursive CTE or crosstab functionPostgres 递归 CTE 或交叉表函数
【发布时间】:2015-09-21 20:52:21
【问题描述】:

我尝试从包含日志信息的表中生成一些用户统计信息。

**TABLE users**
user_id | user_name
-------------------
1       | julia
2       | bob
3       | sebastian


**TABLE logs**
user_id | action        | timepoint
------------------------------------
1       | create_quote  | 2015-01-01
1       | send_quote    | 2015-02-03
1       | create_quote  | 2015-02-02
1       | start_job     | 2015-01-15
2       | start_job     | 2015-02-23
2       | send_quote    | 2015-03-04
2       | start_job     | 2014-12-02

我想要的输出如下表

user_id | username  | create_quote | send_quote | start_job
-----------------------------------------------------------
1       | julia     |2             | 1          | 1
2       | bob       |0             | 1          | 1
3       | sebastian |0             | 0          | 0

它包括所有用户(即使没有记录任何内容),但仅包括日期“2015-01-01”和“2015-05-31”之间的操作。操作按操作类型和用户进行计数/分组。

SQL 语句可能看起来像

SELECT * FROM myfunction() WHERE to_char(timepoint, 'YY/MM') BETWEEN '15/01' AND '15/05';

您知道如何管理吗?我一直在尝试 CTE 和递归以及交叉表函数,但找不到任何解决方案。

【问题讨论】:

  • 你只有这三个动作吗? create_quote、send_quote 和 start_job?
  • @a_horse_with_no_name 现在我只有这三个动作。但未来可能会有不同的
  • 您似乎一直在尝试对我的回答进行编辑...如果您有意见或问题,只需将其添加到我的回答下方或编辑您的问题以澄清。我看到你确实在我的代码中发现了一个错误,现在已经更正了,是的——我不喜欢函数的想法,但我做了一个,因为我认为你的帖子暗示你想要一个函数(尽管我承认我不确定)。见修订...

标签: sql postgresql recursion common-table-expression crosstab


【解决方案1】:

我认为交叉表功能会更优雅,但如果您没有加载扩展程序,或者像我一样在语法上遇到困难,这是一种笨拙的蛮力方式可以做到:

CREATE OR REPLACE FUNCTION get_stats(
    from_date date,
    thru_date date)
  RETURNS table (
    user_id integer, 
    username text, 
    create_quote bigint, 
    send_quote bigint,
    start_job bigint
  )  AS
$BODY$
  select
    l.user_id, u.username,
    sum (case when action = 'create_quote' then 1 else 0 end) as create_quote,
    sum (case when action = 'send_quote' then 1 else 0 end) as send_quote,
    sum (case when action = 'start_job' then 1 else 0 end) as start_job
  from
    logs l
    join users u on l.user_id = u.user_id
  where
    l.timepoint between from_date and thru_date
  group by
    l.user_id, u.username
$BODY$
  LANGUAGE sql VOLATILE
  COST 100
  ROWS 1000;

然后您的查询将是:

select * from get_stats('2015-01-01', '2015-05-31')

就个人而言,我会跳过该函数并将其创建为查询,但可以想象,您需要函数包装器是有原因的。

-- 编辑--

根据尝试的编辑,我知道您可能会接受查询。此外,您希望用户没有条目。

考虑到所有这些,我认为这可能可行:

  select
    u.user_id, u.username,
    sum (case when action = 'create_quote' then 1 else 0 end) as create_quote,
    sum (case when action = 'send_quote' then 1 else 0 end) as send_quote,
    sum (case when action = 'start_job' then 1 else 0 end) as start_job
  from
    users u
    left join logs l on
       l.user_id = u.user_id and
       l.timepoint between '2015-01-01' and '2015-05-31'
  group by
    u.user_id, u.username

【讨论】:

  • 一个小修正,然后它应该正常工作:“group by l.user_id, u.username”必须是“group by u.user_id, u.username”。您是否还想更改标题,使其更适合 cte 和递归?
  • 是的,你是对的——这是固定的。至于标题,这是你的问题,所以改变吧!
猜你喜欢
  • 1970-01-01
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-10
  • 2022-11-30
  • 2012-10-29
  • 1970-01-01
相关资源
最近更新 更多