N036-H2 Tier 3 · Intermediate · hard ecommerce · Brightlane

Return the search key for the author `' DR. JAMES WATSON '` and the year `'1953'`

Part of String Concatenation and Formatting in SQL

The problem

Brightlane's search index builder normalizes contributor entries by lowercasing and trimming an author name, then combining the cleaned name with a publication year using '|' as the separator.

Write a query to return the search key for the author ' DR. JAMES WATSON ' and the year '1953'.

Output:

  • A single row with one column, search_key, containing the constructed key in the form <lowercased-trimmed-name>|<year>.
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
  CONCAT_WS('|', LOWER(TRIM('  DR. JAMES WATSON  ')), '1953') AS search_key

The shape

LOWER(TRIM(' DR. JAMES WATSON ')) runs the two cleanup transforms before the value reaches the concatenation, so what gets joined is the lowercased, trimmed form and not the raw input. CONCAT_WS('|', ..., '1953') then places a single '|' between the cleaned name and the year, producing the search key 'dr. james watson|1953'.

Clause by clause

  • SELECT CONCAT_WS('|', LOWER(TRIM(' DR. JAMES WATSON ')), '1953') AS search_key evaluates the full expression and labels the resulting column search_key. The argument order matters: '|' is the separator; the second argument is the cleaned name; the third is the year.
  • The cleanup runs inside out. TRIM(' DR. JAMES WATSON ') strips the leading and trailing spaces and returns 'DR. JAMES WATSON'. LOWER(...) then lowercases that result to 'dr. james watson'. Only that final cleaned string is handed to CONCAT_WS.
  • CONCAT_WS('|', 'dr. james watson', '1953') joins the two non-NULL values with one '|' between them and returns 'dr. james watson|1953'. There is no FROM because the inputs come straight from the prompt as literals.

Why composing inside CONCAT_WS and not cleaning afterward

Running the transforms on the input before the concatenation keeps the cleanup local to the value that needs it. Concatenating first and then trying to lowercase or trim the combined string would corrupt the year side too, or would require carving the string back apart to apply transforms only to one piece. The cleaner shape is to clean each component on its own and let the concatenation see only the final forms.

The cleanup order itself matters as well. LOWER(TRIM(x)) and TRIM(LOWER(x)) happen to produce the same result on this input, but the conventional shape is to trim first so any subsequent transforms operate on the value's real content rather than on padding.

The trap

It is tempting to read the expression as TRIM(LOWER(...)) or even to skip one transform thinking the other will cover it. Neither holds. LOWER does not strip whitespace, and TRIM does not change case. A search key built from LOWER alone would carry the padding spaces into the index and never match a lookup; one built from TRIM alone would still record the all-caps form. Both transforms are doing distinct work, and dropping either breaks the lookup the index is being built for.

You practiced composing CONCAT_WS(separator, LOWER(TRIM(...)), value) — chain string transforms inside a CONCAT_WS call so the cleaned form is what gets concatenated, not the raw input.

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.