首页 > 试题广场 >

SaaS产品租户核心功能模块用量及占比分析

[编程题]SaaS产品租户核心功能模块用量及占比分析
  • 热度指数:439 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

一、【背景】

某企业级SaaS平台为不同行业的租户提供多个功能模块(如数据分析、用户管理、文件存储等)。产品团队需要分析每个租户最依赖的核心功能模块,并计算该模块的调用量在该租户总调用量中的占比,用于指导功能迭代优先级和个性化套餐推荐。

【表结构与字段说明】

表1:tenants(租户表)

  • tenant_id:INT,租户编号,主键
  • tenant_name:VARCHAR(50),租户名称
  • plan_type:VARCHAR(20),套餐类型(基础版/专业版/企业版/旗舰版)
  • industry:VARCHAR(30),所属行业

表2:usage_logs(功能调用日志表)

  • log_id:INT,日志编号,主键
  • tenant_id:INT,租户编号,关联 tenants.tenant_id
  • module_name:VARCHAR(30),功能模块名称
  • usage_date:DATE,调用日期
  • call_count:INT,当日调用次数(保证 >= 1)

二、问题

请使用 LATERAL JOIN 查询每个租户调用量最高的前 2 个功能模块及其在该租户总调用量中的占比。具体规则:先按模块汇总每个模块的总调用次数(total_calls),再取总调用次数最高的前2个模块,并计算每个模块的调用量占该租户所有模块总调用量的百分比(usage_pct)。

输出以下字段:租户名称(tenant_name)、套餐类型(plan_type)、模块名称(module_name)、模块总调用次数(total_calls)、调用占比百分比(usage_pct,即该模块总调用次数 / 该租户所有模块总调用次数 * 100,四舍五入保留2位小数)。结果按 tenant_id 升序排列,同一租户内按 total_calls 降序排列,若 total_calls 相同则按 module_name 升序排列。若某租户模块不足2个则显示全部模块;若某租户无调用记录则不出现在结果中。

三、示例数据表

tenants 表:

tenant_id tenant_name plan_type industry
1 星辰科技 企业版 互联网
2 蓝海教育 专业版 教育

usage_logs 表:

log_id tenant_id module_name usage_date call_count
1 1 数据分析 2025-03-01 150
2 1 数据分析 2025-03-02 200
3 1 用户管理 2025-03-01 80
4 1 用户管理 2025-03-02 120
5 1 文件存储 2025-03-01 50
6 1 文件存储 2025-03-02 30
7 2 在线课堂 2025-03-01 300
8 2 题库系统 2025-03-01 180
9 2 学员管理 2025-03-01 120

四、示例数据查询结果表

说明:

  • 星辰科技:数据分析=350,用户管理=200,文件存储=80,租户总计=630。Top2为数据分析和用户管理。
    • 数据分析占比:350/630*100=55.56
    • 用户管理占比:200/630*100=31.75
  • 蓝海教育:在线课堂=300,题库系统=180,学员管理=120,租户总计=600。Top2为在线课堂和题库系统。
    • 在线课堂占比:300/600*100=50.00
    • 题库系统占比:180/600*100=30.00
tenant_name plan_type module_name total_calls usage_pct
星辰科技 企业版 数据分析 350 55.56
星辰科技 企业版 用户管理 200 31.75
蓝海教育 专业版 在线课堂 300 50.00
蓝海教育 专业版 题库系统 180 30.00


示例1

输入

CREATE TABLE tenants (
    tenant_id INT PRIMARY KEY,
    tenant_name VARCHAR(50),
    plan_type VARCHAR(20),
    industry VARCHAR(30)
);

CREATE TABLE usage_logs (
    log_id INT PRIMARY KEY,
    tenant_id INT,
    module_name VARCHAR(30),
    usage_date DATE,
    call_count INT
);

INSERT INTO tenants VALUES
(1, '星辰科技', '企业版', '互联网'),
(2, '蓝海教育', '专业版', '教育');

