sqlpostgresqlinitcapstring-functions

INITCAP в SQL: как привести имена и названия к красивому регистру

INITCAP в PostgreSQL делает первую букву каждого слова заглавной, а остальные строчными; разбираем приведение имен и городов и поломки на апострофах, дефисах и локалях.

9 мин чтенияСправочникsql · postgresql · initcap · string-functions · mysql

Иногда данные в таблицу попадают как попало: кто-то написал имя капсом, кто-то маленькими буквами, кто-то случайно включил Caps Lock в форме регистрации.

В итоге в базе лежит неаккуратная смесь:

john DOE
maria ivanova
ALEX PETROV
saint petersburg

Для отчёта, интерфейса или выгрузки такие значения хочется привести к нормальному виду:

John Doe
Maria Ivanova
Alex Petrov
Saint Petersburg

В PostgreSQL для этого есть функция INITCAP. Она делает первую букву каждого слова заглавной, а остальные буквы — строчными.

SELECT initcap('john DOE') AS name;

Результат:

John Doe

На первый взгляд кажется, что INITCAP идеально подходит для имён, городов и названий. Но есть нюанс: функция не знает правил написания реальных фамилий, брендов и географических названий. Она просто применяет механическое правило к строке.

Поэтому INITCAP хорош для быстрой нормализации простых данных, но его нельзя слепо использовать там, где важна точная, каноническая форма имени.

Что делает INITCAP

INITCAP приводит строку к так называемому title case: первая буква каждого слова становится заглавной, а остальные буквы в этом слове становятся строчными.

Примеры:

SELECT initcap('hello world') AS result;

Результат:

Hello World
SELECT initcap('JOHN DOE') AS result;

Результат:

John Doe
SELECT initcap('maria IVANOVA') AS result;

Результат:

Maria Ivanova

Функция не пытается угадать, как строка должна выглядеть «по смыслу». Она не знает, что перед ней: имя человека, город, название компании или обычный текст. Она просто берёт каждое слово и приводит его к виду:

Первая Буква Заглавная, Остальные Строчные

Как INITCAP понимает слова

Для INITCAP слово — это последовательность букв и цифр. Всё, что не является буквой или цифрой, считается границей между словами.

Границей может быть:

  • пробел;
  • дефис;
  • апостроф;
  • точка;
  • запятая;
  • символ #;
  • скобка;
  • любой другой небуквенно-цифровой символ.

Например:

SELECT initcap('order #42 paid') AS result;

Результат:

Order #42 Paid

Слова здесь — order, 42 и paid. Символ # не считается частью слова, поэтому он просто остаётся в строке как разделитель.

Ещё пример:

SELECT initcap('hello.world') AS result;

Результат:

Hello.World

Точка стала границей между словами, поэтому hello превратилось в Hello, а world — в World.

Простой пример с таблицей users

Представим таблицу пользователей:

id | name
---+----------------
1  | john DOE
2  | MARIA ivanova
3  | alex petrov
4  | ELENA SMIRNOVA

Чтобы вывести имена аккуратно, можно написать:

SELECT
    id,
    initcap(name) AS display_name
FROM users
ORDER BY id;

Результат:

id | display_name
---+----------------
1  | John Doe
2  | Maria Ivanova
3  | Alex Petrov
4  | Elena Smirnova

Такой вариант хорошо подходит для показа данных в отчёте, админке или учебном примере.

Важно: исходное значение в таблице при этом не меняется. Мы просто красиво вывели его в запросе.

INITCAP для городов и стран

INITCAP удобно использовать не только для имён, но и для простых географических названий.

Например, в таблице есть города:

city
----------------
moscow
SAINT PETERSBURG
almaty
tbilisi

Запрос:

SELECT initcap(city) AS city_name
FROM users;

Результат:

city_name
----------------
Moscow
Saint Petersburg
Almaty
Tbilisi

Если нужно сгруппировать пользователей по стране, а страны записаны в разном регистре, можно привести их к одному виду:

SELECT
    initcap(country) AS country_name,
    count(*) AS users_count
FROM users
GROUP BY initcap(country)
ORDER BY users_count DESC;

Так строки kazakhstan, KAZAKHSTAN и Kazakhstan попадут в одну группу с названием Kazakhstan.

Но для больших таблиц такой подход не всегда хорош по производительности. Об этом поговорим ниже.

INITCAP — это форматирование, а не истина

Очень важная мысль: INITCAP делает строку красивее, но не делает её правильной.

Например:

SELECT initcap('mcdonald') AS result;

Результат:

Mcdonald

Но фамилия может правильно писаться как:

McDonald

INITCAP этого не знает. Для него это обычное слово: первая буква заглавная, остальные строчные.

Ещё пример:

