N006-H1 Tier 1 · Foundations · hard ecommerce · Brightlane

Return the name and country of every qualifying customer

Part of Boolean Logic in WHERE (AND, OR, NOT) in SQL

The problem

Brightlane's North America retention team is running a reactivation campaign targeting active customers based in the US or Canada.

Write a query to return the name and country of every qualifying customer.

Assumptions:

  • Active customers are identified by is_active = true.
  • North American customers are identified by country = 'US' or country = 'CA'.
  • A qualifying customer must be active and based in one of the two North American countries.

Output:

  • One row per qualifying customer, with columns name and country.
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
  name,
  country
FROM
  customers
WHERE
  is_active = TRUE
  AND (
    country = 'US'
    OR country = 'CA'
  )

The shape

The parentheses around the country OR are the load-bearing piece. They keep AND and OR from interleaving, so the filter reads as "active, AND in one of these two countries" rather than the precedence default of "active and US, OR Canadian at all."

Clause by clause

  • SELECT name, country returns the customer and the country they're being targeted in. Keeping country in the output makes the filter easy to verify.
  • FROM customers reads the customer table.
  • WHERE is_active = true AND (country = 'US' OR country = 'CA') is the compound filter. is_active = true keeps active rows. (country = 'US' OR country = 'CA') is a single grouped condition — a row passes if either equality is true. The outer AND requires both the active flag and the country group to hold on the same row. So an active US customer passes (active is true, US matches), an active Canadian passes (active is true, CA matches), an inactive US customer is dropped (the active flag is false), and an active UK customer is dropped (neither country equality is true).

Why the parentheses are not optional

Without them, the query reads is_active = true AND country = 'US' OR country = 'CA'. AND binds tighter than OR, so PostgreSQL groups it as (is_active = true AND country = 'US') OR country = 'CA'. That keeps active US customers (the left side), and then keeps every Canadian customer regardless of active status (the right side stands alone). An inactive Canadian customer would pass — which is the opposite of what a reactivation campaign needs.

The parentheses bind the OR into a single unit before the AND applies. That's what produces the intended logic: active is required, and country membership is required, on the same row.

The trap

Mixing AND and OR without parentheses lets PostgreSQL's precedence rules quietly group the conditions for you. The query runs, returns a set of customers that looks roughly right at a glance, and the inactive Canadians ride through to a "reactivation" campaign that should never have included them. There's no error, no warning, no signal that the precedence isn't what you meant. Any time AND and OR appear in the same WHERE, parenthesise the OR group so the binding is explicit and the query encodes the business logic, not the default precedence.

You practiced parenthesising an OR group inside an AND to control evaluation order. NOT binds tightest, then AND, then OR — so any time a compound WHERE mixes AND with OR, parentheses are how you make sure PostgreSQL groups the conditions the way you intended.

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.