Repeat Offenders of the Search Bar

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
Capital One

Problem

Each row of search_queries is one search, stamped with its query_time and the user_id that ran it, and logged-out searches all land under a blank user_id that is treated as a single searcher. How many searchers ran more than one search in 2026?

Once is a fluke. Twice is a habit.

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 1 of 1 rows
repeat_searcher_count
16

Worked solution and explanation

This is a count of groups that cleared a bar, dressed up as a search metric. Everyone writes GROUP BY user_id HAVING COUNT(*) > 1; what separates candidates is realizing that clause hands back a list of users, not a number, so a second aggregate has to count the survivors. The shortcut, COUNT(DISTINCT user_id) over the year, returns 75 instead of 16: it counts everyone who searched at all and the 'more than one' threshold silently does nothing.

Two aggregates stacked

Step 1: Restrict to 2026 before collapsing

The year filter has to run on raw rows, before any grouping, so a user's searches from earlier years never pad their total. Drop it and every one of the 75 seeded users has two searches across the whole table, so all of them qualify.

Step 2: Collapse to one row per user_id and keep the repeaters

GROUP BY user_id turns each searcher into one group, and HAVING COUNT(*) > 1 throws away the groups with a single search. This is a group-level test, which is exactly why it cannot live in WHERE.

Step 3: Count the surviving groups

The inner query returns one row per repeat searcher. Wrapping it and taking COUNT(*) turns that list into the single number the question asks for.

Count repeat searchers in 2026
SELECT COUNT(*) AS repeat_searcher_count
FROM (
    SELECT user_id
    FROM search_queries
    WHERE strftime('%Y', query_time) = '2026'
    GROUP BY user_id
    HAVING COUNT(*) > 1
)
One pass
COUNT(DISTINCT user_id) over the 2026 rows returns 75. It counts every searcher, skips the blank-ID bucket, and never applies the more-than-one test.
Group, filter, count
GROUP BY user_id HAVING COUNT(*) > 1, then COUNT(*) over the result, returns 16: the 15 named users with two searches plus the logged-out bucket.

Common follow-up questions