SELECT initcap('iphone store') AS result;

Результат:

Iphone Store

Но бренд пишется как iPhone, а не Iphone.

Поэтому INITCAP подходит для простого приведения регистра, но не подходит для восстановления правильного написания имён, фамилий, брендов и названий компаний.

Где INITCAP может испортить данные

Самые частые проблемы возникают с реальными именами и названиями.

Фамилии с внутренними заглавными буквами

SELECT initcap('McDONALD') AS result;

Результат:

Mcdonald

Функция опустила все буквы после первой в нижний регистр. Поэтому McDONALD превратилось не в McDonald, а в Mcdonald.

То же самое может случиться с фамилиями и именами вроде:

DeShawn
MacArthur
McGregor

INITCAP не знает этих правил.

Частицы в фамилиях

В некоторых фамилиях маленькие слова могут писаться со строчной буквы:

van der Berg
de la Cruz
von Braun

Но INITCAP сделает так:

SELECT initcap('van der berg') AS result;

Результат:

Van Der Berg

Технически функция сработала правильно, но культурно или юридически такое написание может быть неверным.

Апострофы

Апостроф считается границей слова.

SELECT initcap('o''brien') AS result;

Результат:

O'Brien

Иногда это как раз то, что нужно. Но не всегда.

Например:

SELECT initcap('d''artagnan') AS result;

Результат:

D'Artagnan

А в некоторых контекстах ожидаемая форма может быть другой. Функция не понимает культурные правила, она просто видит границу слова после апострофа.

Дефисы

Дефис тоже считается границей слова.

SELECT initcap('jean-luc picard') AS result;

Результат:

Jean-Luc Picard

В этом примере результат выглядит хорошо. Но правило всё равно механическое: после дефиса новое слово начинается с заглавной буквы.

Например:

SELECT initcap('x-ray department') AS result;

Результат:

X-Ray Department

А в реальном тексте вам может быть нужен вариант X-ray department.

Аббревиатуры

INITCAP плохо подходит для аббревиатур.

SELECT initcap('USA') AS result;

Результат:

Usa
SELECT initcap('SQL course') AS result;

Результат:

Sql Course

Для человека понятно, что USA и SQL должны остаться капсом. Но INITCAP этого не знает.

Поэтому если в данных есть аббревиатуры, бренды или технические термины, после INITCAP их нужно обрабатывать отдельно.

INITCAP и русские строки

Для русских строк INITCAP тоже может быть полезен.

SELECT initcap('иван петров') AS result;

Результат:

Иван Петров
SELECT initcap('МОСКВА') AS result;

Результат:

Москва

Для простых русских имён и городов это часто работает ожидаемо.

Но правило остаётся тем же: первая буква слова заглавная, остальные строчные. Если в строке есть необычное написание, аббревиатура или бренд, функция может привести его к нежелательному виду.

Например:

SELECT initcap('sql arena') AS result;

Результат:

Sql Arena

А если название продукта должно быть SQL Arena, то INITCAP уже не подходит как единственное решение.

Когда INITCAP можно использовать смело

INITCAP хорошо подходит для ситуаций, где данные простые и от них не требуется юридическая точность.

Например:

  • привести имя для отображения в отчёте;
  • красиво показать город;
  • привести простые категории к единому виду;
  • подготовить черновую выгрузку;
  • быстро проверить качество данных;
  • сделать временное представление в SELECT.

Пример:

SELECT
    id,
    initcap(first_name) AS first_name,
    initcap(last_name) AS last_name
FROM users;

Если данные простые, результат будет аккуратным:

id | first_name | last_name
---+------------+----------
1  | Ivan       | Petrov
2  | Maria      | Ivanova
3  | Alex       | Smirnov

Для учебных задач, отчётов и базовой чистки данных это хороший инструмент.

Когда INITCAP лучше не использовать автоматически

Не стоит бездумно применять INITCAP ко всей таблице, если там могут быть:

  • фамилии с необычным написанием;
  • имена с апострофами;
  • двойные имена через дефис;
  • частицы вроде de, van, von;
  • названия компаний;
  • бренды;
  • аббревиатуры;
  • технические термины;
  • многоязычные данные.

Плохой пример:

UPDATE users
SET name = initcap(name);

Такой запрос обновит все строки. Если в таблице есть McDonald, SQL, iPhone, O'Connor, van der Berg, функция может изменить их не так, как нужно.

Перед массовым обновлением лучше сначала посмотреть, что именно изменится.

Как безопасно проверить результат перед UPDATE

Хорошая привычка: сначала написать SELECT, а уже потом UPDATE.

Например:

SELECT
    id,
    name AS old_name,
    initcap(name) AS new_name
FROM users
WHERE name <> initcap(name)
ORDER BY id;

Так вы увидите старое и новое значение рядом:

id | old_name      | new_name
---+---------------+--------------
1  | john DOE      | John Doe
2  | McDONALD      | Mcdonald
3  | SQL academy   | Sql Academy

И сразу станет видно, где INITCAP помогает, а где портит данные.

Если результат выглядит безопасным, можно обновить только подходящие строки.

Например, пропустить имена с дефисами и апострофами:

UPDATE users
SET name = initcap(name)
WHERE name <> initcap(name)
  AND name !~ '[''-]';

Здесь условие name !~ '[''-]' означает: не трогать строки, где есть апостроф или дефис.

Это не идеальная защита, но уже лучше, чем обновлять всю таблицу вслепую.

Как сохранить красивое значение отдельно

Иногда исходные данные лучше не менять, но нужно иметь красивую версию для показа.

Например, можно создать представление:

CREATE VIEW users_display AS
SELECT
    id,
    name,
    initcap(name) AS display_name
FROM users;

Теперь можно обращаться к представлению:

SELECT id, display_name
FROM users_display
ORDER BY display_name;

Так вы не переписываете реальные данные, но получаете удобную версию для интерфейса или отчёта.

Это хороший подход, если вы не уверены, что нормализация должна быть постоянной.

INITCAP и NULL

Если в строке лежит NULL, результат тоже будет NULL.

SELECT initcap(NULL) AS result;

Результат:

NULL

Это нормальное поведение SQL: если значения нет, то и форматировать нечего.

Если для отчёта нужно заменить NULL на пустую строку, можно использовать COALESCE:

SELECT initcap(COALESCE(name, '')) AS display_name
FROM users;

COALESCE(name, '') означает: если name равен NULL, возьми пустую строку.

Пример:

name
-------
NULL
john
MARIA

Запрос:

SELECT initcap(COALESCE(name, '')) AS display_name
FROM users;

Результат:

display_name
------------

John
Maria

В первой строке будет пустое значение вместо NULL.

INITCAP в GROUP BY

INITCAP иногда используют для группировки данных, когда значения отличаются только регистром.

Например:

country
-----------
georgia
Georgia
GEORGIA
kazakhstan
KAZAKHSTAN

Запрос:

SELECT
    initcap(country) AS country_name,
    count(*) AS users_count
FROM users
GROUP BY initcap(country)
ORDER BY users_count DESC;

Результат:

country_name | users_count
-------------+------------
Georgia      | 3
Kazakhstan   | 2

Для небольших таблиц это удобно. Но на больших таблицах есть нюанс: база должна вычислить initcap(country) для каждой строки, а потом группировать результат.

Если такой отчёт нужен постоянно, лучше заранее хранить нормализованное значение в отдельной колонке или подготовить отдельный слой данных для аналитики.

INITCAP в WHERE и проблема с индексами

INITCAP лучше использовать в SELECT, когда мы готовим значение для показа.

А вот в WHERE с ним нужно быть осторожнее.

Например:

SELECT *
FROM users
WHERE initcap(name) = 'John Doe';

Такой запрос выглядит удобно, но для базы он менее приятен. Ей нужно применить функцию initcap(name) к строкам, а потом сравнить результат с 'John Doe'.

Обычный индекс по колонке name в такой ситуации может не помочь, потому что в условии используется не сама колонка, а функция от неё.

Лучше хранить данные в нормальном виде или искать по исходному значению:

SELECT *
FROM users
WHERE name = 'John Doe';

Если поиск именно по initcap(name) действительно нужен часто, можно рассмотреть отдельную нормализованную колонку или функциональный индекс. Но для новичка главное правило такое:

форматируйте данные в SELECT, а фильтруйте по нормальным исходным колонкам.

INITCAP в PostgreSQL

В PostgreSQL функция называется initcap.

Синтаксис простой:

initcap(string)

Пример:

SELECT initcap('hello SQL world') AS result;

Результат:

Hello Sql World

Обратите внимание: SQL превратилось в Sql. Это ещё раз показывает, что INITCAP не сохраняет аббревиатуры.

Если нужно сохранить SQL капсом, придётся обрабатывать такие случаи отдельно.

Например:

SELECT replace(initcap('hello SQL world'), 'Sql', 'SQL') AS result;

Результат:

Hello SQL World

Но такой подход подходит только для простых и известных исключений. Если исключений много, лучше держать словарь правильных написаний или решать это на уровне приложения.

INITCAP в MySQL

В MySQL встроенной функции INITCAP обычно нет. Но для одного слова похожий эффект можно собрать вручную через UPPER, LOWER и SUBSTRING.

Например:

SELECT CONCAT(
    UPPER(SUBSTRING(name, 1, 1)),
    LOWER(SUBSTRING(name, 2))
) AS display_name
FROM users;

