Лучше SQL для связи записей в течение 1 часа скользящего окна - PullRequest
0 голосов
/ 24 декабря 2018

Я пытаюсь создать новую таблицу с (user1, user2, count), обозначающую количество раз, когда оба пользователя разделяют одно и то же значение в одном столбце в течение 1 часа.

WITH d1 AS (SELECT * FROM user_access_tab 
WHERE last_access >= 1544630400 AND last_access <= 1545601214)
SELECT d1.userid, d2.userid, COUNT(*) as count
FROM d1
INNER JOIN d1 AS d2
ON d1.item = d2.item AND d1.userid != d2.userid
WHERE d1.last_access < d2.last_access  AND 
      (d2.last_access - d1.last_access) <= 3600
GROUP BY d1.userid, d2.userid

Однако такие запросы очень медленные, даже с интервалом в 1 час.Мне нужно запросить данные за 6 месяцев, которые накопили до миллиардов строк записей.Как я могу улучшить SQL?

user_access_tab выглядит следующим образом

user_access_tab

Новая таблица выглядит следующим образом.Два идентификатора пользователя связаны, если их last_access находятся в пределах 1 часа, а счетчик увеличивается на 1.

enter image description here

1 Ответ

0 голосов
/ 25 декабря 2018

ИМХО, проблема с вашим запросом состоит в том, что вы объединяете слишком много записей друг с другом.

Возьмите приведенный ниже минимальный пример, где я вставил CTE:

WITH user_access_tab(item, userid, last_access) AS (
    SELECT UNNEST(ARRAY['A', 'A', 'A', 'A', 'A', 'A']), 
           UNNEST(ARRAY[11383575,11383575,52539489,52539489,24830131,24830131]),
           UNNEST(ARRAY[1545645324,1545645325,1545647895,1545647896,1545646895,1545646896])
    /*UNION ALL
    SELECT UNNEST(ARRAY['A', 'A', 'A', 'A', 'A', 'A']), 
           UNNEST(ARRAY[11383575,11383575,52539489,52539489,24830131,24830131]),
           UNNEST(ARRAY[1545645326,1545645327,1545647897,1545647898,1545646897,1545646898])*/
),
d1 AS (SELECT * FROM user_access_tab 
WHERE last_access >= 1544630400 AND last_access <= 1545661214
)                       
SELECT d1.userid, d2.userid, COUNT(*) as count
FROM d1
INNER JOIN d1 AS d2
ON d1.item = d2.item AND d1.userid != d2.userid
WHERE d1.last_access < d2.last_access  AND 
      (d2.last_access - d1.last_access) <= 3600
GROUP BY d1.userid, d2.userid

CTEимеет 6 записей, и запрос возвращает 3 записи с количеством, равным 4. Для каждого из них
Раскомментируйте вторую половину CTE, и вы получите 3x16.Это больше, чем количество записей в CTE, и оно только ухудшается с увеличением количества пользователей и событий.

Я бы посоветовал вам сделать что-то более ограничительное с 1 стороны вашей JOIN.Пример ниже:

WITH d1 AS (SELECT * FROM user_access_tab 
WHERE last_access >= 1544630400 AND last_access <= 1545661214),
d2 AS (
SELECT *
FROM d1 d
WHERE NOT EXISTS (SELECT FROM d1 WHERE item = d.item AND userid = d.userid AND d.last_access BETWEEN last_access+1 AND d.last_access + 3600))
SELECT d2.item, d2.userid, d1.userid, COUNT(*)
FROM d2
LEFT OUTER JOIN d1 ON d2.item = d1.item AND d2.userid = d1.userid AND d1.last_access BETWEEN d2.last_access and d2.last_access + 3600
GROUP BY d2.item, d2.userid, d1.userid

Очевидно, это изменит результат в столбце COUNT(*) (в дополнение к тому, что он быстрее), но так как раньше он не имел особого смысла, я бы сказал, что это длялучший.

...