INSERT INTO usage_logs VALUES
(1, 1, '数据分析', '2025-03-01', 150),
(2, 1, '数据分析', '2025-03-02', 200),
(3, 1, '用户管理', '2025-03-01', 80),
(4, 1, '用户管理', '2025-03-02', 120),
(5, 1, '文件存储', '2025-03-01', 50),
(6, 1, '文件存储', '2025-03-02', 30),
(7, 2, '在线课堂', '2025-03-01', 300),
(8, 2, '题库系统', '2025-03-01', 180),
(9, 2, '学员管理', '2025-03-01', 120);

输出

tenant_name|plan_type|module_name|total_calls|usage_pct
星辰科技|企业版|数据分析|350|55.56
星辰科技|企业版|用户管理|200|31.75
蓝海教育|专业版|在线课堂|300|50.00
蓝海教育|专业版|题库系统|180|30.00
with a as(
    select
    tenant_id
    ,module_name
    ,sum(call_count) total_calls
    ,round(sum(call_count)/sum(sum(call_count))over(partition by tenant_id)*100,2) usage_pct
    from usage_logs
    group by 1,2
)

select
tenant_name
,plan_type
,module_name
,total_calls
,usage_pct
from
(select
t.tenant_id
,tenant_name
,plan_type
,module_name
,total_calls
,usage_pct
from tenants t
join lateral (
    select
    module_name
    ,total_calls
    ,usage_pct
    from a
    where a.tenant_id = t.tenant_id
    order by total_calls desc
    limit 2
) b on true) c
order by tenant_id,total_calls desc,module_name
发表于 2026-04-19 18:20:45 回复(0)
select tenant_name,plan_type,module_name,total_calls,usage_pct from tenants join lateral(
select * from (
select usage_logs.tenant_id,module_name,sum(call_count) as total_calls,round(sum(call_count)/sum_id*100,2) as usage_pct from usage_logs left join (select tenant_id,sum(call_count) as sum_id from usage_logs group by tenant_id)e on e.tenant_id=usage_logs.tenant_id
group by usage_logs.tenant_id,module_name,sum_id)f
where f.tenant_id=tenants.tenant_id
order by total_calls desc
limit 2)t on true
order by t.tenant_id,total_calls desc, module_name
发表于 2026-04-12 15:01:02 回复(0)
select
    t.tenant_name,
    t.plan_type,
    t1.module_name,
    t1.total_calls,
    round(t1.total_calls/t1.total_calls_all*100, 2) as usage_pct
from tenants t
join lateral(
    select tenant_id, module_name,
        sum(call_count) as total_calls,
        sum(sum(call_count))over(partition by tenant_id) as total_calls_all,
        row_number()over(partition by tenant_id order by sum(call_count) desc) as rk
    from usage_logs ul
    group by tenant_id, module_name
)t1 on t.tenant_id = t1.tenant_id and t1.rk <= 2
发表于 2026-04-12 11:48:55 回复(0)
select tenant_name, plan_type, module_name, total_calls,
round(total_calls / sum_calls * 100, 2) usage_pct
from tenants t 
join lateral (
select *
from (
select tenant_id, module_name,
sum(call_count) total_calls,
sum(sum(call_count)) over(partition by tenant_id) sum_calls,
row_number() over(partition by tenant_id order by sum(call_count) desc) rnk
from usage_logs u 
group by tenant_id, module_name
) tb1 
where t.tenant_id = tb1.tenant_id and rnk <= 2
) tb2 on TRUE
order by t.tenant_id, total_calls desc, module_name;
发表于 2026-04-10 15:56:09 回复(0)
select tenant_name,plan_type,module_name,total_calls,
round(total_calls/calls*100,2) as usage_pct
from tenants t
join lateral 
(
select *
from
(select 
    tenant_id,
    module_name,
    sum(call_count) as total_calls,
    row_number() over(partition by tenant_id order by sum(call_count) desc,module_name) as rk,
    sum(sum(call_count)) over(partition by tenant_id) as calls
from usage_logs
group by 1,2) a
where t.tenant_id=a.tenant_id
and rk<=2
) e on true
order by t.tenant_id,total_calls desc,module_name

发表于 2026-04-05 16:02:36 回复(1)