Если name = 'jOHN', результат будет:

John

Разберём выражение:

SELECT UPPER(SUBSTRING(name, 1, 1)) FROM users;

берёт первую букву и делает её заглавной.

SELECT LOWER(SUBSTRING(name, 2)) FROM users;

берёт всё, начиная со второго символа, и делает строчным.

CONCAT(...) склеивает первую букву и остальную часть строки.

Но это работает нормально только для одного слова. Для строки вроде john doe такой запрос даст:

John doe

А не:

John Doe

Чтобы полноценно повторить INITCAP для нескольких слов в MySQL, обычно используют пользовательскую функцию, более сложный SQL или выносят эту логику в приложение.

INITCAP в ClickHouse

В ClickHouse есть функции initcap и initcapUTF8.

Для обычных строк можно использовать:

SELECT initcap('john DOE') AS result;

Результат:

John Doe

Для UTF-8 строк, особенно если в данных есть кириллица или другие не-ASCII символы, лучше смотреть в сторону UTF-8 варианта:

SELECT initcapUTF8('иван ПЕТРОВ') AS result;

Ожидаемый результат:

Иван Петров

Как и в PostgreSQL, это не делает строку «юридически правильной». Функция просто применяет правило регистра к словам.

Что тестировать перед переносом между СУБД

Если вы пишете учебный тренажёр или переносите запросы между PostgreSQL, MySQL и ClickHouse, не проверяйте INITCAP только на простых строках вроде john doe или hello world.

На них почти всё выглядит хорошо.

Лучше сразу собрать маленький набор проблемных значений:

john DOE
McDONALD
o'brien
jean-luc picard
van der berg
SQL academy
иван ПЕТРОВ
NULL
''

И посмотреть, что вернёт ваша СУБД.

Именно на таких строках становится понятно, можно ли использовать функцию как есть или нужны дополнительные правила.

Практический пример: аккуратный отчёт по пользователям

Допустим, у нас есть таблица:

id | name          | city
---+---------------+-----------------
1  | john DOE      | tbilisi
2  | MARIA IVANOVA | ALMATY
3  | alex petrov   | saint petersburg

Для отчёта можно написать:

SELECT
    id,
    initcap(name) AS display_name,
    initcap(city) AS display_city
FROM users
ORDER BY display_name;

Результат:

id | display_name  | display_city
---+---------------+-----------------
3  | Alex Petrov   | Saint Petersburg
1  | John Doe      | Tbilisi
2  | Maria Ivanova | Almaty

Для простых данных результат выглядит аккуратно и понятно.

Но если в таблицу попадёт строка SQL academy, то INITCAP сделает Sql Academy.

Поэтому для отчётов INITCAP полезен, но перед массовым применением всё равно нужно понимать состав данных.

Практический пример: безопасная чистка имён

Если вы хотите один раз почистить имена в таблице, не начинайте сразу с UPDATE.

Сначала посмотрите изменения:

SELECT
    id,
    name AS old_name,
    initcap(name) AS new_name
FROM users
WHERE name <> initcap(name)
ORDER BY id;

После проверки можно обновить только безопасные строки:

UPDATE users
SET name = initcap(name)
WHERE name <> initcap(name)
  AND name !~ '[''-]'
  AND name !~ '[A-Z]{2,}';

Здесь мы не трогаем строки:

  • с апострофами;
  • с дефисами;
  • с группами заглавных букв, где может быть аббревиатура.

Это всё равно не идеальная универсальная логика, но она показывает правильный подход: массовую чистку нужно делать осторожно, с проверкой и ограничениями.

Коротко

INITCAP — это функция для приведения строки к виду, где каждое слово начинается с заглавной буквы, а остальные буквы становятся строчными.

Пример:

SELECT initcap('john DOE') AS result;

Результат:

John Doe

Функция хорошо подходит для простых случаев:

  • имена;
  • города;
  • страны;
  • категории;
  • отчёты;
  • временное форматирование данных.

Но у неё есть важные ограничения:

  • McDONALD станет Mcdonald;
  • SQL станет Sql;
  • van der Berg может стать Van Der Berg;
  • дефисы и апострофы считаются границами слов;
  • функция не знает культурных, юридических и брендовых правил написания.

Главное правило: используйте INITCAP для отображения и простой нормализации, но не считайте её источником правильного написания имени.

Если данные важные, сначала проверьте результат через SELECT, а уже потом делайте UPDATE. А если формат нужен постоянно, лучше вынести его в отдельное представление, подготовленную колонку или слой обработки данных.

Закрепи на практике

Решай задачи в SQL-тренажёре с мгновенной проверкой и подсказками.

Открыть тренажёр