Sql 为每个分区生成私有序列

Sql 为每个分区生成私有序列,sql,postgresql,Sql,Postgresql,我有一个不同游戏的对位表,我想计算每个游戏的对位空间有多密集。示例表: id | game | start_dt ---+-------+----------------- 1 | dota2 | 2020-01-01 15:00 ---+-------+----------------- 2 | dota2 | 2020-01-01 15:05 ---+-------+----------------- 3 | dota2 | 2020-01-01 18:00 ---+-------+

我有一个不同游戏的对位表,我想计算每个游戏的对位空间有多密集。示例表:

id | game  | start_dt
---+-------+-----------------
1  | dota2 | 2020-01-01 15:00
---+-------+-----------------
2  | dota2 | 2020-01-01 15:05
---+-------+-----------------
3  | dota2 | 2020-01-01 18:00
---+-------+-----------------
4  | cs-go | 2020-01-01 13:05
---+-------+-----------------
5  | cs-go | 2020-01-01 13:15
---+-------+-----------------
6  | dota2 | 2020-01-01 12:00
---+-------+-----------------
7  | cs-go | 2020-01-01 14:45
理想情况下会产生:

这基本上意味着,如果下一场比赛和上一场比赛之间的差距小于或等于10分钟,他们被认为是在同一时间组。除此之外,它们是不同的时间组,并继续进行

然后使用这些时间组ID映射有关匹配及其时间频率的有用信息

下面是我的代码,理想情况下它可以达到这个目的,但是,它不会给出等距的id,所以我必须使用game VARCHAR和group_id的组合来唯一地表示一个组。请把它放在电脑里,明白我的意思

CREATE TABLE fight(
   id BIGSERIAL PRIMARY KEY,
   date TIMESTAMP NOT NULL,
   game VARCHAR NOT NULL
);

INSERT INTO fight(date, game) 
VALUES 
('2020-01-01 15:00'::TIMESTAMP, 'dota2'), 
('2020-01-01 15:05'::TIMESTAMP, 'dota2'), 
('2020-01-01 18:00'::TIMESTAMP, 'dota2'), 
('2020-01-01 13:05'::TIMESTAMP, 'cs-go'), 
('2020-01-01 13:15'::TIMESTAMP, 'cs-go'),
('2020-01-01 12:00'::TIMESTAMP, 'dota2'),
('2020-01-01 14:45'::TIMESTAMP, 'cs-go');

SELECT * FROM fight;

CREATE SEQUENCE seq START 1 CACHE 1;

SELECT
a.id,
a.game,
a.start_dt,
(CASE WHEN (a.start_dt - INTERVAL '10 min' <= a.prev_start_dt) THEN currval('seq')
     ELSE nextval('seq')
END)::VARCHAR || '|' || a.game AS time_group_id
FROM
(
SELECT 
   fight.id, 
   fight.game,
   fight.date AS start_dt,
   LAG (fight.date, 1, fight.date) OVER (PARTITION BY fight.game ORDER BY fight.date) AS prev_start_dt
FROM fight CROSS JOIN (SELECT setval('seq', 1)) s
) a
ORDER BY a.game, a.start_dt;

问题是:有没有理想的方法可以做到这一点,或者我应该坚持我所得到的吗?

你不需要一个序列,只需要一个累积的总和:

SELECT f.*,
       COUNT(*) FILTER (WHERE prev_date < date - interval '10 min') OVER (ORDER BY date) as time_group_id
FROM (SELECT f.*,
             LAG(f.date) OVER (PARTITION BY f.game ORDER BY f.date) AS prev_date
      FROM fight f
     ) f;
注意:这可能从0开始,而不是从1开始。如果有区别,请使用1+

这将生成一个数字,而不是字符串。如果您真正需要的话,可以使用::text将其转换为字符串

他是一把小提琴


这个查询给了我我真正想要的结果。非常感谢@Gordon Linoff的cumsum创意,谢谢

你的代码看起来不错。但是为什么要存储这些信息呢?您可以在需要时轻松计算。谢谢您的提问。这主要是因为需要将另一个表中存储的大量信息映射到每组事件。例如,第一个事件有100条记录,第二个事件有200条记录,依此类推,此查询将这些记录连接到一个组中,如果这些事件在时间上接近。但该查询的意义是将战斗分组,使当前战斗不晚于前一次战斗的10分钟,并根据第一个条件和他们的游戏为每个组提供唯一的id。我无法运行你的查询,我真的无法理解你想要表达的意思。你能说得更具体些吗?@winwin。我修复了查询并添加了一个dbfiddle。非常感谢,感谢你的想法,我能够以预期的方式解决这个问题!我会把答案贴出来,我真的很想让你看看我想要实现什么。你是个很酷的人,谢谢,我会永远向你竖起大拇指。
SELECT f.*,
       COUNT(*) FILTER (WHERE prev_date < date - interval '10 min') OVER (ORDER BY date) as time_group_id
FROM (SELECT f.*,
             LAG(f.date) OVER (PARTITION BY f.game ORDER BY f.date) AS prev_date
      FROM fight f
     ) f;
SELECT
b.id,
b.game,
b.start_dt,
sum(b.time_group_count) OVER (ORDER BY b.game, b.start_dt) as time_group_id
FROM
(SELECT
a.id,
a.game,
a.start_dt,
CASE WHEN a.prev_start_dt IS NULL THEN 1
     WHEN (a.start_dt - INTERVAL '10 min' <= a.prev_start_dt) THEN 0
     ELSE 1
END AS time_group_count
FROM
(
SELECT 
   fight.id, 
   fight.game,
   fight.date AS start_dt,
   LAG (fight.date, 1) OVER (PARTITION BY fight.game ORDER BY fight.date) AS prev_start_dt
FROM fight 
) a
ORDER BY a.game, a.start_dt) b;