CMD Guide
HomeDatabasesSQL Practice Problems

Game Play Analysis V

Day-one retention in one pass

Day-one retention asks, for every install date x: of the players whose first-ever login was x, what fraction logged in again on x + 1 day. The mechanism that makes this a single-pass query is two facts working together. First, a window function MIN(event_date) OVER (PARTITION BY player_id) stamps every row of a player with that player's install date without collapsing the rows — so each login still carries both its own date and the player's install date side by side. Second, the table's primary key (player_id, event_date) guarantees each player has at most one row whose event_date equals install_dt + 1. That uniqueness is the load-bearing insight: it means a per-row indicator (1 if this login is the day-after-install, else 0) can never fire twice for the same player, so SUM(indicator) is exactly the count of distinct retained players — no DISTINCT needed on the numerator.

That is why the retention numerator is a plain SUM(CASE …) while the denominator is COUNT(DISTINCT player_id). They look asymmetric, and the asymmetry is correct on purpose, explained below.

The query

SELECT
    install_dt,
    COUNT(DISTINCT player_id) AS installs,
    ROUND(
        SUM(CASE WHEN event_date = install_dt + INTERVAL 1 DAY THEN 1 ELSE 0 END)
        / COUNT(DISTINCT player_id),
    2) AS Day1_retention
FROM (
    SELECT
        player_id,
        event_date,
        MIN(event_date) OVER (PARTITION BY player_id) AS install_dt
    FROM Activity
) t
GROUP BY install_dt;

The inner query annotates; it does not aggregate. Every original login row survives and gains a fourth value, install_dt, copied from that player's earliest login. The outer query then groups by install_dt and counts. INTERVAL 1 DAY is MySQL syntax; the same logic in PostgreSQL is install_dt + 1 (date + integer), and in standard SQL install_dt + INTERVAL '1' DAY.

Traced example

LeetCode's sample Activity table, where (player_id, event_date) is the primary key:

player_iddevice_idevent_dategames_played
122016-03-015
122016-03-026
232017-06-251
312016-03-010
342016-07-035

Inner query — stamp each row with its player's install_dt. The window scans each player's partition and copies the minimum event_date onto every row of that player:

player_idevent_dateinstall_dt = MIN over playerevent_date = install_dt + 1 ?
12016-03-012016-03-01no (this is install day itself)
12016-03-022016-03-01yes → 1
22017-06-252017-06-25no
32016-03-012016-03-01no
32016-07-032016-03-01no (124 days later, not +1)

Outer query — group by install_dt.

Final result:

install_dtinstallsDay1_retention
2016-03-0120.50
2017-06-2510.00
diagram
diagram

The two-key subtlety: why numerator and denominator differ

The denominator must use COUNT(DISTINCT player_id) because a player can have many login rows in their install group, and you want to count each player once. The numerator can use a plain SUM precisely because the primary key forbids a second row at the same (player_id, event_date) — and the only date the indicator accepts is the single value install_dt + 1. So per player the indicator is structurally limited to 0 or 1; summing them already counts distinct retained players. If the table allowed duplicate (player_id, event_date) rows, this equivalence would break and you would need COUNT(DISTINCT CASE WHEN … THEN player_id END) instead. Knowing which constraint is doing the work is the difference between copying a recipe and being able to adapt it when the schema changes.

Pitfalls

Takeaways


Problem from LeetCode 1097, “Game Play Analysis V.” Window-function semantics and the date arithmetic per the MySQL 8.0 and PostgreSQL 16 documentation; integer-division behavior verified against each engine's reference. Re-authored and deepened for this guide to surface the primary-key invariant that makes SUM(indicator) equal the count of retained players, add a traced example, a mechanism diagram, and engine portability pitfalls.

🤖 Don't fully get this? Learn it with Claude

Stuck on Game Play Analysis V? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Game Play Analysis V** (Databases) and want to truly understand it. Explain Game Play Analysis V from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Game Play Analysis V** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Game Play Analysis V** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Game Play Analysis V** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes