cswork / libraryOpen practice workspace

Coding questions · Meta

Compute unread and multi-account user percentages

This question evaluates understanding of SQL data manipulation skills such as aggregations, joins, NULL handling, deduplication reasoning, and user- versus person-level computations for accurate percentage metrics.

You’re given two tables. Write ANSI-SQL to answer parts (a)–(d). Treat a notification as unread if read_at IS NULL. Denominator for user-level percentages is all user_ids present in people_users (include users with zero notifications).

Schema and small samples:

people_users

person_iduser_id
110
111
220
330
440
441

notifications

notification_iduser_idcreated_atread_atnotification_type
100102025-08-30 10:00:00NULLmessage
101102025-08-31 12:00:002025-08-31 12:30:00mention
102112025-09-01 08:00:00NULLmessage
103202025-09-01 09:00:002025-09-01 09:05:00follow
104302025-08-28 15:00:00NULLmessage
105412025-08-29 17:00:002025-08-29 17:02:00like

Tasks: (a) What percentage of users have at least one unread notification right now? Return both numerator, denominator, and percentage with 2 decimals. (b) Break (a) down by notification_type (i.e., percentage of users who have ≥1 unread of each type). Users may appear in multiple types; report each type’s numerator, denominator, and percentage. (c) What percentage of persons (distinct person_id) have multiple accounts (>1 user_id)? Report both the percent of persons with multiple accounts and the percent of user_ids that belong to such multi-account persons. (d) Make your queries robust to duplicate rows in notifications (e.g., same notification_id accidentally logged twice). Explain your deduping assumption briefly in a SQL comment.

Percentage of users with at least one unread notification

You are given two tables: people_users (all users in the system) and notifications (notification events). Treat a notification as unread if read_at IS NULL.

Compute the percentage of users who have at least one unread notification.

  • The denominator for this user-level percentage is ALL distinct user_ids present in people_users (include users with zero notifications).
  • Return numerator (users with ≥1 unread), denominator (all users), and percentage rounded to 2 decimals.

Table people_users

columntypeconstraints
person_idINTNOT NULL
user_idINTNOT NULL

Sample data:

person_iduser_id
110
111
220
330
440
441

Table notifications

columntypeconstraints
log_idINTPRIMARY KEY, NOT NULL
notification_idINTNOT NULL
user_idINTNOT NULL
created_atTIMESTAMPNOT NULL
read_atTIMESTAMP
notification_typeVARCHAR(20)NOT NULL

Sample data:

created_atlog_idnotification_idnotification_typeread_atuser_id
2025-08-30 10:00:001100messageNULL10
2025-08-31 12:00:002101mention2025-08-31 12:30:0010
2025-09-01 08:00:003102messageNULL11
2025-09-01 09:00:004103follow2025-09-01 09:05:0020
2025-08-28 15:00:005104messageNULL30
2025-08-29 17:00:006105like2025-08-29 17:02:0041
2025-08-28 15:00:007104messageNULL30
2025-08-31 12:00:008101mention2025-08-31 12:30:0010

Expected output

One row with: numerator_users_with_unread, denominator_all_users, pct_users_with_unread (rounded to 2 decimals). Denominator counts distinct user_ids from people_users, including users with no notifications.

denominator_all_usersnumerator_users_with_unreadpct_users_with_unread
6350

Hints

  1. Denominator is all distinct user_ids from people_users (not from notifications).
  2. Use COUNT(DISTINCT user_id) for the numerator to avoid counting multiple notifications per user.

Unread-user percentage by notification type

Using the same tables and unread definition (read_at IS NULL), break down the result by notification_type.

For each notification_type, compute:

  • numerator: number of distinct users who have ≥1 unread notification of that type
  • denominator: number of distinct users in people_users (include users with zero notifications)
  • percentage: numerator / denominator * 100, rounded to 2 decimals

A user can appear in multiple types. Report one row per notification_type present in notifications (even if its numerator is 0).

Table people_users

columntypeconstraints
person_idINTNOT NULL
user_idINTNOT NULL

Sample data:

person_iduser_id
110
111
220
330
440
441

Table notifications

