PAYATHON 2026

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;

根据给出的示例数据,查询结果为:

AppNamePercentOfUsers
Analytics75.00
Security25.00
User Management50.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,可能会过滤掉没有匹配日志的应用,使它们无法显示为零访问。

备注:内容仅供参考。