Когда мы говорим «длина строки», кажется, что всё просто. В слове Moscow шесть букв, значит длина равна 6. В имени Ivan четыре буквы, значит длина равна 4.
Но в базе данных строка — это не только буквы, которые видит человек. Строка ещё хранится в определённой кодировке, например в UTF-8. А в UTF-8 разные символы могут занимать разное количество байт.
Например:
a -- 1 символ, 1 байт
é -- 1 символ, 2 байта
я -- 1 символ, 2 байта
東 -- 1 символ, 3 байта
🙂 -- 1 символ, 4 байта
Именно поэтому в SQL важно понимать разницу между двумя вопросами:
Сколько в строке символов?
и:
Сколько байт занимает строка?
Для первого вопроса используют CHAR_LENGTH.
Для второго — OCTET_LENGTH.
Если перепутать эти функции, ошибка может долго не проявляться на английских тестовых данных, а потом внезапно всплыть на кириллице, акцентах, японских символах или эмодзи.
Что делает CHAR_LENGTH
CHAR_LENGTH возвращает количество символов в строке.
В PostgreSQL можно писать и так:
char_length(string)
и так:
character_length(string)
Это синонимы.
Простой пример:
SELECT char_length('Moscow') AS len;
Результат:
len
---
6
Всё ожидаемо: в слове Moscow шесть символов.
Ещё примеры:
SELECT char_length('SQL') AS len;
Результат:
len
---
3
SELECT char_length('Привет') AS len;
Результат:
len
---
6
В слове «Привет» шесть русских букв, поэтому char_length возвращает 6.
Почему символы и байты — не одно и то же
На обычной латинице символы и байты часто совпадают.
Например:
SELECT
char_length('cafe') AS chars,
octet_length('cafe') AS bytes;
Результат:
chars | bytes
------+------
4 | 4
Слово cafe состоит из четырёх ASCII-символов. Каждый такой символ занимает 1 байт. Поэтому количество символов и количество байт совпало.
Но теперь возьмём слово café, где последняя буква — é с акцентом:
SELECT
char_length('café') AS chars,
octet_length('café') AS bytes;
Результат в UTF-8:
chars | bytes
------+------
4 | 5
Человек видит четыре символа:
c a f é
Но в байтах строка занимает больше места, потому что é в UTF-8 занимает 2 байта.
Вот в этом и есть главная разница:
char_length считает символы;
octet_length считает байты.
Пример с кириллицей
С кириллицей разница видна ещё лучше.
SELECT
char_length('Привет') AS chars,
octet_length('Привет') AS bytes;
Результат в UTF-8:
chars | bytes
------+------
6 | 12
В слове «Привет» шесть букв. Но каждая русская буква в UTF-8 обычно занимает 2 байта. Поэтому байтов получилось 12.
Если вы проверяете длину имени пользователя, вам почти всегда нужны именно символы:
SELECT char_length(name) FROM users;
А не байты:
SELECT octet_length(name) FROM users;
Пользователю важно, что в имени 6 букв, а не то, что база хранит их в 12 байтах.
Пример с иероглифами
Возьмём строку:
東京
Это два японских символа.
SELECT
char_length('東京') AS chars,
octet_length('東京') AS bytes;
Результат в UTF-8:
chars | bytes
------+------
2 | 6
Символов — 2.
Байт — 6.
Если у вас есть ограничение «название города не длиннее 10 символов», такая строка должна пройти. Но если вы ошибочно проверяете байты и ставите лимит 5 байт, строка не пройдёт, хотя визуально она очень короткая.
Пример с эмодзи
Эмодзи тоже хорошо показывают разницу.
SELECT
char_length('🙂') AS chars,
octet_length('🙂') AS bytes;
Результат в UTF-8:
chars | bytes
------+------
1 | 4
Один эмодзи — это один символ, но четыре байта.
Поэтому если вы ограничиваете комментарий 100 символами, пользователь может ввести 100 эмодзи, и по символам это будет 100. Но по байтам такая строка займёт гораздо больше места.
Это не плохо и не хорошо. Просто это разные способы измерения строки.
Когда использовать CHAR_LENGTH
CHAR_LENGTH используют, когда правило связано с тем, как строку видит человек.
Например:
- имя должно быть не короче 2 символов;
- заголовок должен быть не длиннее 100 символов;
- логин должен быть от 3 до 30 символов;
- комментарий должен быть не длиннее 500 символов;
- название курса должно помещаться в лимит по символам.
Пример проверки имён:
SELECT id, name
FROM users
WHERE char_length(name) < 2;
Так мы найдём пользователей, у которых имя короче двух символов.
Пример проверки длинных заголовков:
SELECT id, title
FROM articles
WHERE char_length(title) > 100;
Так можно найти статьи, у которых заголовок превышает лимит в 100 символов.
Валидация имени через CHECK
Если правило важно для данных, его можно закрепить прямо в таблице через CHECK.
Например, имя сотрудника должно быть от 2 до 100 символов:
ALTER TABLE employees
ADD CONSTRAINT employees_name_length_chk
CHECK (char_length(name) BETWEEN 2 AND 100);
Теперь база не позволит записать имя, которое слишком короткое или слишком длинное.
Но есть важный нюанс: CHECK сам по себе не запрещает NULL.
Если колонка name может быть NULL, то проверка длины не сработает так, как многие ожидают. Поэтому, если имя обязательно, колонку лучше дополнительно сделать NOT NULL:
ALTER TABLE employees
ALTER COLUMN name SET NOT NULL;
И уже вместе с этим использовать ограничение длины:
ALTER TABLE employees
ADD CONSTRAINT employees_name_length_chk
CHECK (char_length(name) BETWEEN 2 AND 100);
Так правило будет полным: имя должно быть заполнено и должно иметь нормальную длину.
Когда использовать OCTET_LENGTH
OCTET_LENGTH нужен, когда вас интересует размер строки в байтах.
Это уже не «человеческий» лимит, а технический.
Например:
- внешний сервис принимает ключ не длиннее 191 байта;
- старая система ждёт поле фиксированного размера в байтах;
- нужно проверить размер значения перед отправкой;
- нужно оценить, сколько места занимает строка;
- есть ограничение на байтовую длину индекса или сообщения.
Пример:
SELECT id, external_key
FROM users
WHERE octet_length(external_key) > 191;
Такой запрос ищет ключи, которые превышают лимит в 191 байт.
Обратите внимание: здесь именно байты важны. Если внешний сервис говорит «не больше 191 байта», то char_length уже не подходит. Строка может быть короткой по символам, но длинной по байтам.
Главное правило выбора
Можно запомнить так:
Лимит для человека -> CHAR_LENGTH
Лимит для машины -> OCTET_LENGTH
Если вы проверяете имя, заголовок, логин, описание, комментарий — почти всегда нужен CHAR_LENGTH.
Если вы проверяете размер ключа, байтовый лимит внешней системы, размер сообщения или техническое ограничение — нужен OCTET_LENGTH.
Плохая идея — смешивать эти две меры в одном правиле без причины.
Например, если в форме написано «имя до 20 символов», не надо проверять octet_length(name) <= 20.
Потому что имя на кириллице или японском может визуально быть коротким, но по байтам превысить лимит.
Правильнее: char_length(name) <= 20.
CHAR_LENGTH и VARCHAR(n)
В PostgreSQL ограничение VARCHAR(n) считается по символам, а не по байтам.
Например:
CREATE TABLE users (
name varchar(5)
);
В такую колонку можно записать строку из пяти русских букв:
Мария
Потому что в ней 5 символов.
Хотя в UTF-8 по байтам она занимает больше места.
Это важный момент: если колонка объявлена как varchar(5), логически это значит «до 5 символов», а не «до 5 байт».
Поэтому, когда вы хотите заранее проверить, поместится ли строка в varchar(5), используйте char_length(name) <= 5, а не octet_length(name) <= 5.
Почему LENGTH может запутать
Во многих СУБД есть функция LENGTH.
Проблема в том, что в разных базах она может считать разное.
В PostgreSQL length(text) считает символы, то есть ведёт себя как char_length.
Например:
SELECT length('café') AS len;
Результат:
len
---
4
Но в MySQL LENGTH() считает байты, а символы в MySQL считает CHAR_LENGTH().
Пример для MySQL:
SELECT
LENGTH('café') AS bytes,
CHAR_LENGTH('café') AS chars;
Результат в UTF-8:
bytes | chars
------+------
5 | 4
В ClickHouse похожая история: обычная length() для строк считает байты, а для подсчёта UTF-8 символов используют lengthUTF8().
Из-за этого код вида:
SELECT * FROM users WHERE length(name) <= 20;
может означать разные вещи в разных СУБД.
В PostgreSQL это будет проверка по символам.
В MySQL и ClickHouse — по байтам.
На английских данных разницы может не быть. А на строке café, «Привет» или 東京 результат уже разойдётся.
Почему лучше писать CHAR_LENGTH явно
Если вы пишете запросы только под PostgreSQL, length(text) технически считает символы. Но для учебного кода и переносимых примеров лучше использовать char_length.
Так сразу видно, что вы хотите считать именно символы.
Сравните length(name) <= 20 и char_length(name) <= 20.
Второй вариант читается понятнее: мы проверяем длину имени в символах.
Это особенно важно для новичков. Они видят название функции и сразу понимают смысл проверки.
Проверка данных перед миграцией
Если вы переносите данные между СУБД или меняете правила длины, не тестируйте только строки вроде:
Ivan
Alex
Moscow
На них символы и байты совпадают, поэтому ошибка может остаться незаметной.
Лучше сразу проверить разные типы строк:
Ivan
café
Привет
東京
🙂
''
NULL
Например:
SELECT
value,
char_length(value) AS chars,
octet_length(value) AS bytes
FROM test_strings;
Так вы увидите, где длина в символах и длина в байтах расходятся.
Пример результата:
value | chars | bytes
-------+-------+------
Ivan | 4 | 4
café | 4 | 5
Привет | 6 | 12
東京 | 2 | 6
🙂 | 1 | 4
Такая маленькая тестовая таблица часто помогает поймать ошибку до того, как она попадёт в прод.
CHAR_LENGTH и NULL
Если передать в char_length значение NULL, результат тоже будет NULL.
SELECT char_length(NULL) AS len;
Результат:
len
----
NULL
Это нормально: если значения нет, SQL не может посчитать его длину.
Если вам нужно считать NULL как пустую строку, используйте COALESCE:
SELECT char_length(COALESCE(name, '')) AS name_length
FROM users;
COALESCE(name, '') означает: если name равен NULL, возьми пустую строку.
Тогда:
NULL будет считаться как длина 0;
- обычная строка будет считаться как есть.
Пример:
SELECT
name,
char_length(COALESCE(name, '')) AS name_length
FROM users;
Результат:
name | name_length
------+------------
Ivan | 4
NULL | 0
Maria | 5
Пустая строка
Пустая строка — это не NULL.
У пустой строки есть значение, просто в ней нет символов.
SELECT char_length('') AS len;
Результат:
len
---
0
Это важно для валидации.
Например, если в колонке name лежит пустая строка, то условие char_length(name) < 2 найдёт такую строку.
А если там NULL, условие не сработает как обычное true, потому что результат сравнения с NULL будет неизвестным.
Поэтому для строгой проверки часто комбинируют условия:
SELECT id, name
FROM users
WHERE name IS NULL
OR char_length(name) < 2;
Так мы найдём и пустые, и слишком короткие, и отсутствующие имена.
Обрезка строк по символам
Если нужно обрезать строку до определённой длины для показа, важно резать её по символам, а не по байтам.
Например, заголовок нужно показать максимум в 20 символов:
SELECT
title,
LEFT(title, 20) AS short_title
FROM articles;
В PostgreSQL LEFT(text, n) работает по символам, поэтому он не разрежет русскую букву или иероглиф посередине байтовой последовательности.
Для обычного отображения это то, что нужно.
Но есть отдельная тонкость: символы Unicode и «видимые знаки» не всегда совпадают. Об этом ниже.
Символы и видимые знаки: ещё одна тонкость
Обычно char_length даёт то, что ожидает человек.
Например:
SELECT char_length('Привет');
Результат:
6
Но в Unicode есть сложные случаи.
Например, букву с акцентом можно записать двумя способами.
Первый способ — готовый символ:
á
В PostgreSQL:
SELECT
char_length('á') AS chars,
octet_length('á') AS bytes;
Результат в UTF-8:
chars | bytes
------+------
1 | 2
Второй способ — буква a плюс отдельный комбинируемый акцент:
SELECT
char_length(U&'a\0301') AS chars,
octet_length(U&'a\0301') AS bytes;
Результат может быть таким:
chars | bytes
------+------
2 | 3
Глазами это может выглядеть почти как один знак, но технически строка состоит из двух Unicode-частей: буквы a и отдельного акцента.
Похожая история бывает с некоторыми эмодзи, флагами и символами с модификаторами.
Например флаг может выглядеть как один значок, но технически состоять из нескольких Unicode-символов.
Поэтому важно понимать:
CHAR_LENGTH считает символы строки, но не всегда считает "видимые глазами знаки" так, как ожидает человек.
Для большинства бизнес-проверок это нормально. Но если вы делаете сложный редактор текста, лимиты для социальных сетей или точную работу с отображаемыми символами, такую логику часто выносят на уровень приложения и используют специальные библиотеки для работы с grapheme clusters.
CHAR_LENGTH в WHERE и производительность
char_length удобно использовать в фильтрах:
SELECT id, name
FROM users
WHERE char_length(name) > 100;
Но нужно понимать, что база должна вычислить длину для строк, которые проверяет.
Обычный индекс по name не равен индексу по char_length(name). Поэтому на большой таблице такой запрос может быть дорогим.
Если проверка длины нужна редко, это нормально.
Если она нужна постоянно, можно подумать о других решениях:
CHECK-ограничение, чтобы плохие данные вообще не попадали в таблицу;
- отдельная generated-колонка с длиной;
- индекс по выражению
char_length(name);
- предварительная чистка данных.
Например в PostgreSQL можно создать индекс по выражению:
CREATE INDEX idx_users_name_length
ON users (char_length(name));
Тогда запросы, которые фильтруют по длине имени, могут использовать этот индекс.
Но создавать такие индексы стоит только под реальные частые запросы. Индекс занимает место и обновляется при изменении данных.
Пример: найти некорректные имена
Допустим, в таблице пользователей есть имена, и правило такое:
Имя должно быть от 2 до 50 символов.
Проверим нарушения:
SELECT
id,
name,
char_length(name) AS name_length
FROM users
WHERE name IS NULL
OR char_length(name) NOT BETWEEN 2 AND 50
ORDER BY id;
Результат может быть таким:
id | name | name_length
---+------+------------
3 | A | 1
7 | NULL | NULL
12 | | 0
Такой запрос удобно использовать для аудита данных перед добавлением CHECK-ограничения.
Пример: найти слишком длинные заголовки
Допустим, заголовок статьи должен быть не длиннее 80 символов.
SELECT
id,
title,
char_length(title) AS title_length
FROM articles
WHERE char_length(title) > 80
ORDER BY title_length DESC;
Так можно быстро найти статьи, которые не проходят ограничение.
Если нужно обрезать заголовок для показа:
SELECT
id,
CASE
WHEN char_length(title) > 80
THEN LEFT(title, 77) || '...'
ELSE title
END AS display_title
FROM articles;
Здесь логика такая:
- если заголовок длиннее 80 символов, берём первые 77 символов и добавляем
...;
- если он короче или равен 80, показываем как есть.
Пример: сравнить символы и байты в одной выдаче
Для обучения и отладки полезно выводить обе длины рядом.
SELECT
id,
name,
char_length(name) AS chars,
octet_length(name) AS bytes
FROM users
ORDER BY id;
Пример результата:
id | name | chars | bytes
---+--------+-------+------
1 | Ivan | 4 | 4
2 | Мария | 5 | 10
3 | café | 4 | 5
4 | 東京 | 2 | 6
Так сразу видно, почему проверка по байтам и проверка по символам — это разные вещи.
CHAR_LENGTH в PostgreSQL
В PostgreSQL можно использовать char_length(text) или character_length(text).
Обе функции делают одно и то же.
Пример:
SELECT char_length('database') AS len;
Результат:
len
---
8
Также в PostgreSQL length(text) считает символы:
SELECT length('database') AS len;
Результат:
len
---
8
Но для переносимого и понятного кода лучше писать char_length, если вы именно считаете символы.
Для байтов используйте octet_length(text).
Пример:
SELECT octet_length('Привет') AS bytes;
Результат в UTF-8:
bytes
-----
12
CHAR_LENGTH в MySQL
В MySQL для подсчёта символов используют CHAR_LENGTH(str).
Пример:
SELECT CHAR_LENGTH('café') AS chars;
Результат:
chars
-----
4
А LENGTH() в MySQL считает байты:
SELECT LENGTH('café') AS bytes;
Результат в UTF-8:
bytes
-----
5
Поэтому в MySQL важно не путать:
CHAR_LENGTH -> символы
LENGTH -> байты
Если вы переносите запросы из PostgreSQL в MySQL, особенно внимательно проверяйте все места, где написано length().
Длина строк в ClickHouse
В ClickHouse обычная функция length(str) для строк считает байты.
Для подсчёта UTF-8 символов используют lengthUTF8(str).
Пример:
SELECT
length('Привет') AS bytes,
lengthUTF8('Привет') AS chars;
Результат в UTF-8:
bytes | chars
------+------
12 | 6
Поэтому при переносе логики валидации в ClickHouse нужно быть особенно внимательным: если вам нужны именно символы, используйте lengthUTF8.
Коротко
CHAR_LENGTH считает количество символов в строке.
SELECT char_length('café');
Результат:
4
OCTET_LENGTH считает количество байт.
SELECT octet_length('café');
Результат в UTF-8:
5
Разница появляется на строках вне обычного ASCII:
café -- 4 символа, 5 байт
Привет -- 6 символов, 12 байт
東京 -- 2 символа, 6 байт
🙂 -- 1 символ, 4 байта
Главные правила:
- для имён, заголовков, логинов и комментариев используйте
CHAR_LENGTH;
- для технических байтовых лимитов используйте
OCTET_LENGTH;
- не полагайтесь на
LENGTH в переносимом SQL, потому что в разных СУБД она считает разное;
- в PostgreSQL
length(text) считает символы;
- в MySQL
LENGTH() считает байты, а CHAR_LENGTH() — символы;
- в ClickHouse
length() считает байты, а lengthUTF8() — UTF-8 символы;
- перед миграцией проверяйте строки с кириллицей, акцентами, иероглифами, эмодзи, пустыми строками и
NULL;
- помните, что «символы» и «видимые глазами знаки» в Unicode иногда тоже отличаются.
Самое простое правило: если ограничение написано для человека — считайте символы через CHAR_LENGTH. Если ограничение написано для системы, протокола, индекса или байтового формата — считайте байты через OCTET_LENGTH.
Когда мы говорим «длина строки», кажется, что всё просто. В слове
Moscowшесть букв, значит длина равна 6. В имениIvanчетыре буквы, значит длина равна 4.Но в базе данных строка — это не только буквы, которые видит человек. Строка ещё хранится в определённой кодировке, например в UTF-8. А в UTF-8 разные символы могут занимать разное количество байт.
Например:
Именно поэтому в SQL важно понимать разницу между двумя вопросами:
и:
Для первого вопроса используют
CHAR_LENGTH.Для второго —
OCTET_LENGTH.Если перепутать эти функции, ошибка может долго не проявляться на английских тестовых данных, а потом внезапно всплыть на кириллице, акцентах, японских символах или эмодзи.
Что делает CHAR_LENGTH
CHAR_LENGTHвозвращает количество символов в строке.В PostgreSQL можно писать и так:
char_length(string)и так:
character_length(string)Это синонимы.
Простой пример:
SELECT char_length('Moscow') AS len;Результат:
Всё ожидаемо: в слове
Moscowшесть символов.Ещё примеры:
SELECT char_length('SQL') AS len;Результат:
Результат:
В слове «Привет» шесть русских букв, поэтому
char_lengthвозвращает 6.Почему символы и байты — не одно и то же
На обычной латинице символы и байты часто совпадают.
Например:
SELECT char_length('cafe') AS chars, octet_length('cafe') AS bytes;Результат:
Слово
cafeсостоит из четырёх ASCII-символов. Каждый такой символ занимает 1 байт. Поэтому количество символов и количество байт совпало.Но теперь возьмём слово café, где последняя буква — é с акцентом:
Результат в UTF-8:
Человек видит четыре символа:
Но в байтах строка занимает больше места, потому что é в UTF-8 занимает 2 байта.
Вот в этом и есть главная разница:
char_lengthсчитает символы;octet_lengthсчитает байты.Пример с кириллицей
С кириллицей разница видна ещё лучше.
Результат в UTF-8:
В слове «Привет» шесть букв. Но каждая русская буква в UTF-8 обычно занимает 2 байта. Поэтому байтов получилось 12.
Если вы проверяете длину имени пользователя, вам почти всегда нужны именно символы:
SELECT char_length(name) FROM users;А не байты:
SELECT octet_length(name) FROM users;Пользователю важно, что в имени 6 букв, а не то, что база хранит их в 12 байтах.
Пример с иероглифами
Возьмём строку:
Это два японских символа.
Результат в UTF-8:
Символов — 2.
Байт — 6.
Если у вас есть ограничение «название города не длиннее 10 символов», такая строка должна пройти. Но если вы ошибочно проверяете байты и ставите лимит 5 байт, строка не пройдёт, хотя визуально она очень короткая.
Пример с эмодзи
Эмодзи тоже хорошо показывают разницу.
Результат в UTF-8:
Один эмодзи — это один символ, но четыре байта.
Поэтому если вы ограничиваете комментарий 100 символами, пользователь может ввести 100 эмодзи, и по символам это будет 100. Но по байтам такая строка займёт гораздо больше места.
Это не плохо и не хорошо. Просто это разные способы измерения строки.
Когда использовать CHAR_LENGTH
CHAR_LENGTHиспользуют, когда правило связано с тем, как строку видит человек.Например:
Пример проверки имён:
SELECT id, name FROM users WHERE char_length(name) < 2;Так мы найдём пользователей, у которых имя короче двух символов.
Пример проверки длинных заголовков:
SELECT id, title FROM articles WHERE char_length(title) > 100;Так можно найти статьи, у которых заголовок превышает лимит в 100 символов.
Валидация имени через CHECK
Если правило важно для данных, его можно закрепить прямо в таблице через
CHECK.Например, имя сотрудника должно быть от 2 до 100 символов:
ALTER TABLE employees ADD CONSTRAINT employees_name_length_chk CHECK (char_length(name) BETWEEN 2 AND 100);Теперь база не позволит записать имя, которое слишком короткое или слишком длинное.
Но есть важный нюанс:
CHECKсам по себе не запрещаетNULL.Если колонка
nameможет бытьNULL, то проверка длины не сработает так, как многие ожидают. Поэтому, если имя обязательно, колонку лучше дополнительно сделатьNOT NULL:ALTER TABLE employees ALTER COLUMN name SET NOT NULL;И уже вместе с этим использовать ограничение длины:
ALTER TABLE employees ADD CONSTRAINT employees_name_length_chk CHECK (char_length(name) BETWEEN 2 AND 100);Так правило будет полным: имя должно быть заполнено и должно иметь нормальную длину.
Когда использовать OCTET_LENGTH
OCTET_LENGTHнужен, когда вас интересует размер строки в байтах.Это уже не «человеческий» лимит, а технический.
Например:
Пример:
SELECT id, external_key FROM users WHERE octet_length(external_key) > 191;Такой запрос ищет ключи, которые превышают лимит в 191 байт.
Обратите внимание: здесь именно байты важны. Если внешний сервис говорит «не больше 191 байта», то
char_lengthуже не подходит. Строка может быть короткой по символам, но длинной по байтам.Главное правило выбора
Можно запомнить так:
Если вы проверяете имя, заголовок, логин, описание, комментарий — почти всегда нужен
CHAR_LENGTH.Если вы проверяете размер ключа, байтовый лимит внешней системы, размер сообщения или техническое ограничение — нужен
OCTET_LENGTH.Плохая идея — смешивать эти две меры в одном правиле без причины.
Например, если в форме написано «имя до 20 символов», не надо проверять
octet_length(name) <= 20.Потому что имя на кириллице или японском может визуально быть коротким, но по байтам превысить лимит.
Правильнее:
char_length(name) <= 20.CHAR_LENGTH и VARCHAR(n)
В PostgreSQL ограничение
VARCHAR(n)считается по символам, а не по байтам.Например:
CREATE TABLE users ( name varchar(5) );В такую колонку можно записать строку из пяти русских букв:
Потому что в ней 5 символов.
Хотя в UTF-8 по байтам она занимает больше места.
Это важный момент: если колонка объявлена как
varchar(5), логически это значит «до 5 символов», а не «до 5 байт».Поэтому, когда вы хотите заранее проверить, поместится ли строка в
varchar(5), используйтеchar_length(name) <= 5, а неoctet_length(name) <= 5.Почему LENGTH может запутать
Во многих СУБД есть функция
LENGTH.Проблема в том, что в разных базах она может считать разное.
В PostgreSQL
length(text)считает символы, то есть ведёт себя какchar_length.Например:
Результат:
Но в MySQL
LENGTH()считает байты, а символы в MySQL считаетCHAR_LENGTH().Пример для MySQL:
Результат в UTF-8:
В ClickHouse похожая история: обычная
length()для строк считает байты, а для подсчёта UTF-8 символов используютlengthUTF8().Из-за этого код вида:
SELECT * FROM users WHERE length(name) <= 20;может означать разные вещи в разных СУБД.
В PostgreSQL это будет проверка по символам.
В MySQL и ClickHouse — по байтам.
На английских данных разницы может не быть. А на строке café, «Привет» или 東京 результат уже разойдётся.
Почему лучше писать CHAR_LENGTH явно
Если вы пишете запросы только под PostgreSQL,
length(text)технически считает символы. Но для учебного кода и переносимых примеров лучше использоватьchar_length.Так сразу видно, что вы хотите считать именно символы.
Сравните
length(name) <= 20иchar_length(name) <= 20.Второй вариант читается понятнее: мы проверяем длину имени в символах.
Это особенно важно для новичков. Они видят название функции и сразу понимают смысл проверки.
Проверка данных перед миграцией
Если вы переносите данные между СУБД или меняете правила длины, не тестируйте только строки вроде:
На них символы и байты совпадают, поэтому ошибка может остаться незаметной.
Лучше сразу проверить разные типы строк:
Например:
SELECT value, char_length(value) AS chars, octet_length(value) AS bytes FROM test_strings;Так вы увидите, где длина в символах и длина в байтах расходятся.
Пример результата:
Такая маленькая тестовая таблица часто помогает поймать ошибку до того, как она попадёт в прод.
CHAR_LENGTH и NULL
Если передать в
char_lengthзначениеNULL, результат тоже будетNULL.SELECT char_length(NULL) AS len;Результат:
Это нормально: если значения нет, SQL не может посчитать его длину.
Если вам нужно считать
NULLкак пустую строку, используйтеCOALESCE:SELECT char_length(COALESCE(name, '')) AS name_length FROM users;COALESCE(name, '')означает: еслиnameравенNULL, возьми пустую строку.Тогда:
NULLбудет считаться как длина 0;Пример:
SELECT name, char_length(COALESCE(name, '')) AS name_length FROM users;Результат:
Пустая строка
Пустая строка — это не
NULL.У пустой строки есть значение, просто в ней нет символов.
SELECT char_length('') AS len;Результат:
Это важно для валидации.
Например, если в колонке
nameлежит пустая строка, то условиеchar_length(name) < 2найдёт такую строку.А если там
NULL, условие не сработает как обычноеtrue, потому что результат сравнения сNULLбудет неизвестным.Поэтому для строгой проверки часто комбинируют условия:
SELECT id, name FROM users WHERE name IS NULL OR char_length(name) < 2;Так мы найдём и пустые, и слишком короткие, и отсутствующие имена.
Обрезка строк по символам
Если нужно обрезать строку до определённой длины для показа, важно резать её по символам, а не по байтам.
Например, заголовок нужно показать максимум в 20 символов:
SELECT title, LEFT(title, 20) AS short_title FROM articles;В PostgreSQL
LEFT(text, n)работает по символам, поэтому он не разрежет русскую букву или иероглиф посередине байтовой последовательности.Для обычного отображения это то, что нужно.
Но есть отдельная тонкость: символы Unicode и «видимые знаки» не всегда совпадают. Об этом ниже.
Символы и видимые знаки: ещё одна тонкость
Обычно
char_lengthдаёт то, что ожидает человек.Например:
Результат:
Но в Unicode есть сложные случаи.
Например, букву с акцентом можно записать двумя способами.
Первый способ — готовый символ:
В PostgreSQL:
Результат в UTF-8:
Второй способ — буква
aплюс отдельный комбинируемый акцент:SELECT char_length(U&'a\0301') AS chars, octet_length(U&'a\0301') AS bytes;Результат может быть таким:
Глазами это может выглядеть почти как один знак, но технически строка состоит из двух Unicode-частей: буквы
aи отдельного акцента.Похожая история бывает с некоторыми эмодзи, флагами и символами с модификаторами.
Например флаг может выглядеть как один значок, но технически состоять из нескольких Unicode-символов.
Поэтому важно понимать:
Для большинства бизнес-проверок это нормально. Но если вы делаете сложный редактор текста, лимиты для социальных сетей или точную работу с отображаемыми символами, такую логику часто выносят на уровень приложения и используют специальные библиотеки для работы с grapheme clusters.
CHAR_LENGTH в WHERE и производительность
char_lengthудобно использовать в фильтрах:SELECT id, name FROM users WHERE char_length(name) > 100;Но нужно понимать, что база должна вычислить длину для строк, которые проверяет.
Обычный индекс по
nameне равен индексу поchar_length(name). Поэтому на большой таблице такой запрос может быть дорогим.Если проверка длины нужна редко, это нормально.
Если она нужна постоянно, можно подумать о других решениях:
CHECK-ограничение, чтобы плохие данные вообще не попадали в таблицу;char_length(name);Например в PostgreSQL можно создать индекс по выражению:
CREATE INDEX idx_users_name_length ON users (char_length(name));Тогда запросы, которые фильтруют по длине имени, могут использовать этот индекс.
Но создавать такие индексы стоит только под реальные частые запросы. Индекс занимает место и обновляется при изменении данных.
Пример: найти некорректные имена
Допустим, в таблице пользователей есть имена, и правило такое:
Проверим нарушения:
SELECT id, name, char_length(name) AS name_length FROM users WHERE name IS NULL OR char_length(name) NOT BETWEEN 2 AND 50 ORDER BY id;Результат может быть таким:
Такой запрос удобно использовать для аудита данных перед добавлением
CHECK-ограничения.Пример: найти слишком длинные заголовки
Допустим, заголовок статьи должен быть не длиннее 80 символов.
SELECT id, title, char_length(title) AS title_length FROM articles WHERE char_length(title) > 80 ORDER BY title_length DESC;Так можно быстро найти статьи, которые не проходят ограничение.
Если нужно обрезать заголовок для показа:
SELECT id, CASE WHEN char_length(title) > 80 THEN LEFT(title, 77) || '...' ELSE title END AS display_title FROM articles;Здесь логика такая:
...;Пример: сравнить символы и байты в одной выдаче
Для обучения и отладки полезно выводить обе длины рядом.
SELECT id, name, char_length(name) AS chars, octet_length(name) AS bytes FROM users ORDER BY id;Пример результата:
Так сразу видно, почему проверка по байтам и проверка по символам — это разные вещи.
CHAR_LENGTH в PostgreSQL
В PostgreSQL можно использовать
char_length(text)илиcharacter_length(text).Обе функции делают одно и то же.
Пример:
SELECT char_length('database') AS len;Результат:
Также в PostgreSQL
length(text)считает символы:SELECT length('database') AS len;Результат:
Но для переносимого и понятного кода лучше писать
char_length, если вы именно считаете символы.Для байтов используйте
octet_length(text).Пример:
Результат в UTF-8:
CHAR_LENGTH в MySQL
В MySQL для подсчёта символов используют
CHAR_LENGTH(str).Пример:
Результат:
А
LENGTH()в MySQL считает байты:Результат в UTF-8:
Поэтому в MySQL важно не путать:
Если вы переносите запросы из PostgreSQL в MySQL, особенно внимательно проверяйте все места, где написано
length().Длина строк в ClickHouse
В ClickHouse обычная функция
length(str)для строк считает байты.Для подсчёта UTF-8 символов используют
lengthUTF8(str).Пример:
Результат в UTF-8:
Поэтому при переносе логики валидации в ClickHouse нужно быть особенно внимательным: если вам нужны именно символы, используйте
lengthUTF8.Коротко
CHAR_LENGTHсчитает количество символов в строке.Результат:
OCTET_LENGTHсчитает количество байт.Результат в UTF-8:
Разница появляется на строках вне обычного ASCII:
Главные правила:
CHAR_LENGTH;OCTET_LENGTH;LENGTHв переносимом SQL, потому что в разных СУБД она считает разное;length(text)считает символы;LENGTH()считает байты, аCHAR_LENGTH()— символы;length()считает байты, аlengthUTF8()— UTF-8 символы;NULL;Самое простое правило: если ограничение написано для человека — считайте символы через
CHAR_LENGTH. Если ограничение написано для системы, протокола, индекса или байтового формата — считайте байты черезOCTET_LENGTH.