Analyze video flags and reviews with SQL
Company: Google
Role: Data Scientist
Category: Data Manipulation (SQL/Python)
Difficulty: medium
Interview Round: Online Assessment
You are designing SQL queries for YouTube Trust & Safety. Use the schema and sample data below. Unless stated otherwise, treat a flag as reviewed if there exists a Reviews row for that flag with a non-NULL reviewed_outcome. If a user flags the same video multiple times, count that user at most once per video when asked for distinct-user counts. If multiple items tie for a maximum, return all ties. Schema:
- Users(user_id PK, first_name, last_name)
- Videos(video_id PK, title)
- Flags(flag_id PK, user_id FK->Users.user_id, video_id FK->Videos.video_id, flagged_at TIMESTAMP)
- Reviews(flag_id PK FK->Flags.flag_id, reviewed_date DATE, reviewed_outcome VARCHAR CHECK(reviewed_outcome IN ('APPROVED','REJECTED','ESCALATED') OR reviewed_outcome IS NULL))
Sample rows (minimal, illustrative):
Users
+---------+------------+-----------+
| user_id | first_name | last_name |
+---------+------------+-----------+
| 1 | Alice | Zhang |
| 2 | Bob | Li |
| 3 | Carol | Chen |
+---------+------------+-----------+
Videos
+----------+----------------+
| video_id | title |
+----------+----------------+
| 10 | Cat Tricks |
| 11 | Dog Tricks |
| 12 | Bird Tricks |
+----------+----------------+
Flags
+---------+---------+----------+---------------------+
| flag_id | user_id | video_id | flagged_at |
+---------+---------+----------+---------------------+
| 100 | 1 | 10 | 2025-08-30 10:00:00 |
| 101 | 1 | 10 | 2025-08-31 09:00:00 |
| 102 | 2 | 10 | 2025-08-31 12:00:00 |
| 103 | 2 | 11 | 2025-09-01 08:30:00 |
| 104 | 3 | 10 | 2025-09-01 11:45:00 |
| 105 | 3 | 12 | 2025-09-01 12:00:00 |
+---------+---------+----------+---------------------+
Reviews
+---------+---------------+------------------+
| flag_id | reviewed_date | reviewed_outcome |
+---------+---------------+------------------+
| 100 | 2025-09-02 | APPROVED |
| 101 | 2025-09-02 | REJECTED |
| 102 | 2025-09-03 | APPROVED |
| 103 | 2025-09-03 | APPROVED |
| 104 | NULL | NULL |
+---------+---------------+------------------+
Tasks — write standard SQL for each:
1) Count distinct-user flagging per video: Return video_id and distinct_user_flaggers for every video, where a user who flagged the same video multiple times counts once. Also include total_flags for that video (count of all Flags rows) as a second column. Order by distinct_user_flaggers DESC, then total_flags DESC, then video_id ASC.
2) Most-flagged video(s) and reviewed-flag count: Find the video_id(s) with the highest total number of flags. For those video_id(s), return video_id, total_flags, reviewed_flags where reviewed_flags counts Flags that have a Reviews row with non-NULL reviewed_outcome. If multiple videos tie for total_flags, return all ties.
3) Top user by approved-video count: Which user flagged the most distinct videos that eventually got APPROVED? Count each (user_id, video_id) at most once even if the user flagged that video multiple times; consider a video approved-by-user if there exists at least one of the user’s flags on that video with reviewed_outcome = 'APPROVED'. Return user_id, approved_videos_flagged, first_name, last_name; if there’s a tie for the maximum approved_videos_flagged, return all tied users.
4) Rows containing NULLs: Write queries to return all rows that contain at least one NULL in the Reviews table (any column) and, separately, all rows in the Flags table where any non-PK column (user_id, video_id, or flagged_at) is NULL. Return all columns for those rows.
Overview: This question evaluates SQL skills for data manipulation, including aggregation, joins, deduplication of repeated user actions, and conditional counting based on review status.
Distinct user flaggers and total flags per video
You work on YouTube Trust & Safety. Using the tables below, return one row per video with:
- video_id
- distinct_user_flaggers: number of distinct users who flagged that video (if a user flagged the same video multiple times, count them once)
- total_flags: total number of flags for that video (count of all Flags rows)
Return results for every video (including videos with zero flags). Order by distinct_user_flaggers DESC, then total_flags DESC, then video_id ASC.
Tables
Users(user_id INT, first_name VARCHAR(50), last_name VARCHAR(50))
Videos(video_id INT, title VARCHAR(200))
Flags(flag_id INT, user_id INT, video_id INT, flagged_at TIMESTAMP)
Reviews(flag_id INT, reviewed_date DATE, reviewed_outcome VARCHAR(20))
Hints
- Start from Videos and LEFT JOIN to Flags so every video appears.
- COUNT(DISTINCT user_id) ignores NULL user_id automatically.
Most-flagged video(s) and number of reviewed flags
A flag is considered reviewed if there exists a Reviews row for that flag with a non-NULL reviewed_outcome.
Find the video_id(s) with the highest total number of flags. For those videos, return:
- video_id
- total_flags
- reviewed_flags (count of that video's flags that are reviewed)
If multiple videos tie for the highest total_flags, return all tied videos.
Tables
Users(user_id INT, first_name VARCHAR(50), last_name VARCHAR(50))
Videos(video_id INT, title VARCHAR(200))
Flags(flag_id INT, user_id INT, video_id INT, flagged_at TIMESTAMP)
Reviews(flag_id INT, reviewed_date DATE, reviewed_outcome VARCHAR(20))
Hints
- Compute total_flags per video first, then filter to the maximum.
- Reviewed means reviewed_outcome IS NOT NULL (not just having a Reviews row).
Top user by number of distinct videos with an APPROVED flag
Count, for each user, how many distinct videos they flagged that eventually got APPROVED.
Rules:
- Count each (user_id, video_id) at most once, even if the user flagged the same video multiple times.
- A video counts as approved-for-that-user if there exists at least one flag by that user on that video where reviewed_outcome = 'APPROVED'.
- If there is a tie for the maximum count, return all tied users.
Return: user_id, approved_videos_flagged, first_name, last_name.
Tables
Users(user_id INT, first_name VARCHAR(50), last_name VARCHAR(50))
Videos(video_id INT, title VARCHAR(200))
Flags(flag_id INT, user_id INT, video_id INT, flagged_at TIMESTAMP)
Reviews(flag_id INT, reviewed_date DATE, reviewed_outcome VARCHAR(20))
Hints
- Build DISTINCT (user_id, video_id) pairs where reviewed_outcome = 'APPROVED'.
- Use a window MAX() to keep all ties.
Find Reviews rows containing at least one NULL
Return all rows from the Reviews table that contain at least one NULL value in any column. Return all columns for those rows.
Tables
Users(user_id INT, first_name VARCHAR(50), last_name VARCHAR(50))
Videos(video_id INT, title VARCHAR(200))
Flags(flag_id INT, user_id INT, video_id INT, flagged_at TIMESTAMP)
Reviews(flag_id INT, reviewed_date DATE, reviewed_outcome VARCHAR(20))
Hints
- Use IS NULL checks combined with OR.
- Even if a column is a primary key in theory, still write the query generically.
Find Flags rows where any non-PK column is NULL
Return all rows from the Flags table where any non-primary-key column (user_id, video_id, or flagged_at) is NULL. Return all columns for those rows.
Tables
Users(user_id INT, first_name VARCHAR(50), last_name VARCHAR(50))
Videos(video_id INT, title VARCHAR(200))
Flags(flag_id INT, user_id INT, video_id INT, flagged_at TIMESTAMP)
Reviews(flag_id INT, reviewed_date DATE, reviewed_outcome VARCHAR(20))
Hints
- Only check non-PK columns for NULL.
- Use OR across the three nullable columns.
Community answers
Answer by reemajhunjhunwala01
Done
Answer by sindhujakasula03
Approach here is curate ranking of videos by total flags, And curate total reviewed flags. Then do a left join of both and filter by rank = 1
With cte1 as (SELECT video_id, count(flags) as total_flags, rank() over(order by count(flags) desc) as flags_rnk FROM flags group by video_id), cte2 as (select f.video_id, count(r.flag_id) as reviewed_flags from flags f left join reviews r onf.flag_id = r.flag_idwhere r.reviewed_outcome is not Nullgroup by f.video_id) select cte1.video_id, total_flags, coalesce(reviewed_flags, 0) as reviewed_flags from cte1 left join cte2 on cte1.video_id = cte2.video_idwhere flags_rnk = 1
Answer by sindhujakasula03
Part 5 ANSWER is being evaluated incorrectly
SELECT * FROM flags
where user_id is Null or
video_id is Null or
flagged_at is Null