postgresql 使用sql对交通数据进行会话-间隙和孤岛问题

pexxcrt2  于 2023-03-12  发布在  PostgreSQL
关注(0)|答案(1)|浏览(105)

我有一个名为myTable的活动表,数据如下所示:

CREATE TABLE myTable
(
    userid      text,
    webid         text,
    "ts" timestamp
);

INSERT INTO myTable
    ("userid", "webid", "ts")
VALUES ('1', 'A', '34', '2023-01-31 16:34:49.000'),
       ('2', 'A', '73', '2023-01-31 16:34:50.000'),
       ('3', 'A', '97', '2023-01-31 16:34:58.000'),
       ('4', 'A', '17', '2023-01-31 17:35:02.000'),
       ('5', 'A', '17', '2023-01-31 17:35:07.000'),
       ('6', 'A', '17', '2023-01-31 17:35:18.000'),
       ('7', 'A', '17', '2023-01-31 17:35:30.000'),
       ('8', 'A', '1', '2023-01-31 17:35:37.000'),
       ('9', 'A', '1', '2023-01-31 17:35:38.000'),
       ('10', 'A', '77', '2023-01-31 17:35:41.000'),
       ('11', 'A', '77', '2023-01-31 17:35:42.000'),
       ('12', 'A', '15', '2023-01-31 17:35:42.000'),
       ('13', 'A', '44', '2023-01-31 17:35:42.000'),
       ('14', 'A', '1', '2023-01-31 17:35:42.000'),
       ('15', 'A', '77', '2023-01-31 17:35:45.000'),
       ('16', 'A', '44', '2023-01-31 17:35:45.000'),
       ('17', 'A', '1', '2023-01-31 17:37:10.000'),
       ('18', 'A', '1', '2023-01-31 17:37:12.000'),
       ('19', 'A', '77', '2023-01-31 17:37:14.000'),
       ('20', 'A', '77', '2023-01-31 17:52:14.000'),
       ('21', 'A', '77', '2023-01-31 18:12:14.000'),
       ('22', 'A', '77', '2023-01-31 18:45:14.000'),
       ('23', 'A', '77', '2023-01-31 18:55:15.000'),
       ('1', 'B', '33', '2023-01-31 06:37:15.000'),
       ('2', 'B', '56', '2023-01-31 06:40:15.000')
  ;

