Frequent Message Senders

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

Domain
SQL
Difficulty
medium
Seniority
L3
Asked in
Amazon

Problem

Our anomaly-detection team is hunting accounts that flooded the chat platform this year, where only messages carrying actual text count as real activity (blank posts are just noise). Surface every sender behind more than two such messages, along with that count.

Someone is sending too many messages.

Tables

chat_msgs

Columns: msg_id INTEGER, channel TEXT, sender_id INTEGER, content TEXT, msg_type TEXT, sent_at TEXT, edited INTEGER, reply_to INTEGER

chat_msgs: 10 of 200 rows
msg_idchannelsender_idcontentmsg_typesent_ateditedreply_to
147#engineering197Anyone seeing increased latency?file2026-02-02 01:07:000NULL
194#incidents294PR #412 ready for reviewreaction2026-03-03 02:14:000NULL
241#random391Dashboard is downthread_reply2026-04-04 03:21:000NULL
288#deployments488Fixed the flaky testsystem2026-05-05 04:28:000NULL
335#data-team585Merged the migrationText2026-06-06 05:35:000NULL
382#general682NULLFILE2026-07-07 06:42:000NULL
429#engineering779LGTMtext2026-08-08 07:49:000NULL
476#incidents876👍file2026-09-09 08:56:0005
523#random973reaction2026-01-10 09:03:000NULL
570#deployments1070Can someone check the pipeline?thread_reply2026-02-11 10:10:000NULL

Expected output

First 5 of 7 rows
sender_idmsg_count
1004
5854
10704
15554
20404

Worked solution and explanation

What this is really asking

This is a two-phase filter wearing an anomaly-detection costume. You filter rows FIRST (keep only this year's messages that carry text), collapse them to one row per sender_id, count what survived, then filter the GROUPS down to those above two. A single WHERE cannot say 'more than two surviving messages' because that count does not exist until the rows are grouped. The trap is knowing which filter runs at which phase: mix them up and you either crash or count the wrong rows.


Break it down

Step 1: Filter the rows in WHERE

content <> '' drops empty and null content (in SQL, NULL <> '' is not true, so those rows fall out too), and sent_at >= the start of the year drops older messages. This runs before grouping, so only qualifying rows ever reach the count.

Step 2: Aggregate per sender

GROUP BY sender_id with COUNT(*) yields one row per sender carrying their surviving message total across every channel. It is COUNT(*), not COUNT(content): after the WHERE there are no nulls left, and every remaining row is one real message.

Step 3: Filter the groups in HAVING

The more-than-two test reads COUNT(*), which does not exist until the group forms, so it belongs in HAVING. A sender with only one or two qualifying messages this year is dropped here, leaving just the accounts that are actually flooding.

Step 4: Order busiest first

ORDER BY msg_count DESC puts the loudest senders first. Counts tie (many senders share the same total), so sender_id ascending is the tiebreaker that makes the order total and reproducible.


FREQUENT SENDERS
SELECT
    sender_id,
    COUNT(*) AS msg_count
FROM chat_msgs
WHERE content <> ''
  AND sent_at >= '2026-01-01'
GROUP BY sender_id
HAVING COUNT(*) > 2
;

COMMON FOLLOW-UP QUESTIONS

Common follow-up questions