String functions

SQL · Practical · 9 min

Data arrives with stray spaces, inconsistent case and combined fields; fixing it in SQL avoids round trips.

Every database ships dozens of string functions. The core set, with PostgreSQL names:

  • length(s), lower(s), upper(s).
  • trim(s) removes surrounding spaces; ltrim, rtrim remove one side.
  • substring(s FROM 2 FOR 3) or substr(s, 2, 3) extracts characters; positions start at 1, not 0.
  • position('@' IN email) finds a substring (0 if absent).
  • replace(s, 'from', 'to') replaces every occurrence.
  • split_part(email, '@', 2) takes the n-th field of a delimited string.
  • concat_ws(', ', a, b, c) joins with a separator and, unlike ||, skips NULL arguments.
  • left(s, n), right(s, n), lpad(s, n, '0').

For anything beyond LIKE, PostgreSQL supports POSIX regular expressions: email ~ '^[a-z]+@' tests a match (~* is case-insensitive) and regexp_replace(phone, '[^0-9]', '', 'g') strips non-digits.

string_agg(name, ', ' ORDER BY name) is an aggregate that concatenates values across rows; MySQL calls it GROUP_CONCAT, Oracle and the standard LISTAGG.

Dialect names vary more than behaviour: SQL Server uses LEN (which ignores trailing spaces) and CHARINDEX; MySQL's LENGTH counts bytes, so use CHAR_LENGTH for characters in UTF-8 text. PostgreSQL's length counts characters.

Applying a function to a column in WHERE, such as WHERE lower(email) = 'ana@example.com', normally prevents a plain index on email from being used. Either store data normalised or create an index on the expression itself, as the index lessons show.

Example

SELECT email,
       split_part(email, '@', 1)         AS local_part,
       upper(left(name, 1))              AS initial,
       length(name)                      AS len,
       concat_ws(' / ', name, city)      AS label   -- NULL city skipped
FROM customers ORDER BY id;
--  ana@example.com   | ana   | A | 3 | Ana / Yerevan
--  ben@example.com   | ben   | B | 3 | Ben / London
--  chloe@example.com | chloe | C | 5 | Chloe / London
--  dev@example.com   | dev   | D | 3 | Dev

SELECT trim('  Mouse  ') AS t,
       regexp_replace('+1 (555) 010-99', '[^0-9]', '', 'g') AS digits;
--  Mouse | 155501099

SELECT city, string_agg(name, ', ' ORDER BY name) AS people
FROM customers WHERE city IS NOT NULL GROUP BY city ORDER BY city;
--  London  | Ben, Chloe
--  Yerevan | Ana

Remember

  • String positions start at 1
  • concat_ws skips NULLs; || turns the whole result NULL
  • string_agg (GROUP_CONCAT, LISTAGG) concatenates across rows
  • Functions on a column in WHERE usually bypass a plain index

Watch out for

  • Using MySQL's LENGTH on UTF-8 text and getting bytes instead of characters.

Check yourself

  1. In PostgreSQL, what is 'Hi ' || NULL || 'there'?
  2. Which aggregates names from many rows into one comma-separated string in PostgreSQL?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.postgresql.org/docs/current/functions-string.html

Builds on

Unlocks

Nothing builds on this yet.