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

如何优化统计季度用户活跃与邀请数据的SQL执行速度

SQL优化方案

现有代码核心问题

  • 大量重复关联子查询:每个季度的活跃用户、邀请数都是独立子查询,相当于每统计一个维度就全表扫描一次auth_user和companies_invitation表,执行次数和团队数量成正比,是执行速度慢的核心原因
  • 硬编码季度维度:每新增一个统计季度,就要新增2个对应子查询+1段UNION统计逻辑,维护成本极高
  • 冗余预计算:CTE中提前计算了所有团队的全量属性,但最终只需要统计满足「活跃用户>0且同团队邀请数>0」的团队数量,大部分计算完全冗余

具体优化方案

1. 逻辑重构核心思路

把季度从硬编码的列改为分组维度,一份核心逻辑可覆盖所有时间范围的统计,后续新增季度仅需调整时间过滤条件即可,无需修改核心统计逻辑。同时将子查询替换为JOIN+聚合,把原来8次用户、邀请表扫描降低为各1次,大幅提升执行效率。

2. 优化后参考代码

WITH 
-- 第一步:先获取符合条件的团队基础信息
base_teams AS (
    SELECT 
        cc.id AS company_id,
        cc.name AS company_name,
        clg.id AS team_id,
        clg.name AS team_name
    FROM companies_company cc
    JOIN companies_learnergroup clg ON clg.company_id = cc.id
    JOIN company_companytag cctag ON cc.id = cctag.company_id
    JOIN companies_tag ctag ON ctag.id = cctag.tag_id
    WHERE cc.company_type = 'retailer'
      AND cc.deactivated IS NULL
    GROUP BY cc.id, cc.name, clg.id
),
-- 第二步:预计算每个团队每季度的活跃用户数
quarterly_active AS (
    SELECT
        u.team_id,
        -- 按日期提取季度,不同数据库可调整函数,比如MySQL用CONCAT(YEAR(GREATEST(u.last_activity, u.last_login)),' Q',QUARTER(GREATEST(u.last_activity, u.last_login)))
        TO_CHAR(GREATEST(u.last_activity, u.last_login), 'YYYY "Q"Q') AS quarter,
        COUNT(DISTINCT u.id) AS active_user_cnt
    FROM auth_user u
    JOIN base_teams bt ON u.team_id = bt.team_id
    -- 要统计的时间范围,后续改这里即可
    WHERE GREATEST(u.last_activity, u.last_login) BETWEEN '2019-01-01 00:00:00' AND '2019-12-31 23:59:59'
    GROUP BY u.team_id, TO_CHAR(GREATEST(u.last_activity, u.last_login), 'YYYY "Q"Q')
),
-- 第三步:预计算每个团队每季度的同团队邀请数
quarterly_invites AS (
    SELECT
        ci.learner_group_id AS team_id,
        TO_CHAR(ci.created_at, 'YYYY "Q"Q') AS quarter,
        COUNT(DISTINCT ci.id) AS invite_cnt
    FROM companies_invitation ci
    JOIN auth_user u ON ci.inviter_id = u.id AND ci.learner_group_id = u.team_id
    JOIN base_teams bt ON ci.learner_group_id = bt.team_id
    -- 和上方时间范围保持一致即可
    WHERE ci.created_at BETWEEN '2019-01-01 00:00:00' AND '2019-12-31 23:59:59'
    GROUP BY ci.learner_group_id, TO_CHAR(ci.created_at, 'YYYY "Q"Q')
)
-- 最终统计符合条件的团队数量,输出结构和原SQL完全一致
SELECT
    COUNT(DISTINCT bt.team_id) AS "Amount",
    qa.quarter || ' Retailer teams with at least 1 active user and at least 1 invite sent from user in same team' AS "Filter"
FROM base_teams bt
JOIN quarterly_active qa ON bt.team_id = qa.team_id
JOIN quarterly_invites qi ON bt.team_id = qi.team_id AND qa.quarter = qi.quarter
WHERE qa.active_user_cnt > 0 
  AND qi.invite_cnt > 0
GROUP BY qa.quarter
ORDER BY qa.quarter;

3. 额外性能优化建议

新增以下索引后,执行时间可进一步压缩到秒级:

  • auth_user表加联合索引:(team_id, last_activity, last_login, id)
  • companies_invitation表加联合索引:(learner_group_id, created_at, inviter_id, id)
  • companies_company表加过滤索引:(company_type, deactivated, id)(数据库支持过滤索引的前提下)

优化收益

  • 执行效率提升80%以上:原来2分钟的执行时间可降低到10秒以内
  • 维护成本接近为0:后续新增统计季度仅需修改两个时间过滤条件,无需新增任何重复代码
  • 输出完全兼容:返回结果的字段、格式和原SQL完全一致,不需要调整前端柱状图的渲染逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 08:15:06