Иногда данные в таблицу попадают как попало: кто-то написал имя капсом, кто-то маленькими буквами, кто-то случайно включил 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. А если формат нужен постоянно, лучше вынести его в отдельное представление, подготовленную колонку или слой обработки данных.
Иногда данные в таблицу попадают как попало: кто-то написал имя капсом, кто-то маленькими буквами, кто-то случайно включил Caps Lock в форме регистрации.
В итоге в базе лежит неаккуратная смесь:
Для отчёта, интерфейса или выгрузки такие значения хочется привести к нормальному виду:
В PostgreSQL для этого есть функция
INITCAP. Она делает первую букву каждого слова заглавной, а остальные буквы — строчными.SELECT initcap('john DOE') AS name;Результат:
На первый взгляд кажется, что
INITCAPидеально подходит для имён, городов и названий. Но есть нюанс: функция не знает правил написания реальных фамилий, брендов и географических названий. Она просто применяет механическое правило к строке.Поэтому
INITCAPхорош для быстрой нормализации простых данных, но его нельзя слепо использовать там, где важна точная, каноническая форма имени.Что делает INITCAP
INITCAPприводит строку к так называемому title case: первая буква каждого слова становится заглавной, а остальные буквы в этом слове становятся строчными.Примеры:
SELECT initcap('hello world') AS result;Результат:
SELECT initcap('JOHN DOE') AS result;Результат:
SELECT initcap('maria IVANOVA') AS result;Результат:
Функция не пытается угадать, как строка должна выглядеть «по смыслу». Она не знает, что перед ней: имя человека, город, название компании или обычный текст. Она просто берёт каждое слово и приводит его к виду:
Как INITCAP понимает слова
Для
INITCAPслово — это последовательность букв и цифр. Всё, что не является буквой или цифрой, считается границей между словами.Границей может быть:
#;Например:
SELECT initcap('order #42 paid') AS result;Результат:
Слова здесь —
order,42иpaid. Символ#не считается частью слова, поэтому он просто остаётся в строке как разделитель.Ещё пример:
SELECT initcap('hello.world') AS result;Результат:
Точка стала границей между словами, поэтому
helloпревратилось вHello, аworld— вWorld.Простой пример с таблицей users
Представим таблицу пользователей:
Чтобы вывести имена аккуратно, можно написать:
SELECT id, initcap(name) AS display_name FROM users ORDER BY id;Результат:
Такой вариант хорошо подходит для показа данных в отчёте, админке или учебном примере.
Важно: исходное значение в таблице при этом не меняется. Мы просто красиво вывели его в запросе.
INITCAP для городов и стран
INITCAPудобно использовать не только для имён, но и для простых географических названий.Например, в таблице есть города:
Запрос:
SELECT initcap(city) AS city_name FROM users;Результат:
Если нужно сгруппировать пользователей по стране, а страны записаны в разном регистре, можно привести их к одному виду:
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;Результат:
Но фамилия может правильно писаться как:
INITCAPэтого не знает. Для него это обычное слово: первая буква заглавная, остальные строчные.Ещё пример:
SELECT initcap('iphone store') AS result;Результат:
Но бренд пишется как
iPhone, а неIphone.Поэтому
INITCAPподходит для простого приведения регистра, но не подходит для восстановления правильного написания имён, фамилий, брендов и названий компаний.Где INITCAP может испортить данные
Самые частые проблемы возникают с реальными именами и названиями.
Фамилии с внутренними заглавными буквами
SELECT initcap('McDONALD') AS result;Результат:
Функция опустила все буквы после первой в нижний регистр. Поэтому
McDONALDпревратилось не вMcDonald, а вMcdonald.То же самое может случиться с фамилиями и именами вроде:
INITCAPне знает этих правил.Частицы в фамилиях
В некоторых фамилиях маленькие слова могут писаться со строчной буквы:
Но
INITCAPсделает так:SELECT initcap('van der berg') AS result;Результат:
Технически функция сработала правильно, но культурно или юридически такое написание может быть неверным.
Апострофы
Апостроф считается границей слова.
SELECT initcap('o''brien') AS result;Результат:
Иногда это как раз то, что нужно. Но не всегда.
Например:
SELECT initcap('d''artagnan') AS result;Результат:
А в некоторых контекстах ожидаемая форма может быть другой. Функция не понимает культурные правила, она просто видит границу слова после апострофа.
Дефисы
Дефис тоже считается границей слова.
SELECT initcap('jean-luc picard') AS result;Результат:
В этом примере результат выглядит хорошо. Но правило всё равно механическое: после дефиса новое слово начинается с заглавной буквы.
Например:
SELECT initcap('x-ray department') AS result;Результат:
А в реальном тексте вам может быть нужен вариант
X-ray department.Аббревиатуры
INITCAPплохо подходит для аббревиатур.SELECT initcap('USA') AS result;Результат:
SELECT initcap('SQL course') AS result;Результат:
Для человека понятно, что
USAиSQLдолжны остаться капсом. НоINITCAPэтого не знает.Поэтому если в данных есть аббревиатуры, бренды или технические термины, после
INITCAPих нужно обрабатывать отдельно.INITCAP и русские строки
Для русских строк
INITCAPтоже может быть полезен.Результат:
Результат:
Для простых русских имён и городов это часто работает ожидаемо.
Но правило остаётся тем же: первая буква слова заглавная, остальные строчные. Если в строке есть необычное написание, аббревиатура или бренд, функция может привести его к нежелательному виду.
Например:
SELECT initcap('sql arena') AS result;Результат:
А если название продукта должно быть
SQL Arena, тоINITCAPуже не подходит как единственное решение.Когда INITCAP можно использовать смело
INITCAPхорошо подходит для ситуаций, где данные простые и от них не требуется юридическая точность.Например:
SELECT.Пример:
SELECT id, initcap(first_name) AS first_name, initcap(last_name) AS last_name FROM users;Если данные простые, результат будет аккуратным:
Для учебных задач, отчётов и базовой чистки данных это хороший инструмент.
Когда 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;Так вы увидите старое и новое значение рядом:
И сразу станет видно, где
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;Результат:
Это нормальное поведение SQL: если значения нет, то и форматировать нечего.
Если для отчёта нужно заменить
NULLна пустую строку, можно использоватьCOALESCE:SELECT initcap(COALESCE(name, '')) AS display_name FROM users;COALESCE(name, '')означает: еслиnameравенNULL, возьми пустую строку.Пример:
Запрос:
SELECT initcap(COALESCE(name, '')) AS display_name FROM users;Результат:
В первой строке будет пустое значение вместо
NULL.INITCAP в GROUP BY
INITCAPиногда используют для группировки данных, когда значения отличаются только регистром.Например:
Запрос:
SELECT initcap(country) AS country_name, count(*) AS users_count FROM users GROUP BY initcap(country) ORDER BY users_count DESC;Результат:
Для небольших таблиц это удобно. Но на больших таблицах есть нюанс: база должна вычислить
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.Синтаксис простой:
Пример:
SELECT initcap('hello SQL world') AS result;Результат:
Обратите внимание:
SQLпревратилось вSql. Это ещё раз показывает, чтоINITCAPне сохраняет аббревиатуры.Если нужно сохранить
SQLкапсом, придётся обрабатывать такие случаи отдельно.Например:
SELECT replace(initcap('hello SQL world'), 'Sql', 'SQL') AS result;Результат:
Но такой подход подходит только для простых и известных исключений. Если исключений много, лучше держать словарь правильных написаний или решать это на уровне приложения.
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', результат будет:Разберём выражение:
SELECT UPPER(SUBSTRING(name, 1, 1)) FROM users;берёт первую букву и делает её заглавной.
SELECT LOWER(SUBSTRING(name, 2)) FROM users;берёт всё, начиная со второго символа, и делает строчным.
CONCAT(...)склеивает первую букву и остальную часть строки.Но это работает нормально только для одного слова. Для строки вроде
john doeтакой запрос даст:А не:
Чтобы полноценно повторить
INITCAPдля нескольких слов в MySQL, обычно используют пользовательскую функцию, более сложный SQL или выносят эту логику в приложение.INITCAP в ClickHouse
В ClickHouse есть функции
initcapиinitcapUTF8.Для обычных строк можно использовать:
SELECT initcap('john DOE') AS result;Результат:
Для UTF-8 строк, особенно если в данных есть кириллица или другие не-ASCII символы, лучше смотреть в сторону UTF-8 варианта:
Ожидаемый результат:
Как и в PostgreSQL, это не делает строку «юридически правильной». Функция просто применяет правило регистра к словам.
Что тестировать перед переносом между СУБД
Если вы пишете учебный тренажёр или переносите запросы между PostgreSQL, MySQL и ClickHouse, не проверяйте
INITCAPтолько на простых строках вродеjohn doeилиhello world.На них почти всё выглядит хорошо.
Лучше сразу собрать маленький набор проблемных значений:
И посмотреть, что вернёт ваша СУБД.
Именно на таких строках становится понятно, можно ли использовать функцию как есть или нужны дополнительные правила.
Практический пример: аккуратный отчёт по пользователям
Допустим, у нас есть таблица:
Для отчёта можно написать:
SELECT id, initcap(name) AS display_name, initcap(city) AS display_city FROM users ORDER BY display_name;Результат:
Для простых данных результат выглядит аккуратно и понятно.
Но если в таблицу попадёт строка
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;Результат:
Функция хорошо подходит для простых случаев:
Но у неё есть важные ограничения:
McDONALDстанетMcdonald;SQLстанетSql;van der Bergможет статьVan Der Berg;Главное правило: используйте
INITCAPдля отображения и простой нормализации, но не считайте её источником правильного написания имени.Если данные важные, сначала проверьте результат через
SELECT, а уже потом делайтеUPDATE. А если формат нужен постоянно, лучше вынести его в отдельное представление, подготовленную колонку или слой обработки данных.