我有一个用户事件表:
CREATE TABLE public.user_session_activity_table (
id integer NOT NULL,
"userId" integer NOT NULL,
"creationDate" timestamp without time zone DEFAULT now() NOT NULL
);
INSERT INTO public.user_session_activity_table
(
id,
"userId",
"creationDate"
)
VALUES
(1, 1, '2021-11-06 10:54:23.891327'),
(2, 1, '2021-11-06 10:59:56.616956'),
(3, 1, '2021-11-06 10:59:57.680751'),
(4, 1, '2021-11-06 10:59:58.857336'),
(5, 1, '2021-11-06 11:36:47.112812'),
(6, 1, '2021-11-06 11:36:49.049485'),
(7, 1, '2021-11-06 11:36:50.931315')
所需输出:
id userId sessionLenght
1 1 123s -- row 1
2 1 123s -- row 2-4 grouped together
3 1 123s -- row 4-7 grouped together
说明:
我正在创建用户 session View ,形成一个包含已保存用户事件行的表。我想根据创建日期之间耗时增量进行分组。如果经过了太多时间(假设阈值是 1 分钟),则当前组结束并开始新一组。这将导致此示例数据对齐到 3 个组:
- id:1
- id:2,id:3,id:4
- id:5,id:6,id:7
如您所见,最显着的时间差异是 id:1 <-> id:2 和 id:4 <-> id:5 之间,这就是为什么它应该分为 3 个单独的组。
我正在使用最新版本的 PostgreSQL。 “sessionLength”并不是那么重要,我自己可以找到解决方案,主要问题是创建这些组。
一个重要的事实是:四舍五入日期不起作用, session 可能会持续几分钟或几小时。应该结束和开始组的事情是事件之间的时间差(例如,当用户注销或离开键盘一小时时)。
更新1:
窗口 RANGE 函数也不是孤立的。一开始它很有说服力,但它只将预先指定的时间范围内的行分组在一起。
SELECT
*
FROM (
SELECT
"usa"."userId",
"usa"."creationDate" AS "currentDate",
FIRST_VALUE("usa"."creationDate") OVER www AS "sessionStartDate",
LAST_VALUE("usa"."creationDate") OVER www AS "sessionEndDate"
-- first_value("usa"."id") OVER www AS first_id,
-- last_value("usa"."id") OVER www AS last_id,
-- LAST_VALUE("usa"."creationDate") OVER www - FIRST_VALUE("usa"."creationDate") OVER www AS "sessionLength"
FROM public."user_session_activity_view" AS "usa"
WINDOW www AS
(
PARTITION BY "userId"
ORDER BY "creationDate"
RANGE BETWEEN '3 min' PRECEDING AND '3 min' FOLLOWING
)
) AS "sq"
WHERE "sq"."userId" = 33
ORDER BY
"sq"."userId",
"sq"."sessionStartDate"
谢谢,非常感谢任何帮助! (如果问题不清楚,请告诉我,我会尽力澄清一下!:))
最佳答案
我知道这并不能为您提供完整的解决方案,但它可能会帮助您实现这一目标,使用 row_number 来标识要在 60 秒的持续时间内分组在一起的行:
with u as (
select *,
id - row_number() over (partition by userid, round(extract('epoch' from creationdate) / 60) * 60 order by creationdate) gp
from t
)
select
row_number() over(partition by max(userId) order by max(creationdate)) GroupNo,
max(userid) UserId,
min(creationdate) StartOfRange, max(CreationDate) EndOfRange,
round(max(date_part('second',creationdate::time))- min(date_part('second',creationdate::time))) duration
from u
group by gp
关于sql - 如何按彼此接近的时间戳对 SQL 表进行分组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69864133/