| 劳伊德|用户识别码|网络标识符|ts|
| - ------|- ------|- ------|- ------|
| 1个|A类|三十四|2023年1月31日16时34分49秒|
| 第二章|A类|七十三|2023年1月31日16时34分50秒|
| 三个|A类|九十七|2023年1月31日16时34分58秒|
| 四个|A类|十七|2023年1月31日17时35分02秒|
| 五个|A类|十七|2023年1月31日17时35分07秒|
| 六个|A类|十七|2023年1月31日17时35分18秒|
| 七|A类|十七|2023年1月31日17时35分30秒|
| 八个|A类|1个|2023年1月31日17时35分37秒|
| 九|A类|1个|2023年1月31日17时35分38秒|
| 十个|A类|七十七|2023年1月31日17时35分41秒|
| 十一|A类|七十七|2023年1月31日17时35分42秒|
| 十二|A类|十五|2023年1月31日17时35分42秒|
| 十三|A类|四十四|2023年1月31日17时35分42秒|
| 十四|A类|1个|2023年1月31日17时35分42秒|
| 十五|A类|七十七|2023年1月31日17时35分45秒|
| 十六|A类|四十四|2023年1月31日17时35分45秒|
| 十七|A类|1个|2023年1月31日17时37分10秒|
| 十八|A类|1个|2023年1月31日17时37分12秒|
| 十九|A类|七十七|2023年1月31日17时37分14秒|
| 二十个|A类|七十七|2023年1月31日17时52分14秒|
| 二十一|A类|七十七|2023年1月31日18时12分14秒|
| 二十二|A类|七十七|2023年1月31日18时45分14秒|
| 二十三|A类|七十七|2023年1月31日18时55分15秒|
| 1个|B|三十三|2023年1月31日06:37:15|
| 第二章|B|五十六|2023年1月31日06时40分15秒|
我要返回的输出如下:
| 劳伊德|用户识别码|网络标识符|会话标识|ts|第一个_ts|
| - ------|- ------|- ------|- ------|- ------|- ------|
| 1个|A类|三十四|1个|2023年1月31日16时34分49秒|2023年1月31日16时34分49秒|
| 第二章|A类|七十三|第二章|2023年1月31日16时34分50秒|2023年1月31日16时34分50秒|
| 三个|A类|九十七|三个|2023年1月31日16时34分58秒|2023年1月31日16时34分58秒|
| 四个|A类|十七|四个|2023年1月31日17时35分02秒|2023年1月31日17时35分02秒|
| 五个|A类|十七|四个|2023年1月31日17时35分07秒|2023年1月31日17时35分02秒|
| 六个|A类|十七|四个|2023年1月31日17时35分18秒|2023年1月31日17时35分02秒|
| 七|A类|十七|四个|2023年1月31日17时35分30秒|2023年1月31日17时35分02秒|
| 八个|A类|1个|五个|2023年1月31日17时35分37秒|2023年1月31日17时35分37秒|
| 九|A类|1个|五个|2023年1月31日17时35分38秒|2023年1月31日17时35分37秒|
| 十个|A类|七十七|六个|2023年1月31日17时35分41秒|2023年1月31日17时35分41秒|
| 十一|A类|七十七|六个|2023年1月31日17时35分42秒|2023年1月31日17时35分42秒|
| 十二|A类|十五|七|2023年1月31日17时35分42秒|2023年1月31日17时35分42秒|
| 十三|A类|四十四|八个|2023年1月31日17时35分42秒|2023年1月31日17时35分42秒|
| 十四|A类|1个|九|2023年1月31日17时35分42秒|2023年1月31日17时35分42秒|
| 十五|A类|七十七|十个|2023年1月31日17时35分45秒|2023年1月31日17时35分45秒|
| 十六|A类|四十四|十一|2023年1月31日17时35分45秒|2023年1月31日17时35分45秒|
| 十七|A类|1个|十二|2023年1月31日17时37分10秒|2023年1月31日17时37分10秒|
| 十八|A类|1个|十二|2023年1月31日17时37分12秒|2023年1月31日17时37分10秒|
| 十九|A类|七十七|十三|2023年1月31日17时37分14秒|2023年1月31日17时37分14秒|
| 二十个|A类|七十七|十三|2023年1月31日17时52分14秒|2023年1月31日17时37分14秒|
| 二十一|A类|七十七|十三|2023年1月31日18时12分14秒|2023年1月31日17时37分14秒|
| 二十二|A类|七十七|十四|2023年1月31日18时45分14秒|2023年1月31日18时45分14秒|
| 二十三|A类|七十七|十四|2023年1月31日18时55分15秒|2023年1月31日18时45分14秒|
| 1个|B|三十三|1个|2023年1月31日06:37:15|2023年1月31日06:37:15|
| 第二章|B|五十六|第二章|2023年1月31日06时40分15秒|2023年1月31日06时40分15秒|
first_ts表示第一个时间戳,first_ts的逻辑是如果用户ID在连续的时间戳访问同一个webID,并且每个时间戳的间隔福尔斯30分钟以内,则将这些访问记录为一个会话,例如,第4、5、6、7行,每个时间间隔都小于30分钟,则其first_ts为第一个事件的时间戳,即2023-01-31 17:35:02。
如果用户ID访问一个webid跳转到另一个webid返回到第一个webid,则first_ts刷新到当前时间戳,如第8、9、10、11、12、13、14、15、16、17、18行,用户ID A先访问webid=1,然后跳转到webid= 77、15、44,返回webid=1后,再到第12行。行13和行14应当被记录为具有当前first_ts的新会话。
如果一个userid在连续的时间戳访问一个webid,并且有一个时间戳间隔大于30分钟,也会中断会话并刷新first_ts,例如第19、20、21、22、23行,第21行和第22行之间的时间间隔超过30分钟,那么对于第22行和第23行,它们的first_ts被刷新为当前时间戳,并且它们将成为新的会话。
我在这里分享我的脚本,该脚本包含了视图的rowid,看起来它返回了正确的first_ts,但是session_id是错误的。
SQL版本:

create temp table lags as
select *, lag(webid,1,webid) over(partition by userid order by ts) lag_webid,
lag(ts,1,ts) over(partition by userid order by ts) lag_ts 
from mytable;

create temp table is_sessions as 
select *,
case when webid!=lag_webid or abs(datediff(minutes,ts,lag_ts)) > 30 then 1
     else 0
end as is_session
from lags;

create temp table sessions as 
select *,
sum(is_session) over(partition by userid order by ts)+1 as session_id
from is_sessions;

create temp table sessions_ts as 
select *,
min(ts) over(partition by userid, session_id order by ts) as first_ts
from sessions;

等效PostgreSQL版本:

