有什么方法可以清理这个复杂的聚合函数?

时间:2017-10-31 00:58:31

标签: sql postgresql

我正在使用Postgres,但我对这个通用解决方案很好(如果你愿意,你可以忽略文本聚合)。

使用以下表格设置:

id  ref_id  user_id  start_time  duration text
100 2000    1        15000       200      hello
101 2000    1        16000       300      world
102 2000    1        22000       400      foo
103 2000    2        17000       500      bar
104 2000    2        21000       600      baz

我正在尝试使用最小整数时间戳(ms或其他)并将其用作窗口化查询的基础。这个想法是所有用户的绝对最小化它是对话的窗口。所以如果我看了5000个单位的窗户,我就有2个街区(15000-20000,20001-25000)。

预期产出:

ref_id  user_id  block  sum(duration) count(*) text
2000    1        1      500           2        hello world
2000    1        2      400           1        foo
2000    2        1      500           1        bar
2000    2        2      600           1        baz

我经历了多次自连接和窗口的迭代,但我不能比三个嵌套查询更紧凑。

select user_id, block, string_agg(text, ' '), sum(duration)
from 
    (select user_id, FLOOR((start_time - t1.st)/5000) as block, start_time, text, duration
    from table t0
    inner join 
        (select id, min(start_time) as st from table group by 1) as t1
        on t0.ref_id = t1.ref_id
    order by 1, 2, 3) t2
group by 1, 2;

我认为我需要第三个的原因是因为我无法通过start_time进行排序,并且对于某些聚合(例如文本连接)很重要。

如果有人是专家,我会喜欢一些帮助!

2 个答案:

答案 0 :(得分:1)

我不确定为什么"阻止"是如此重要,但您可以删除一级子查询:

select user_id, FLOOR((start_time - minstart)/5000),
       string_agg(text, ' '), sum(duration)
from (select user_id,  as block, 
             start_time, text, duration,
             min(start_time) over (partition by id) as minstart
      from table t0
      ) t
group by 1, 2;

答案 1 :(得分:1)

假设示例sql中名为id的字段是拼写错误,应该是示例数据中指定的user_id

编写此查询的最简单方法是:

SELECT
  user_id
, FLOOR((start_time - mst)/5000) + 1 block
, SUM(duration)
, STRING_AGG("text", ' ')
FROM mytable, (SELECT MIN(start_time) mst FROM mytable) minst
GROUP BY 1, 2

您不需要任何窗口函数或嵌套查询。这里,,子句中FROM分隔两个关系,隐含地执行CROSS JOIN或笛卡尔积)。这就是大多数人开始在SQL查询中编写联接的方式。

但是,使用,隐式表示CROSS JOIN通常被认为是错误的样式,因此上述查询可以写成:

SELECT
  user_id
, FLOOR((start_time - mst)/5000) + 1 block
, SUM(duration)
, STRING_AGG("text", ' ')
FROM mytable CROSS JOIN (SELECT MIN(start_time) mst FROM mytable) minst
GROUP BY 1, 2

此外,根据您的示例sql尝试和所需的输出,您似乎必须向FLOOR((start_time - minst)/5000)添加1以使块从1,2开始... ...