Books and Authors
Problem
You have two tables. Books holds one row per book with a genre; Authors maps authors to books (an author may appear once per book they wrote).
Books Authors
+----------+---------+ +-----------+---------+---------------------+
| book_id | genre | | author_id | book_id | author_name |
+----------+---------+ +-----------+---------+---------------------+
| 1 | Fiction | | 101 | 1 | Leo Tolstoy |
| 2 | Fiction | | 101 | 2 | Leo Tolstoy |
| 3 | Fiction | | 102 | 3 | Fyodor Dostoevsky |
| 4 | Fiction | | 103 | 4 | F. Scott Fitzgerald |
| 5 | Romance | | 104 | 5 | Jane Austen |
+----------+---------+ +-----------+---------+---------------------+Task: for each author, count how many 'Fiction' books they wrote, returning author_name and fiction_books_count. Authors with zero Fiction books are excluded. Order by fiction_books_count descending; on a tie, order by author_name ascending.
Solution
The shape is the classic filter → join → group → order pipeline. Join authors to their books, keep only Fiction rows, count per author, then apply the two-level sort.
SELECT A.author_name,
COUNT(B.book_id) AS fiction_books_count
FROM Authors A
JOIN Books B ON A.book_id = B.book_id
WHERE B.genre = 'Fiction'
GROUP BY A.author_name
ORDER BY fiction_books_count DESC, A.author_name ASC;Because the join is an inner join, an author with no matching Fiction book contributes no rows and is dropped automatically — no extra filter needed for the "exclude zero" rule. The WHERE runs before GROUP BY, so the COUNT only ever sees Fiction rows.
Result:
+---------------------+---------------------+
| author_name | fiction_books_count |
+---------------------+---------------------+
| Leo Tolstoy | 2 |
| F. Scott Fitzgerald | 1 |
| Fyodor Dostoevsky | 1 |
+---------------------+---------------------+The tie-break, demonstrated correctly
Tolstoy is alone at count 2, so he sits on top. The interesting part is the tie at count 1 between Fyodor Dostoevsky and F. Scott Fitzgerald. The tie-break is author_name ASC, which compares the full strings character by character by code point, left to right, stopping at the first position where they differ.
Compare the two strings position by position:
| Position | F. Scott Fitzgerald | Fyodor Dostoevsky | Decision |
|---|---|---|---|
| 0 | F (0x46, 70) | F (0x46, 70) | equal — keep going |
| 1 | . (0x2E, 46) | y (0x79, 121) | 46 < 121 → decided here |
The very first difference is at position 1: '.' (code point 0x2E = 46) versus 'y' (code point 0x79 = 121). Since 46 < 121, F. Scott Fitzgerald sorts first, and the comparison never reaches any later characters. There is no 'i' involved: char[1] of F. Scott Fitzgerald is the period, not an 'i'. It would be wrong to reason about "Fi" vs "Fy" here — that compares characters that are not at the same position in these two strings. What actually breaks the tie is the period preceding the letters.
Pitfall: the filter and the sort both depend on collation
Two collation-sensitive things hide in this query: the = 'Fiction' filter and the author_name tie-break. How each behaves is engine- and collation-specific, so be precise rather than assuming a universal rule:
- PostgreSQL string comparison with the default collation is case-sensitive and space-sensitive.
'Fiction'will not match'fiction'or'Fiction '(trailing space), and the sort orders by raw byte/code-point order much like the table above. - MySQL defaults to a case-insensitive collation (e.g.
utf8mb4_0900_ai_ciin 8.0, historicallylatin1_swedish_ci). There'fiction' = 'Fiction'is true, and accent-insensitive variants treat accented letters as equal too. Trailing-space handling is its own axis: comparisons with=have traditionally ignored trailing spaces (PAD SPACE), and the exact behavior varies by collation and version.
So the same WHERE B.genre = 'Fiction' can match different rows on different engines, and the tie-break can order names differently (case-folding can change which of two near-identical names comes first). If you need deterministic, portable behavior, normalize the data on insert or pin the collation explicitly — e.g. WHERE B.genre = 'Fiction' COLLATE utf8mb4_bin in MySQL, or an explicit ORDER BY ... COLLATE "C" in PostgreSQL — rather than relying on whatever the server default happens to be.
Source
Problem statement and example adapted from the LeetCode-style "Books and Authors" SQL exercise. Code-point values cited from the ASCII table ('.' = 0x2E = 46, 'F' = 0x46 = 70, 'y' = 0x79 = 121). Collation behavior per the MySQL 8.0 collation reference and the PostgreSQL collation documentation.
🤖 Don't fully get this? Learn it with Claude
Stuck on Books and Authors? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Books and Authors** (Databases) and want to truly understand it. Explain Books and Authors 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.
Socratic — adapts to where you're stuck.
Teach me **Books and Authors** 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.
Active recall exposes what you missed.
Quiz me on **Books and Authors** 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.
Intuition + hook + flashcards for long-term memory.
Help me remember **Books and Authors** 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.