You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效实现按周分组并统计用户总流量及最频繁位置?

周维度用户总流量与高频位置统计的SQL优化

原数据表

eventdate   userid  traffic location    
18.09.2023  user_1  10      A
18.09.2023  user_1  20      A
18.09.2023  user_2  10      B
18.09.2023  user_2  20      B
18.09.2023  user_2  30      B
18.09.2023  user_3  100     A
19.09.2023  user_1  50      B
19.09.2023  user_2  10      B
19.09.2023  user_2  20      B
19.09.2023  user_3  150     C
19.09.2023  user_3  250     C
20.09.2023  user_1  50      A
20.09.2023  user_1  20      A
20.09.2023  user_2  30      B
20.09.2023  user_3  110     C
20.09.2023  user_3  120     C

需求说明

查询结果需包含:

  • 每周起始日期eventdate
  • 每周唯一用户ID userid
  • 总流量traffic
  • 该周用户出现最频繁的位置location

示例结果:

eventdate   userid  traffic location
18.09.2023  user_1  150    A
18.09.2023  user_2  120    B
18.09.2023  user_3  730    C

现有实现SQL

SELECT t1.eventdate, t1.userid, t1.traffic, t2.location
  FROM (SELECT TO_CHAR(TRUNC(TO_DATE('2023-09-18', 'yyyy-mm-dd'), 'IW'),
                       'yyyy-mm-dd') AS eventdate,
               tk.userid,
               SUM(tk.traffic) AS traffic
          FROM test_kt tk
         GROUP BY tk.userid) t1
  JOIN (
         WITH cte AS 
        (
         SELECT tk2.userid,
                tk2.location,
                ROW_NUMBER() OVER 
                (PARTITION BY tk2.userid ORDER BY COUNT(tk2.location) DESC) rn
           FROM test_kt tk2
          GROUP BY tk2.userid, tk2.location
        )
        SELECT userid, location 
          FROM cte 
         WHERE rn = 1
       ) t2 
    ON t1.userid = t2.userid;

优化方案

现有SQL存在两个核心问题:一是硬编码固定周起始日期,未按实际数据的周维度分组统计;二是对表进行了两次全表扫描,IO开销较高。以下是更高效的实现方式:

优化后的SQL(方式一:单次扫描+窗口函数)

WITH user_weekly_stats AS (
    SELECT
        -- 转换日期格式并计算每周起始日期
        TO_CHAR(TRUNC(TO_DATE(eventdate, 'dd.mm.yyyy'), 'IW'), 'dd.mm.yyyy') AS week_start_date,
        userid,
        location,
        -- 计算用户每周总流量
        SUM(traffic) OVER (PARTITION BY userid, TRUNC(TO_DATE(eventdate, 'dd.mm.yyyy'), 'IW')) AS total_traffic,
        -- 统计每个位置在用户每周数据中的出现次数
        COUNT(*) OVER (PARTITION BY userid, TRUNC(TO_DATE(eventdate, 'dd.mm.yyyy'), 'IW'), location) AS loc_count,
        -- 按用户每周分组,按位置出现次数降序排序,标记最高频位置
        ROW_NUMBER() OVER (PARTITION BY userid, TRUNC(TO_DATE(eventdate, 'dd.mm.yyyy'), 'IW') ORDER BY COUNT(*) OVER (PARTITION BY userid, TRUNC(TO_DATE(eventdate, 'dd.mm.yyyy'), 'IW'), location) DESC) AS rn
    FROM test_kt
)
SELECT DISTINCT
    week_start_date AS eventdate,
    userid,
    total_traffic AS traffic,
    location
FROM user_weekly_stats
WHERE rn = 1
ORDER BY eventdate, userid;

优化后的SQL(方式二:分组后筛选)

WITH user_weekly_loc AS (
    SELECT
        TO_CHAR(TRUNC(TO_DATE(eventdate, 'dd.mm.yyyy'), 'IW'), 'dd.mm.yyyy') AS week_start_date,
        userid,
        location,
        SUM(traffic) AS total_traffic,
        COUNT(*) AS loc_occurrences,
        -- 按用户每周分组,筛选出现次数最多的位置
        ROW_NUMBER() OVER (PARTITION BY userid, week_start_date ORDER BY loc_occurrences DESC) AS rn
    FROM test_kt
    GROUP BY userid, week_start_date, location
)
SELECT
    week_start_date AS eventdate,
    userid,
    total_traffic AS traffic,
    location
FROM user_weekly_loc
WHERE rn = 1
ORDER BY eventdate, userid;

优化亮点

  • 减少IO开销:仅对表进行一次全表扫描,避免原SQL两次扫描的重复开销,数据量越大效率提升越明显。
  • 符合需求逻辑:正确按周维度分组统计,不再依赖硬编码的固定日期。
  • 逻辑紧凑:在同一计算流程中完成总流量统计与高频位置筛选,执行计划更高效。

额外优化建议

如果表数据量较大,建议创建复合索引加速分组和窗口函数计算:

CREATE INDEX idx_test_kt_user_date_loc ON test_kt(userid, eventdate, location);

内容的提问来源于stack exchange,提问作者Koke Abeke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 15:58:12