SQL Server 如何计算至少访问过一次各应用的用户占比?
使用 COUNT(DISTINCT UserID) 统计每个应用的访问用户数,再除以 Users 表中的不同用户总数。分母包含没有访问记录的用户,同一用户多次访问同一应用只计一次。
查询示例
查询沿用原问题中的表名 WepAppAuditLogs,并假设三张表都位于 dbo schema 下。执行时请按实际表名和 schema 调整。
WITH UserTotal AS (
SELECT COUNT(DISTINCT UserID) AS TotalUsers
FROM dbo.Users
)
SELECT
a.AppName,
CAST(
100.0 * COUNT(DISTINCT u.UserID)
/ NULLIF(t.TotalUsers, 0)
AS decimal(5, 2)
) AS PercentOfUsers
FROM dbo.WebApps AS a
CROSS JOIN UserTotal AS t
LEFT JOIN dbo.WepAppAuditLogs AS l
ON l.AppID = a.AppID
LEFT JOIN dbo.Users AS u
ON u.UserID = l.UserID
GROUP BY
a.AppID,
a.AppName,
t.TotalUsers
ORDER BY
a.AppName;
根据给出的示例数据,查询结果为:
| AppName | PercentOfUsers |
|---|---|
| Analytics | 75.00 |
| Security | 25.00 |
| User Management | 50.00 |
PercentOfUsers 返回百分数数值,例如 75.00 表示 75%,可由展示层添加 %。
为什么这样计算
计算公式为:
用户使用率 = 至少访问过该应用一次的不同用户数 / 全部不同用户数 × 100%
示例中共有 4 名用户:
Analytics的访问用户为 1、2、3,占比为3 / 4 × 100% = 75%。Security的访问用户只有 2,占比为1 / 4 × 100% = 25%。两条访问日志属于同一个用户。User Management的访问用户为 1、3,占比为2 / 4 × 100% = 50%。
查询先从 Users 中独立计算用户总数,再按应用统计访问用户。通过关联 Users,日志中无法匹配当前用户表的用户 ID 不会计入分子,使分子和分母使用相同的用户范围。
去重、零访问和空用户表的处理
COUNT(DISTINCT u.UserID) 会对用户去重并忽略 NULL。这里不能使用 COUNT(*):它统计的是连接后的行数,重复访问会增加计数,而应用没有访问记录时,LEFT JOIN 也会保留一行。
查询以 WebApps 为起点,使用 LEFT JOIN 保留没有访问记录的应用。只要用户总数大于零,这些应用的占比就是 0.00。
表达式中的 100.0 让计算保留小数,避免整数除法截断;CAST(... AS decimal(5, 2)) 将结果保留两位小数。
NULLIF(t.TotalUsers, 0) 用于避免除以零。当用户总数为零时,占比返回 NULL,表示没有可计算的用户基数。
分组同时包含 AppID 和 AppName,即使两个应用名称相同,也会分别统计。
统计范围
这条查询使用全部访问日志,没有按 Date 限定时间范围。即使用户从未访问任何应用,也仍然包含在分母中。
如果需要统计某个时间段内的使用率,可以在日志的 LEFT JOIN ... ON 条件中增加日期限制。将日志日期条件直接放进 WHERE,可能会过滤掉没有匹配日志的应用,使它们无法显示为零访问。
备注:内容仅供参考。
