Basic Q7 used ROW_NUMBER + rn=1 to retrieve the latest row. In this practical set, we retrieve an arbitrary Nth row. Two details matter: the ORDER BY direction determines what “Nth” means, and LEFT JOIN preserves customers for whom that row does not exist.
-- ORDER BY direction changes the meaning of rn ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY ordered_at DESC) -- rn=1 means “latest” ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY ordered_at ASC) -- rn=2 means “second”
LEFT JOIN ... ON ... AND r.rn = 2 keeps a customer with one purchase as a NULL row.LEFT JOIN ... WHERE r.rn = 2 evaluates NULL = 2 as UNKNOWN and removes that row, effectively becoming an INNER JOIN.For every customer, retrieve the second purchased product as second_product and its timestamp as second_ordered_at. Customers with one or zero purchases must still appear with NULL values.
| customer_id | name |
|---|---|
| 1 | Tanaka |
| 2 | Sato |
| 3 | Yamada |
| order_id | customer_id | product_name | ordered_at |
|---|---|---|---|
| 101 | 1 | Keyboard | 2024-04-01 10:00 |
| 102 | 1 | Mouse | 2024-04-03 14:30 |
| 103 | 1 | Monitor | 2024-04-10 09:15 |
| 104 | 2 | Earphones | 2024-04-02 18:00 |
| 105 | 2 | Charger | 2024-04-08 12:00 |
| 106 | 3 | Cable | 2024-04-05 11:00 |
Note: Yamada (customer_id=3) has only one purchase. If the rn=2 condition is placed in WHERE, Yamada disappears from the result.
| name | second_product | second_ordered_at |
|---|---|---|
| Tanaka | Mouse | 2024-04-03 14:30 |
| Sato | Charger | 2024-04-08 12:00 |
| Yamada | NULL | NULL |
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
When the same product master exists in a core system and an e-commerce site, reconciliation means finding the differences. LEFT JOIN preserves only rows from the left, but reconciliation must also find products that exist only on the right. FULL OUTER JOIN retains rows from both sides.
-- The join result has three zones -- ① Present on both sides (matching key) → values can be compared -- ② Left side only → every right-side column is NULL -- ③ Right side only → every left-side column is NULL FULL OUTER JOIN ... ON a.product_id = b.product_id
For a one-sided row,
a.price <> b.price becomes “100 <> NULL”, which is UNKNOWN rather than TRUE or FALSE. WHERE rejects UNKNOWN. A difference condition alone therefore misses missing products; add explicit IS NULL conditions or use a NULL-safe comparison.Reconcile products_kikan and products_ec on product_id and return only products with differences. Show the three statuses—price mismatch, missing from EC, and missing from the core system—in a status column, along with both prices. The completely matching product (id=101) must not be returned.
| product_id | name | price |
|---|---|---|
| 101 | Notebook | 300 |
| 102 | Pen | 150 |
| 103 | Eraser | 100 |
| 104 | Ruler | 200 |
| product_id | name | price |
|---|---|---|
| 101 | Notebook | 300 |
| 102 | Pen | 180 |
| 105 | Marker | 250 |
Note: id=102 exists in both tables but has different prices (150 vs 180). IDs 103 and 104 exist only in the core system; id=105 exists only in EC.
| product_id | name | price_kikan | price_ec | status |
|---|---|---|---|---|
| 102 | Pen | 150 | 180 | Price mismatch |
| 103 | Eraser | 100 | NULL | Missing from EC |
| 104 | Ruler | 200 | NULL | Missing from EC |
| 105 | Marker | NULL | 250 | Missing from core |
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
When a parent department has many child departments and each child has many sales rows, joining sales and targets directly creates a fan trap: the target is copied once per sale. Pre-aggregate the many-side rows to child-department grain, attach the parent with a self-join, and aggregate once more at parent grain.
-- Two-stage roll-up -- ① Aggregate sales at child-department grain -- ② Self-join to attach the parent -- ③ Aggregate sales and targets at parent grain
If a target row is joined while sales still has multiple rows per child, the target is duplicated and the achievement rate becomes wrong.
Using departments, sales, and dept_targets, roll each child department's sales and target up to its parent department. Return parent_dept_name, total sales, total target, and achievement rate. Use pre-aggregation and a self-join so each target is counted once.
| dept_id | dept_name | parent_id |
|---|---|---|
| 1 | Sales HQ | NULL |
| 2 | Engineering HQ | NULL |
| 11 | Domestic Sales | 1 |
| 12 | International Sales | 1 |
| 21 | App Development | 2 |
| 22 | Platform Development | 2 |
| sale_id | dept_id | amount |
|---|---|---|
| 1 | 11 | 300000 |
| 2 | 11 | 150000 |
| 3 | 12 | 400000 |
| 4 | 21 | 500000 |
| 5 | 21 | 250000 |
| 6 | 22 | 250000 |
| dept_id | target |
|---|---|
| 11 | 400000 |
| 12 | 500000 |
| 21 | 800000 |
| 22 | 300000 |
Note: each child has a unique target, but sales has multiple rows for some children. Aggregate sales before joining targets.
| parent_dept | total_sales | total_target | achievement_rate |
|---|---|---|---|
| Sales HQ | 850000 | 900000 | 94.4 |
| Engineering HQ | 1000000 | 1100000 | 90.9 |
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
A non-equi join matches rows using a range condition rather than equality. Contract masters are coarse-grained—one row per contract period—while usage logs are fine-grained—one row per event. Put both the user equality and the inclusive date range in the ON clause.
ON s.user_id = l.user_id AND l.used_on BETWEEN s.start_date AND s.end_date
04-10 and 04-30 are inside the range. Keep the range condition in ON so a contract with no matching usage remains as a NULL row under LEFT JOIN.
For every subscription, count distinct usage days inside its contract period. Return user_id, plan, and active_days. A subscription with no usage must remain in the output with 0.
| sub_id | user_id | plan | start_date | end_date |
|---|---|---|---|---|
| 1 | 1 | Pro | 2024-04-01 | 2024-04-30 |
| 2 | 2 | Lite | 2024-04-10 | 2024-05-09 |
| 3 | 3 | Pro | 2024-04-15 | 2024-05-14 |
| log_id | user_id | used_on |
|---|---|---|
| 1 | 1 | 2024-04-05 |
| 2 | 1 | 2024-04-05 |
| 3 | 1 | 2024-04-20 |
| 4 | 1 | 2024-04-28 |
| 5 | 1 | 2024-05-02 |
| 6 | 2 | 2024-04-08 |
| 7 | 2 | 2024-04-12 |
| 8 | 2 | 2024-04-25 |
Note: user1 has four events across three days because 04-05 is duplicated. user2's 04-08 event is before the contract starts, and user3 has no usage.
| user_id | plan | active_days |
|---|---|---|
| 1 | Pro | 3 |
| 2 | Lite | 2 |
| 3 | Pro | 0 |
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free
“Has bought coffee or tea” and “has never bought cake” cannot be decided from one row. They are conditions on the user's entire purchase set. WHERE filters individual rows; GROUP BY creates one set per user, conditional aggregation counts the relevant rows, and HAVING evaluates the set-level conditions.
-- Conditional aggregation counts only rows matching a condition COUNT(*) FILTER (WHERE condition) -- Standard SQL / PostgreSQL SUM(CASE WHEN condition THEN 1 ELSE 0 END) -- Portable form (including MySQL)
WHERE product_name <> 'Cake' removes cake rows, not users who bought cake. A user who bought both coffee and cake still has a surviving coffee row. Translate “has never bought” into the set predicate COUNT(condition) = 0.Join users and purchases and return each user's user_id and user_name when they have bought coffee or tea at least once and have never bought cake.
| user_id | user_name |
|---|---|
| 1 | Tanaka |
| 2 | Sato |
| 3 | Yamada |
| 4 | Suzuki |
| 5 | Takahashi |
| purchase_id | user_id | product_name |
|---|---|---|
| 1 | 1 | Coffee |
| 2 | 1 | Cake |
| 3 | 2 | Coffee |
| 4 | 2 | Sandwich |
| 5 | 3 | Cake |
| 6 | 4 | Tea |
| 7 | 5 | Sandwich |
Note: Tanaka bought coffee and cake; Yamada bought only cake; Takahashi bought only a sandwich. None of those users satisfies both conditions.
| user_id | user_name |
|---|---|
| 2 | Sato |
| 4 | Suzuki |
- Read every model answer, explanation and table-transition visualization across all 48 advanced sets (240 questions)
- One-time purchase — no subscription. Future advanced sets are included
- Background, problem and expected output stay free