Log Entries by Level
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
- Yelp
Problem
The SRE dashboard needs a count of server log entries at each log level, alphabetically by level.
Info, warn, error, fatal. The breakdown matters.
Tables
server_logs
Columns: log_id INTEGER, server_name TEXT, log_level TEXT, message TEXT, response_time_ms INTEGER, log_timestamp TEXT
| log_id | server_name | log_level | message | response_time_ms | log_timestamp |
|---|---|---|---|---|---|
| 137 | web-prod-02 | WARN | Connection timeout after 30s | 23 | 2026-02-02 01:07:13 |
| 174 | api-prod-01 | ERROR | Database query slow: 2.5s | NULL | 2026-03-03 02:14:26 |
| 211 | api-prod-02 | DEBUG | Cache miss for key user_123 | 49 | 2026-04-04 03:21:39 |
| 248 | db-primary | CRITICAL | Memory usage at 85% | 0 | 2026-05-05 04:28:52 |
| 285 | db-replica-01 | INFO | SSL certificate expires in 7 days | NULL | 2026-06-06 05:35:05 |
| 322 | cache-01 | WARN | Rate limit exceeded for IP 192.168.1.x | 88 | 2026-07-07 06:42:18 |
| 359 | worker-01 | ERROR | Disk space low: 10% remaining | 101 | 2026-08-08 07:49:31 |
| 396 | cdn-edge-us | DEBUG | New deployment v2.3.1 started | NULL | 2026-09-09 08:56:44 |
| 433 | cdn-edge-eu | CRITICAL | Health check passed | 127 | 2026-01-10 09:03:57 |
| 470 | monitor-01 | INFO | Failed to connect to upstream | 140 | 2026-02-11 10:10:10 |
Expected output
| log_level | entry_count |
|---|---|
| CRITICAL | 40 |
| DEBUG | 40 |
| ERROR | 40 |
| INFO | 40 |
| WARN | 40 |
Worked solution and explanation
This is a grain question wearing a dashboard costume. The whole job is deciding that the answer has one row per log_level, and that each row counts every line in server_logs, not something about the lines. Nobody fumbles the syntax. The slip is in the count: reach for COUNT(DISTINCT message) because 'entries' sounds like unique alerts, and every level reads 19 instead of 40, because the same message repeats inside a level. Lose the per-level grain entirely and you hand the dashboard a single 200 it cannot plot.
Walk the grain first
Step 1: Name the grain before the query
Say it out loud: one row per log level. log_level holds five values on this data ('CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'WARN'), so the answer is five rows, and GROUP BY log_level is what produces exactly that shape.
Step 2: Count lines, not messages
COUNT(*) counts every row in the group. A repeated alert is still a separate log line, and message repeats inside every level, so COUNT(DISTINCT message) quietly reports 19 where the truth is 40.
Step 3: Sort by the name, not the number
The listing is alphabetical, so ORDER BY log_level ASC. Every level happens to hold 40 lines here, so sorting by entry_count would hand back the five rows in whatever order the engine pleases.
SELECT log_level, COUNT(*) AS entry_count
FROM server_logs
GROUP BY log_level
ORDER BY log_level ASC- Counts unique messages
COUNT(DISTINCT message)collapses repeated alerts, so each level shows 19: the number of different texts, not the volume the dashboard is tracking.- Counts every line
COUNT(*)counts each row ofserver_logsonce, so each level shows its true 40 lines, repeats included.
Common follow-up questions
- How would you count only the last seven days, and what does partitioning on `log_timestamp` buy you there? (Tests partition pruning on a time-partitioned log table.)
- Severity order is CRITICAL, ERROR, WARN, INFO, DEBUG. How would you list the levels that way instead of alphabetically? (Tests mapping a categorical value to a sort key.)
- How would you add each level's share of all log lines next to `entry_count`? (Tests combining a per-group count with a whole-table total.)
- What changes if `log_level` arrives in mixed case, such as 'warn' and 'WARN'? (Tests normalizing a grouping key before counting.)