更好的SQL在1小时的滑动窗口内链接记录

时间:2018-12-24 16:35:56

标签: hive count hbase aggregate-functions sliding-window

我尝试用(user1,user2,count)创建一个新表,以表示两个用户在一小时间隔内在一列中共享相同值的时间。

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

新表格如下所示。只要两个用户ID的last_access在1小时窗口内,它们就会链接在一起,并且计数器增加1。

enter image description here

1 个答案:

答案 0 :(得分:0)

恕我直言,您查询的问题是您之间的记录太多了。

以下面的最小示例为例,我插入了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中的记录数还多,并且只有随着更多的用户和事件而变得更糟。

我建议您在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(*)列中的结果(除了要更快),但是由于以前似乎没有多大意义,所以我认为这是最好的。