columntypeconstraints
log_idINTPRIMARY KEY, NOT NULL
notification_idINTNOT NULL
user_idINTNOT NULL
created_atTIMESTAMPNOT NULL
read_atTIMESTAMP
notification_typeVARCHAR(20)NOT NULL

Sample data:

created_atlog_idnotification_idnotification_typeread_atuser_id
2025-08-30 10:00:001100messageNULL10
2025-08-31 12:00:002101mention2025-08-31 12:30:0010
2025-09-01 08:00:003102messageNULL11
2025-09-01 09:00:004103follow2025-09-01 09:05:0020
2025-08-28 15:00:005104messageNULL30
2025-08-29 17:00:006105like2025-08-29 17:02:0041
2025-08-28 15:00:007104messageNULL30
2025-08-31 12:00:008101mention2025-08-31 12:30:0010

Expected output

One row per notification_type in notifications with numerator (distinct users with ≥1 unread of that type), denominator (distinct users from people_users), and percent rounded to 2 decimals.

denominator_all_usersnotification_typenumerator_users_with_unreadpct_users_with_unread
6follow00
6like00
6mention00
6message350

Hints

  1. Compute the denominator once from people_users and reuse it for every type.
  2. To include types with 0 unread users, build a list of all types then LEFT JOIN unread users.

Multi-account person percentages

Using people_users, answer both of the following:

  1. What percentage of persons (distinct person_id) have multiple accounts (> 1 user_id)?
  2. What percentage of user_ids belong to persons who have multiple accounts?

Return:

  • multi_account_persons, total_persons, pct_persons_multi_account (2 decimals)
  • user_ids_in_multi_account_persons, total_user_ids, pct_user_ids_in_multi_account_persons (2 decimals)

Table people_users

columntypeconstraints
person_idINTNOT NULL
user_idINTNOT NULL

Sample data:

person_iduser_id
110
111
220
330
440
441

Expected output

One row containing both the person-level and user-level percentages related to multi-account persons, rounded to 2 decimals.

multi_account_personspct_persons_multi_accountpct_user_ids_in_multi_account_personstotal_personstotal_user_idsuser_ids_in_multi_account_persons
25066.67464

Hints

  1. First compute how many user_ids each person_id has, then filter to those with > 1.
  2. For the user_id percentage, count distinct user_ids belonging to multi-account persons divided by all distinct user_ids.

Robust unread-user percentage with notification deduplication

The notifications table may contain accidental duplicate rows for the same real notification (e.g., the same notification_id logged twice).

Write an ANSI-SQL query that returns the same output as question (a): numerator, denominator, and percent of users with ≥1 unread notification, BUT make it robust to duplicates in notifications.

Add a brief SQL comment explaining your deduplication assumption (e.g., how you choose which row to keep per notification_id).

Table people_users

columntypeconstraints
person_idINTNOT NULL
user_idINTNOT NULL

Sample data:

person_iduser_id
110
111
220
330
440
441

Table notifications

columntypeconstraints
log_idINTPRIMARY KEY, NOT NULL
notification_idINTNOT NULL
user_idINTNOT NULL
created_atTIMESTAMPNOT NULL
read_atTIMESTAMP
notification_typeVARCHAR(20)NOT NULL

Sample data:

created_atlog_idnotification_idnotification_typeread_atuser_id
2025-08-30 10:00:001100messageNULL10
2025-08-31 12:00:002101mention2025-08-31 12:30:0010
2025-09-01 08:00:003102messageNULL11
2025-09-01 09:00:004103follow2025-09-01 09:05:0020
2025-08-28 15:00:005104messageNULL30
2025-08-29 17:00:006105like2025-08-29 17:02:0041
2025-08-28 15:00:007104messageNULL30
2025-08-31 12:00:008101mention2025-08-31 12:30:0010

Expected output

One row with numerator, denominator, and percentage (2 decimals). The result should not change if duplicate rows exist for the same notification_id.

denominator_all_usersnumerator_users_with_unreadpct_users_with_unread
6350

Hints

  1. Use a window function like ROW_NUMBER() to pick one row per notification_id.
  2. After deduplication, compute the unread-user numerator using DISTINCT user_id.