skip to content

How do you build a full-name column from first_name and last_name, and how portable is that expression?

level: middleimportance: should knowfreq 50%

answer

  1. Two pipe characters, not a plus
  2. One missing part can blank the whole value
  3. Ask what unknown plus known should mean
  4. The operator itself is a dialect boundary
  5. COALESCE each part before joining

basics

~20 s

Standard SQL concatenates with the || operator: first_name || ' ' || last_name. A NULL operand makes the whole result NULL, so wrap parts in COALESCE. SQL Server uses + or CONCAT, and MySQL treats || as OR by default.

solid answer

~40 s

The standard's concatenation operator is `||`, so the portable-first spelling is `first_name || ' ' || last_name AS full_name`. Two things bite. First, **NULL propagates**: under the standard, `'Ann' || NULL` is NULL, so one missing surname blanks the entire column — guard with `COALESCE(last_name, '')` (or `TRIM` the joined result if a stray space matters). Second, the operator itself is not universal: PostgreSQL, Oracle and SQLite implement `||`; SQL Server uses `+` and offers `CONCAT()`; MySQL reads `||` as logical OR unless the `PIPES_AS_CONCAT` SQL mode is enabled, so MySQL code uses `CONCAT()`. Engines also disagree about whether `CONCAT` skips NULL arguments or returns NULL, so if the query must run on more than one engine, make the NULL handling explicit rather than inheriting it.

code

sql · 7 lines
sql
-- Blanks the whole column when last_name is NULL
SELECT first_name || ' ' || last_name AS full_name
FROM people;

-- Explicit about what a missing part contributes
SELECT TRIM(COALESCE(first_name, '') || ' ' || COALESCE(last_name, '')) AS full_name
FROM people;

go deeper

for a junior

Be able to write first_name || ' ' || last_name AS full_name and to explain why a row with a missing surname can come back empty, plus the COALESCE fix.

for a middle

Explain NULL propagation through string expressions and name the dialect split: || in PostgreSQL, Oracle and SQLite, + or CONCAT in SQL Server, CONCAT in MySQL where || means OR by default.

for a senior

Show that you make NULL handling explicit rather than inheriting an engine default, since the same query can produce empty cells on one engine and full names on another, and that you keep a shared derivation in one view.

for a principal

Own where presentation-shaped derivations live at all — database view versus application layer — and how locale rules and multi-engine targets factor into that call.

## The standard operator SQL's concatenation operator is `||`, and it works on character strings: ```sql SELECT first_name || ' ' || last_name AS full_name FROM people; ``` That is a per-row expression like any other: it produces one derived column, and it needs an alias because a concatenation has no natural name. ## NULL propagates through the expression The most common defect in this expression has nothing to do with syntax. Under the standard, concatenating anything with NULL yields NULL, because NULL means "unknown" and an unknown component makes the whole string unknown. So a person with `last_name` NULL gets `full_name` NULL — not `'Ann '`, not `'Ann'`, but nothing at all, and the report shows an empty cell where a first name was expected. The fix is to decide, in the query, what a missing part should contribute: ```sql SELECT TRIM(COALESCE(first_name, '') || ' ' || COALESCE(last_name, '')) AS full_name FROM people; ``` `COALESCE(x, '')` substitutes an empty string for a missing part; the outer `TRIM` removes the space you would otherwise be left with at one end. Note that Oracle is a genuine exception to the propagation rule — it treats the empty string as NULL and its `||` returns the non-NULL operand — which is one more reason not to rely on an engine's default behaviour when the query has to travel. ## The operator is not universal This is one of the least portable corners of everyday SQL: - **PostgreSQL, Oracle, SQLite** implement `||` as concatenation. - **SQL Server** has no `||` operator; it concatenates with `+` and also provides `CONCAT(...)`. - **MySQL** parses `||` as logical OR under its default SQL mode; enabling `PIPES_AS_CONCAT` changes that, but MySQL code conventionally uses `CONCAT(...)`. `CONCAT` is widely available, which makes it tempting as the common denominator — but its NULL rule is itself engine-specific. Some engines return NULL if any argument is NULL, others treat a NULL argument as an empty string. Since those two behaviours produce completely different reports, an expression that must be portable should spell out its own NULL handling with `COALESCE` and not depend on which convention the engine picked. A further trap with `+` on SQL Server: because `+` is also arithmetic addition, concatenating strings that happen to look numeric — or mixing a string and a number — can turn into an addition or a conversion error rather than a concatenation. ## The neighbouring string functions The same portability logic applies to the rest of the string toolkit. The standard spellings are: - `SUBSTRING(sku FROM 1 FOR 3)` — take three characters starting at position 1. The familiar `SUBSTR(sku, 1, 3)` is common but is not the standard's form, and `LEFT`/`RIGHT` are engine extensions. - `CHARACTER_LENGTH(name)` (also spelled `CHAR_LENGTH`) — length in characters, as opposed to `OCTET_LENGTH` in bytes. - `TRIM(BOTH ' ' FROM code)` — with `LEADING` and `TRAILING` variants. - `UPPER(city)` / `LOWER(email)` — case folding, whose behaviour on non-ASCII text depends on the collation in force, so do not assume it is a pure per-character mapping. - `POSITION('-' IN sku)` — find a substring's offset. Stick to these and a query moves; reach for the friendlier local names and it stops at the engine boundary. ## Where to build the string A display name is presentation, and there is a legitimate argument for assembling it in the application, where locale rules (family name first, particles, single-name cultures) are easier to express than in a SELECT list. Building it in SQL is right when the value is used for filtering, sorting or export inside the database, or when many consumers need the same rule and you can pin it in one view. Building it in SQL and then re-implementing it slightly differently in three services is the outcome to avoid. ## Summary of the safe pattern Use `||` as the default, name the result, decide explicitly what NULL contributes with `COALESCE`, trim the seams, and if the codebase targets more than one engine, note in review that concatenation is a dialect boundary rather than assuming the operator travels.

  • Under the standard, what does 'Ann' || NULL evaluate to, and why?
    NULL. NULL means an unknown value, so a string built from an unknown part is itself unknown — the standard propagates it rather than treating NULL as an empty string. Oracle is the notable divergence, returning the non-NULL operand because it equates the empty string with NULL. Wrap operands in `COALESCE(x, '')` when you want a partial result.
  • What is the standard's spelling for taking the first three characters of sku?
    `SUBSTRING(sku FROM 1 FOR 3)`. The positional form `SUBSTR(sku, 1, 3)` and the shortcut `LEFT(sku, 3)` are common engine extensions rather than the standard's syntax, so they may not travel. Character positions in the standard form are 1-based.
  • Should the display name be built in SQL at all?
    It depends on who consumes it. Build it in SQL when the database filters, sorts or exports on that value, or when one shared view can give every consumer the same rule. Build it in the application when the rule is locale-sensitive — name order, particles, single-name cultures — since that logic is far more awkward to express and test inside a SELECT list.

saying these in an interview costs you the question

  • Assumes || works on every SQL engine
  • Expects NULL to concatenate as an empty string
  • Calls CONCAT's NULL handling identical everywhere
  • Uses + for strings and is surprised by a conversion error
  • Treats SUBSTR and LEFT as standard SQL

context