select rowid, userid, webid, session_id, ts, min(ts) over(partition by userid, session_id order by ts) first_ts
from (
    select t.*,
        1 + count(*) 
            filter(where webid != lag_webid or ts > lag_ts + interval '30' minute) 
            over(partition by userid order by ts) session_id
    from (
        select t.*, 
            lag(webid, 1, webid) over(partition by userid order by ts) lag_webid,
            lag(ts, 1, ts)       over(partition by userid order by ts) lag_ts
        from mytable t
    ) t
) t

有人有更好的解决方案来完成这个任务吗?谢谢!
脚本参考:https://dbfiddle.uk/-c3ZNO65
我尝试了几个查询,但无法获得正确的session_id。

von4xj4u

von4xj4u1#

您的数据有点棘手,但是由于您有一个rowid作为排序顺序,我们可以使用它来生成您想要的结果

WITH CTE as (
  SELECT
"rowid", "userid", "webid", "ts"
  , CASE WHEN "webid" = LAG("webid") OVER(PARTITION BY "userid" ORDER BY "rowid") then 
  CASE WHEN EXTRACT(MINUTE from"ts"-LAG("ts") OVER(PARTITION BY "userid" ORDER BY "rowid")) > 30 
  THEn 1 ELSE  0 END
  ELSE 1 END tab
  FROM mytable
  )
, CTE2 As
(
  SELECT "rowid", "userid", "webid", "ts",tab, SUM(tab) OVER(PARTITION BY "userid" ORDER BY "rowid") session_id
FROM CTE
  )
SELECT  
  "rowid", "userid", "webid","session_id",  "ts"
,FIRST_VALUE("ts") OVER(PARTITION BY "userid","session_id" ORDER BY "ts") as first_ts
FROM CTE2

| 劳伊德|用户识别码|网络标识符|会话标识|ts|第一个_ts|
| - ------|- ------|- ------|- ------|- ------|- ------|
| 1个|A类|三十四|1个|2023年1月31日16时34分49秒|2023年1月31日16时34分49秒|
| 第二章|A类|七十三|第二章|2023年1月31日16时34分50秒|2023年1月31日16时34分50秒|
| 三个|A类|九十七|三个|2023年1月31日16时34分58秒|2023年1月31日16时34分58秒|
| 四个|A类|十七|四个|2023年1月31日17时35分02秒|2023年1月31日17时35分02秒|
| 五个|A类|十七|四个|2023年1月31日17时35分07秒|2023年1月31日17时35分02秒|
| 六个|A类|十七|四个|2023年1月31日17时35分18秒|2023年1月31日17时35分02秒|
| 七|A类|十七|四个|2023年1月31日17时35分30秒|2023年1月31日17时35分02秒|
| 八个|A类|1个|五个|2023年1月31日17时35分37秒|2023年1月31日17时35分37秒|
| 九|A类|1个|五个|2023年1月31日17时35分38秒|2023年1月31日17时35分37秒|
| 十个|A类|七十七|六个|2023年1月31日17时35分41秒|2023年1月31日17时35分41秒|
| 十一|A类|七十七|六个|2023年1月31日17时35分42秒|2023年1月31日17时35分41秒|
| 十二|A类|十五|七|2023年1月31日17时35分42秒|2023年1月31日17时35分42秒|
| 十三|A类|四十四|八个|2023年1月31日17时35分42秒|2023年1月31日17时35分42秒|
| 十四|A类|1个|九|2023年1月31日17时35分42秒|2023年1月31日17时35分42秒|
| 十五|A类|七十七|十个|2023年1月31日17时35分45秒|2023年1月31日17时35分45秒|
| 十六|A类|四十四|十一|2023年1月31日17时35分45秒|2023年1月31日17时35分45秒|
| 十七|A类|1个|十二|2023年1月31日17时37分10秒|2023年1月31日17时37分10秒|
| 十八|A类|1个|十二|2023年1月31日17时37分12秒|2023年1月31日17时37分10秒|
| 十九|A类|七十七|十三|2023年1月31日17时37分14秒|2023年1月31日17时37分14秒|
| 二十个|A类|七十七|十三|2023年1月31日17时52分14秒|2023年1月31日17时37分14秒|
| 二十一|A类|七十七|十三|2023年1月31日18时12分14秒|2023年1月31日17时37分14秒|
| 二十二|A类|七十七|十四|2023年1月31日18时45分14秒|2023年1月31日18时45分14秒|
| 二十三|A类|七十七|十四|2023年1月31日18时55分15秒|2023年1月31日18时45分14秒|
| 1个|B|三十三|1个|2023年1月31日06:37:15|2023年1月31日06:37:15|
| 第二章|B|五十六|第二章|2023年1月31日06时40分15秒|2023年1月31日06时40分15秒|

SELECT 25

fiddle

相关问题