cswork / libraryOpen practice workspace

Coding questions · Meta

Compute survey rates and bias-correct ratings

This question evaluates proficiency in data manipulation and analytics, specifically SQL window functions, joins, time-windowed de-duplication, aggregation of response and completion rates, and statistical reweighting to correct non-response bias.

Today is 2025-09-01. Use the schema and sample data below to answer A and B with SQL (standard SQL; you may use CTEs and window functions). Assume timestamps are UTC and exclude test users. Schema:

  • users(user_id INT, country STRING, is_test BOOLEAN)
  • survey_invites(invite_id INT, user_id INT, survey_id INT, sent_at TIMESTAMP)
  • survey_responses(response_id INT, user_id INT, survey_id INT, answered_at TIMESTAMP, is_complete BOOLEAN, rating INT)

Sample tables:

users
user_id | country | is_test
1 | US | false
2 | IN | false
3 | US | false
4 | BR | true
5 | IN | false
survey_invites
invite_id | user_id | survey_id | sent_at
100 | 1 | 10 | 2025-08-26 10:00:00
101 | 2 | 10 | 2025-08-27 09:00:00
102 | 3 | 10 | 2025-08-28 12:00:00
103 | 1 | 11 | 2025-08-20 08:00:00
104 | 5 | 10 | 2025-08-29 14:00:00
105 | 4 | 10 | 2025-08-28 15:00:00
106 | 2 | 11 | 2025-08-30 11:00:00
107 | 2 | 10 | 2025-08-27 10:00:00
survey_responses
response_id | user_id | survey_id | answered_at | is_complete | rating
200 | 1 | 10 | 2025-08-26 10:05:00 | true | 4
201 | 2 | 10 | 2025-08-30 09:20:00 | false | 3
202 | 2 | 10 | 2025-09-02 08:00:00 | true | 5
203 | 3 | 10 | 2025-08-28 13:00:00 | true | 2
204 | 5 | 10 | 2025-09-01 15:00:00 | true | 4
205 | 2 | 11 | 2025-08-30 12:00:00 | true | 5

A) For invites sent in the last 7 days (2025-08-25 through 2025-09-01 inclusive), compute per survey_id: response_rate and completion_rate. Rules: (i) Only non-test users; (ii) If a user receives multiple invites to the same survey, use the earliest invite in-window; (iii) Count a user as 'responded' if they have any response within 7 days after their earliest in-window invite and with answered_at <= 2025-09-01 23:59:59; (iv) 'Completed' requires is_complete = true under the same window. Return survey_id, invited_users, responded_users, completed_users, response_rate, completion_rate.

B) For survey_id = 10 over the same 7-day invite window, compute a country-reweighted average rating that matches the invite country distribution to correct non-response bias. Use only complete responses within 7 days after the user's earliest in-window invite and with answered_at <= 2025-09-01 23:59:59. Let w_country = (invite_share_country) / (response_share_country). Output weighted_avg_rating rounded to 3 decimals and a breakdown table with country, invites, responses, invite_share, response_share, weight, and weighted contribution.

Survey response rate and completion rate for last 7 days of invites

Assume the fixed 'current date' is 2025-06-01 (UTC). For invites sent from 2025-05-26 00:00:00 through 2025-06-01 23:59:59 (inclusive), compute per survey_id the response_rate and completion_rate.

Rules:

  1. Exclude test users (users.is_test = true).
  2. If a user receives multiple invites to the same survey within the invite window, use only the earliest invite within the window.
  3. Count a user as 'responded' if they have ANY response for that survey where answered_at is between their earliest in-window invite time and 7 days after it (inclusive), AND answered_at <= 2025-06-01 23:59:59.
  4. Count a user as 'completed' if they have ANY response meeting the responded window rules AND is_complete = true.

Return: survey_id, invited_users, responded_users, completed_users, response_rate, completion_rate.

  • response_rate = responded_users / invited_users
  • completion_rate = completed_users / invited_users

Table users

columntypeconstraints
user_idINTPRIMARY KEY, NOT NULL
countryVARCHAR(2)NOT NULL
is_testBOOLEANNOT NULL

Sample data:

countryis_testuser_id
USfalse1
INfalse2
USfalse3
BRtrue4
INfalse5

Table survey_invites

columntypeconstraints
invite_idINTPRIMARY KEY, NOT NULL
user_idINTNOT NULL
survey_idINTNOT NULL
sent_atTIMESTAMPNOT NULL

Sample data:

