Создайте частную ПОСЛЕДОВАТЕЛЬНОСТЬ для каждого РАЗДЕЛА

У меня есть таблица матчей в разных играх, и я хотел бы рассчитать, насколько плотным является пространство матчей в отношении каждой игры. Пример таблицы:

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

В идеале получится:

id | game  | start_dt        |  time_group_id
---+-------+-----------------+---------------
6  | dota2 | 2020-01-01 12:00| 1
---+-------+-----------------+---------------
1  | dota2 | 2020-01-01 15:00| 2
---+-------+-----------------+---------------
2  | dota2 | 2020-01-01 15:05| 2
---+-------+-----------------+---------------
3  | dota2 | 2020-01-01 18:00| 3
---+-------+-----------------+---------------
4  | cs-go | 2020-01-01 13:05| 4
---+-------+-----------------+---------------
5  | cs-go | 2020-01-01 13:15| 4
---+-------+-----------------+---------------
7  | cs-go | 2020-01-01 14:45| 5

Что в основном означает, что если разрыв между следующей игрой и предыдущей меньше или равен 10 минутам, они считаются в одной временной группе. В противном случае это разные временные группы, и это продолжается.

Затем эти time_group_id используются для отображения полезной информации о совпадениях и их частоте времени.

Мой код приведен ниже, и он идеально подходит для этой цели, однако он не дает равномерно распределенных идентификаторов, поэтому я должен использовать комбинацию game VARCHAR и group_id для поля, чтобы уникально представлять группу. Пожалуйста, запустите его в dbfiddle, чтобы понять, что я имею в виду.

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;

Вопрос в том, есть ли идеальный способ сделать это, или я должен придерживаться того, что у меня есть?

Ваш код выглядит нормально. Но почему вы хотите хранить эту информацию? Вы можете легко вычислить его на лету, когда это необходимо.

GMB 23.12.2020 18:37

Спасибо за вопрос. В основном это связано с необходимостью сопоставления большого объема информации, хранящейся в другой таблице, с каждой группой событий. Например, есть 100 записей для первого события и 200 записей для второго и так далее, и этот запрос соединит эти записи в одну группу, если эти события близки по времени.

winwin 23.12.2020 18:42
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
2
91
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Для этого вам не нужна последовательность, просто кумулятивная сумма:

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), если это то, что вам действительно нужно.

Вот db<>рабочий пример

Но смысл запроса состоит в том, чтобы разделить бои на группы так, чтобы текущий бой был не позднее, чем на 10 минут, чем предыдущий, И присвоить каждой из этих групп уникальный идентификатор, основанный как на первом условии, так и на их игре. Я не смог выполнить ваш запрос, и я действительно не могу понять, что вы пытались донести. Не могли бы Вы уточнить?

winwin 23.12.2020 22:02

@винвин. . . Я исправил запрос и добавил скрипку db<>.

Gordon Linoff 23.12.2020 22:14

Большое спасибо, благодаря вашей идее с накопительной суммой я смог решить эту проблему так, как было задумано! Я опубликую ответ, я действительно хотел бы, чтобы вы увидели, чего я пытался достичь. Ты классный парень, спасибо, я всегда поставлю тебе палец вверх.

winwin 23.12.2020 22:27
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;

Этот запрос дал мне результаты, которые я действительно хотел. Очень благодарен за идею cumsum от @Gordon Linoff, спасибо!

Другие вопросы по теме