N017-H1 Tier 2 · Core SQL · hard ecommerce · Brightlane

Returns the product name and category name for every product whose `category_id` matches a row in `categories`

Part of INNER JOIN in SQL

The problem

Brightlane's product team is reviewing catalogue organisation. They want a report listing each product alongside its category — the gap between the report's row count and the catalogue's true product count is itself the audit finding.

Write a query that returns the product name and category name for every product whose category_id matches a row in categories.

Assumptions:

  • The products table contains 63 products in the catalogue.
  • The categories table contains every defined category, identified by categories.id.
  • Some products have a category_id value that does not match any row in categories, or have a missing category_id. Those products will not appear in the result.
  • The result will contain fewer than 63 rows; the missing products are the ones the audit is meant to surface.

Output:

  • One row per product whose category resolves, with columns product_name and category_name.
Schema · ecommerce 5 tables
categories
id integer
name text
parent_id? integer
products
id integer
name text
category_id integer
price numeric
stock_qty integer
attributes? jsonb
order_items
id integer
order_id integer
product_id integer
quantity integer
unit_price numeric
customers
id integer
name text
email text
city? text
country text
created_at timestamptz
is_active boolean
orders
id integer
customer_id integer
ordered_at timestamptz
status text
total_amount numeric

Run previews · Check grades

Write a query, then run it to see results here.

Worked solution Try it yourself first
Solution query
SELECT
  p.name AS product_name,
  cat.name AS category_name
FROM
  products p
  JOIN categories cat ON p.category_id = cat.id

The shape

INNER JOIN's drop-unmatched behavior is the audit. The catalogue has 63 products, but only the ones whose category_id matches a row in categories come through the join. Every product with a missing or invalid category_id is silently excluded. The size of the gap between 63 and the result row count is the data-quality finding the product team came to read.

Clause by clause

  • FROM products p reads from the catalogue side: all 63 products, regardless of category assignment.
  • JOIN categories cat ON p.category_id = cat.id pairs each product with its category. For products whose category_id lines up with a categories.id, the row passes through with the category's columns attached. For products with a NULL category_id, or with a category_id value that no row in categories matches, the join drops the row from the result. There is no error, no warning, no flag.
  • SELECT p.name AS product_name, cat.name AS category_name returns the product's name and the resolved category's name. Both name columns are qualified because both tables carry one.

Why this and not a different shape

The audit could be written several ways, but INNER JOIN is the right tool here precisely because the prompt frames the gap as the finding. The product team can compare the row count to the known catalogue total (63) and read the difference directly as "how many products fail to map." The detail of which products fall out belongs to a later report; this one is the count-the-gap version.

The trap

INNER JOIN's drop behavior is silent. A learner running this query for the first time might see, say, 60 rows come back and assume the catalogue has 60 products. The query gives no signal that 3 were dropped. The result row count is the only evidence, and only if you already know the source row count to compare against.

The rule: any time an INNER JOIN row count comes in lower than the left-side source's row count, the gap is the number of rows that failed to match. When that gap matters for an audit, a coverage check, or a data-quality review, verify it explicitly rather than assuming the result row count equals the source row count.

You practiced relying on INNER JOIN's drop-unmatched behavior as the answer to a real business question. Useful when the absence of a row is itself the finding.

How you actually get good at SQL

Reading explains SQL. Writing it, over and over with instant feedback, is what makes you fluent.

That's the whole SQLMaxx loop: 600+ real problems, instant AI feedback, mastery you can actually see, and spaced review that won't let you forget.

A stack of SQL practice problem cards, the top card showing an employees table.
615 problems · 66 concepts

Real problems. Not toy examples.

615 hand-built problems spanning all 66 concepts, from basic SELECTs to window functions, built on real schemas and real business questions, the kind you'll actually get asked on the job. Enough reps to make SQL automatic.

A retro computer showing a SQL query marked correct with a green checkmark.
Instant AI feedback

Write a query. Know if it's right in one second.

No copying an answer and hoping it clicked. The AI grader checks your real query against real data, catches exactly what's wrong, and explains the fix in plain English, like a senior analyst reading over your shoulder on every problem.

A circular mastery progress dial filling from blue to green, the SQLMaxx diamond at its center.
Mastery tracking

Stop guessing whether you actually know it.

SQLMaxx tracks every concept and shows you what you've mastered and what's still shaky. Your skills fill in one concept at a time, so 'I think I get joins' becomes something you can prove.

A SQL query editor circled by a blue return arrow with a clock, scheduled to come back for review.
Spaced review

Learn it once. Keep it for good.

Most of what you learn this week fades by next week. So when a concept comes due for review, SQLMaxx hands you a fresh problem to solve from a blank editor, not a flashcard to re-read. A research-backed spaced-repetition algorithm (FSRS) times each return for right before you'd forget, so your SQL is still there months later, when the interview or the job actually needs it.

Practice, feedback, mastery, review. That's the loop that turns reading into real skill.

Start free

No account, no credit card. Start solving in under a minute.