invite_idsent_atsurvey_iduser_id
1002025-05-26 10:00:00101
1012025-05-27 09:00:00102
1022025-05-28 12:00:00103
1032025-05-20 08:00:00111
1042025-05-29 14:00:00105
1052025-05-28 15:00:00104
1062025-05-30 11:00:00112
1072025-05-27 10:00:00102

Table survey_responses

columntypeconstraints
response_idINTPRIMARY KEY, NOT NULL
user_idINTNOT NULL
survey_idINTNOT NULL
answered_atTIMESTAMPNOT NULL
is_completeBOOLEANNOT NULL
ratingINTNOT NULL

Sample data:

answered_atis_completeratingresponse_idsurvey_iduser_id
2025-05-26 10:05:00true4200101
2025-05-30 09:20:00false3201102
2025-06-02 08:00:00true5202102
2025-05-28 13:00:00true2203103
2025-06-01 15:00:00true4204105
2025-05-30 12:00:00true5205112

Expected output

One row per survey_id that had at least one (deduped) in-window invite to a non-test user, with counts of invited/responded/completed users and the corresponding rates.

completed_userscompletion_rateinvited_usersresponded_usersresponse_ratesurvey_id
30.7544110
1111111

Hints

  1. Deduplicate invites first: MIN(sent_at) per (user_id, survey_id) within the invite window.
  2. Use EXISTS / flags to turn 'any response within 7 days' into 0/1 per user before aggregating.

Country reweighted average rating to correct non-response bias (survey_id = 10)

Assume the fixed 'current date' is 2025-06-01 (UTC). For invites sent from 2025-05-26 00:00:00 through 2025-06-01 23:59:59 (inclusive), compute a country-reweighted average rating for survey_id = 10 that matches the invite country distribution.

Rules:

  1. Exclude test users.
  2. If a user receives multiple invites to survey_id = 10 within the invite window, use only the earliest invite within the window.
  3. Use only COMPLETE responses (is_complete = true) within 7 days after the user's earliest in-window invite AND with answered_at <= 2025-06-01 23:59:59.
  4. Compute weights per country: w_country = (invite_share_country) / (response_share_country), where shares are computed over the invite set and response set respectively.

Output a breakdown table with:

  • country, invites, responses, invite_share, response_share, weight,
  • weighted_contribution (defined as weight * sum_of_ratings_in_country), and also include weighted_avg_rating (rounded to 3 decimals) as a column (it may repeat on each row).

Table users

columntypeconstraints
user_idINTPRIMARY KEY, NOT NULL
countryVARCHAR(2)NOT NULL
is_testBOOLEANNOT NULL

Sample data:

countryis_testuser_id
USfalse1
INfalse2
USfalse3
BRtrue4
INfalse5

Table survey_invites

columntypeconstraints
invite_idINTPRIMARY KEY, NOT NULL
user_idINTNOT NULL
survey_idINTNOT NULL
sent_atTIMESTAMPNOT NULL

Sample data:

invite_idsent_atsurvey_iduser_id
1002025-05-26 10:00:00101
1012025-05-27 09:00:00102
1022025-05-28 12:00:00103
1032025-05-20 08:00:00111
1042025-05-29 14:00:00105
1052025-05-28 15:00:00104
1062025-05-30 11:00:00112
1072025-05-27 10:00:00102

Table survey_responses

columntypeconstraints
response_idINTPRIMARY KEY, NOT NULL
user_idINTNOT NULL
survey_idINTNOT NULL
answered_atTIMESTAMPNOT NULL
is_completeBOOLEANNOT NULL
ratingINTNOT NULL

Sample data:

answered_atis_completeratingresponse_idsurvey_iduser_id
2025-05-26 10:05:00true4200101
2025-05-30 09:20:00false3201102
2025-06-02 08:00:00true5202102
2025-05-28 13:00:00true2203103
2025-06-01 15:00:00true4204105
2025-05-30 12:00:00true5205112

Expected output

One row per country for survey_id = 10, showing invite and (eligible complete) response distributions, the computed reweighting factor, per-country weighted contribution, and the final weighted average rating (repeated on each row).

countryinvite_shareinvitesresponse_shareresponsesweightweighted_avg_ratingweighted_contribution
IN0.520.33333311.53.56
US0.520.66666720.753.54.5

Hints

  1. Compute the invite distribution by country from deduped in-window invites; compute the response distribution by country from eligible complete responses.
  2. Use w_country = invite_share / response_share, then compute weighted_avg = SUM(w * rating) / SUM(w) across response rows (or equivalently via country-level sums).