Question Details

No question body available.

Tags

sql sql-server sql-server-2019

Answers (4)

March 26, 2026 Score: 5 Rep: 10,909 Quality: Medium Completeness: 100%

You can break down the task into several steps:

  1. Count the distinct users that have accessed each application. This can be accomplished with a combination of COUNT(DISTINCT UserID) and GROUP BY AppName.
  2. Count the total number of users.
  3. Combine the two to calculate percentages.

One of several ways to write this query is:

SELECT
    UAC.AppName,
    UAC.UserAccessCount  100.00 / UC.UserCount AS [% of users that have used app]
FROM (
    SELECT AAL.AppName, COUNT(DISTINCT AAL.UserID) AS UserAccessCount
    FROM AppAuditLog AAL
    GROUP BY AAL.AppName
) UAC
CROSS JOIN (
    SELECT COUNT() AS UserCount
    FROM Users
) UC
ORDER BY UAC.AppName

The above could also be rearranged to use CTEs (common table expressions):

WITH UserCount AS (
    SELECT COUNT() AS UserCount
    FROM Users
),
UserAccessCounts AS (
    SELECT AAL.AppName, COUNT(DISTINCT AAL.UserID) AS UserAccessCount
    FROM AppAuditLog AAL
    GROUP BY AAL.AppName
)
SELECT
    UAC.AppName,
    UAC.UserAccessCount  100.00 / UC.UserCount AS [% of users that have used app]
FROM UserAccessCounts UAC
CROSS JOIN UserCount UC
ORDER BY UAC.AppName

Results:

AppName % of users that have used app
Analytics 75.0000000000000
Security 25.0000000000000
User Management 50.0000000000000

You can round or cast the result to achieve a specific desired precision. If you are running this query from an application, it is common (best?) practice just to leave the result as a simple fraction (0.75, etc.) and perform the scaling and rounding at the presentation layer. You would still need to cast the UserAccessCount value to a FLOAT, DECIMAL, or NUMERIC type before the divide to avoid integer division.

You can add date filters as needed, if you wish you report on accesses for specific time periods.

Note that this query will omit any apps that have no accesses. This can be fixed if you have a separate Apps table (which is why CharlieFace was asking in the comments). You could change the UserAccessCount part of the query to use a LEFT JOIN as follows:

SELECT A.AppName, COUNT(DISTINCT AAL.UserID) AS UserAccessCount
FROM Apps A
LEFT JOIN AppAuditLog AAL
    ON AAL.AppName = A.AppName
GROUP BY A.AppName

See this dbfiddle for a demo.

March 26, 2026 Score: 1 Rep: 28,109 Quality: Low Completeness: 70%

Divide the count of distinct userids per group by the total user count. You can get the total user count with a subquery on the Users table.

SELECT
    wa.AppName,
    COUNT(DISTINCT waal.UserID)  100.0 / (SELECT COUNT() FROM Users) AS UserUsagePercentage
FROM WebApps wa
    LEFT JOIN WebAppAuditLogs waal
        ON waal.AppID = wa.AppID
GROUP BY wa.AppName

Results:

AppName UserUsagePercentage
Analytics 75.0000000000000
Security 25.0000000000000
User Management 50.0000000000000

dbfiddle demo

March 26, 2026 Score: 1 Rep: 81,361 Quality: Low Completeness: 50%

A way to achieve it is with subqueries, like

select t2.AppName, 100  count() / cnt
(
    select count(*) as cnt
    from Users
) t
join
(
    select WebApps.AppName, Users.UserID
    from Users
    join WebAppAuditlogs
    on Users.UserID = WebAppAuditLogs.UserID
    join WebApps
    on WebAppAuditlogs.AppID = WebApps.AppID
    group by WebApps.AppName, Users.UserID
) t2
on 1 = 1
group by t2.AppName

So t is the count for users, t2 is a mapping between Apps and Users upon user visit and grouped by app and user, then joining the two together and compute the percentage for the app.

March 26, 2026 Score: 1 Rep: 8,583 Quality: Low Completeness: 100%

First, subquery "uc" take distinct pairs AppId-UserId. One UserId for every AppId.
Then count UserId's for AppId - group by AppId and divide by total users count.

AppName taked by subquery, since "WebApps" should be little table. It might be better than doing a JOIN.

SELECT AppId
  ,(SELECT AppName FROM WebApps a WHERE a.AppId=uc.AppId) AppName
  ,count()100.0 /(SELECT count() FROM Users) as [ % of users that have used app]
from(
  SELECT DISTINCT AppId,UserID
  FROM WebAppAuditLogs
  WHERE date BETWEEN '2026-01-01' AND '2026-01-31' 
)uc
GROUP BY AppId;

I assume that the query execution plan will be like this:

  • scan(index scan) WebAppAuditLogs and filter by date
  • sort by (AppId,UserId) and output DISTINCT values
  • calculate count() from Users
  • aggregate by AppId
  • INNER JOIN with WebApps

Output

AppId       AppName         % of users that have used app
----------- --------------- -----------------------------
          1 User Management               50.000000000000
          2 Analytics                     75.000000000000
          3 Security                      25.000000000000

Case when you want show all App's, including not presented in log,
JOIN calculated rows to main table WebApps

SELECT a.AppId,a.AppName,coalesce(pct,0.0) as  [% of users that have used app]
FROM WebApps a
LEFT JOIN 
  (SELECT AppId ,count()100.0 /(SELECT count(*) FROM Users) as pct
  FROM(
    SELECT DISTINCT AppId,UserID
    FROM WebAppAuditLogs
    WHERE date BETWEEN '2026-01-01' AND '2026-01-31' 
    )uc
  GROUP BY AppId
)lc ON lc.AppId=a.AppId
;

Output example

AppId       AppName         % of users that have used app
----------- --------------- -----------------------------
    1   User Management               50.000000000000
    2   Analytics                     75.000000000000
    3   Security                      25.000000000000
    4   Tests                           .000000000000

Example