Average Search Results Per User

An easy SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.

Domain
SQL
Difficulty
easy
Seniority
L3
Asked in
Airbnb

Problem

Some shoppers say search keeps coming back thin while others get pages of hits, so the personalization team wants it measured per person from the search_queries log: each signed-in user's average number of results returned, from the lowest user id to the highest. A user whose searches never logged a result count still belongs on the list, with an empty average.

How many results per searcher?

Tables

search_queries

Columns: query_id INTEGER, user_id INTEGER, search_term TEXT, results_count INTEGER, clicked_result INTEGER, query_time TEXT

search_queries: 10 of 200 rows
query_iduser_idsearch_termresults_countclicked_resultquery_time
2071100laptop deals1722026-02-02 01:11:00
2142197phone case iphone 153432026-03-03 02:22:00
2213294usb-c cable 6ft51NULL2026-04-04 03:33:00
2284NULLgaming mouse6852026-05-05 04:44:00
2355488mechanical keyboard8562026-06-06 05:55:00
24265854k monitor102NULL2026-07-07 06:06:00
2497682standing desk11982026-08-08 07:17:00
2568NULLwebcam 1080p13692026-09-09 08:28:00
2639876bluetooth speaker153NULL2026-01-10 09:39:00
2710973smart watch17012026-02-11 10:50:00

Expected output

First 5 of 75 rows
user_idavg_results
10017
19734
29451
48885
585102

Worked solution and explanation

What this really is

This is a per-user average with a ghost in the data. Anyone can write AVG(results_count) per user_id. What separates candidates is noticing that 50 of the 200 rows in search_queries have no user_id at all. Group straight away and those anonymous searches fold into one extra row with an empty user, averaging strangers together as if they were one shopper. Skip the per-user split entirely and you get one number for the whole log, which says nothing about which users see richer results.

Step 1: Pick the grain: one row per user

The question is about people, so the output grain is user_id. Everything lives on search_queries, so no join is needed. Grouping by query_id would hand back the 200 raw searches unchanged.

Step 2: Set aside anonymous searches first

WHERE user_id IS NOT NULL runs before grouping, so the anonymous rows never form a bucket. A NULL key is not a user, but GROUP BY still collects every NULL into a single group, and that group is exactly the phantom row.

Step 3: Average results_count inside each group

AVG skips NULL inputs, so a search that logged no count does not drag the mean toward zero. Four users have only uncounted searches. Their group survives with an empty avg_results, which is the honest answer: nobody knows how rich their results were.

Step 4: List by user_id ascending

The team reads the list from the lowest user id up. user_id is unique per output row, so ORDER BY user_id fully determines the sequence and needs no tiebreaker.

The solution

Average results per signed-in user
SELECT user_id, AVG(results_count) AS avg_results
FROM search_queries
WHERE user_id IS NOT NULL
GROUP BY user_id
ORDER BY user_id
Grouped straight away
GROUP BY user_id on the raw table returns 76 rows: the 75 real users plus one row whose user_id is NULL, averaging every anonymous search as if one person ran them all.
Filtered, then grouped
WHERE user_id IS NOT NULL first, then GROUP BY user_id: 75 rows, each summarizing one real user's own searches.

Common follow-up questions