N036 Tier 3 · Intermediate

String Concatenation and Formatting in SQL

PostgreSQL provides three tools for combining and formatting strings: the `||` operator, the `CONCAT` and `CONCAT_WS` functions, and the `FORMAT` function. They differ in how they handle NULL inputs and in what kind of output they are designed to produce.

PostgreSQL has three ways to join strings together — the || operator, the CONCAT family, and FORMAT — and the choice between them matters whenever a column might be NULL.

You're building a display label for each customer: their name and country, formatted for a report. Some customers are missing a country. Whether the result is NULL, partial, or gracefully filled depends entirely on which tool you use.

|| is the simplest: it concatenates two values with a binary operator. But it follows the standard NULL propagation rule — if either side is NULL, the result is NULL. A missing country means the entire label comes back NULL:

If country is NULL for any customer, that row returns NULL for customer_label. No error — just a missing value where you expected a string.

CONCAT fixes this. It accepts any number of arguments and treats NULL inputs as empty strings:

SELECT CONCAT(first_name, ' ', last_name) AS full_name
FROM customers

If last_name is NULL, CONCAT skips it and returns just the first name. This is usually what you want when building display values from optional fields.

CONCAT_WS (concatenate with separator) takes a separator as its first argument and joins all non-NULL values with that separator between them — no doubled separators, no trailing delimiter:

SELECT CONCAT_WS(', ', last_name, first_name, middle_name) AS full_name
FROM customers

If middle_name is NULL, the output is 'Smith, John' — not 'Smith, John, '. The separator only appears between values that actually exist.

FORMAT works from a template. You write a string with %s placeholders and provide the values to fill them in order:

SELECT FORMAT('Order #%s placed on %s', id, created_at::date) AS order_label
FROM orders

FORMAT coerces each argument to text automatically, so you don't need to cast numbers or dates first. NULL arguments are inserted as empty strings. FORMAT is especially useful when building a label with fixed structure — if you find yourself writing long || chains with multiple casts and literal strings, FORMAT is usually cleaner.

One practical difference: CONCAT_WS with a separator handles variable-length data (unknown number of non-NULL fields) cleanly. FORMAT is better when the output has fixed structure with a known set of placeholders.

The one thing that trips people up: || with NULL produces NULL, not a partial string.

If you're building a label from multiple columns and any might be NULL, use CONCAT or CONCAT_WS instead of ||. The || operator is the right choice when you know the inputs are not NULL, or when you want NULL to propagate intentionally — for example, when a NULL in one field means the entire record is incomplete and the label should be absent.

Practice

9 String Concatenation and Formatting practice problems

These problems are part of the String Concatenation and Formatting lesson in SQLMaxx, with instant grading and a worked solution on each.

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.