Dan l-artiklu bħalissa huwa bir-Russu — it-traduzzjoni bl-Ingliż għaddejja.
Когда нужно превратить 42 в 000042 или выровнять имена в колонке фиксированной ширины, в дело вступают LPAD и RPAD. Это функции дополнения строк: они добивают значение до заданной длины слева или справа символами-заполнителями. Разберём типичные сценарии и подводные камни.
У дополнения две практические задачи. Первая — ведущие нули: LPAD превращает id вроде 7 в код 000007, чтобы счета, заказы и платёжные ссылки имели одинаковую ширину и сортировались как числа. Вторая — выравнивание: RPAD добивает имена и коды стран пробелами до фиксированной длины, когда выгрузка идёт без разделителей, как в банковских и legacy-форматах. В обоих случаях исходное значение не меняется — мы лишь строим из него представление нужной ширины, поэтому форматирование держат в SELECT, view или generated column, а не в хранимой колонке.
Сигнатура и базовый принцип
Обе функции принимают три аргумента: исходную строку, целевую длину и строку-заполнитель.
SELECT LPAD('42', 6, '0') AS padded_left,
RPAD('John', 10, '.') AS padded_right;
Ключевые правила:
LPAD(string, length, fill) добавляет fill слева, пока строка не достигнет length.
RPAD(string, length, fill) делает то же самое, но справа.
- Третий аргумент необязателен: по умолчанию заполнитель — пробел.
- Аргумент должен быть текстом, поэтому числовые колонки сначала приводите к типу:
id::text.
SELECT LPAD(id::text, 6, '0') AS user_code
FROM users
ORDER BY id
LIMIT 5;
Zero-padding для id и номеров счетов
Самый частый случай — человекочитаемые коды с ведущими нулями. Идентификатор пользователя 7 превращается в USR-000007, номер заказа — в счёт фиксированного формата.
SELECT id,
'USR-' || LPAD(id::text, 6, '0') AS user_code
FROM users
ORDER BY id;
SELECT o.id,
'INV-2026-' || LPAD(o.id::text, 6, '0') AS invoice_no
FROM orders o
WHERE o.status = 'paid'
ORDER BY o.id;
Почему именно LPAD, а не хранение готовой строки? Код всегда выводится из числового id, остаётся отсортированным как число и не занимает лишнего места в таблице. Форматирование — забота слоя представления, а не хранения.
Обрезка: когда строка длиннее цели
Главная ловушка: если исходная строка длиннее length, LPAD и RPAD не расширяют результат, а обрезают его до целевой длины. Никакой ошибки при этом не возникает.
SELECT LPAD('1234567', 6, '0') AS clipped;
Это критично для счётчиков: когда id вырастет до семи цифр, поле в шесть символов молча потеряет первую цифру. Защищайтесь явной проверкой ширины:
SELECT LPAD(id::text, GREATEST(6, LENGTH(id::text)), '0') AS safe_code
FROM users;
Ловушка: заполнитель может быть длиннее одного символа, и тогда он повторяется и обрезается по символам. LPAD('7', 5, 'ab') даст abab7 — заполнитель циклически дополняется слева, а лишние символы отбрасываются.
Колонки фиксированной ширины для выгрузок
Старые системы и банковские форматы часто требуют файлы с колонками фиксированной ширины, без разделителей. Здесь RPAD выравнивает текст влево, а LPAD — числа вправо.
SELECT RPAD(u.name, 20, ' ')
|| RPAD(u.country, 2, ' ')
|| LPAD(o.amount::text, 12, '0') AS export_line
FROM users u
JOIN orders o ON o.user_id = u.id
ORDER BY u.id;
Полезные приёмы:
- Текстовые поля (имена, отделы) выравнивайте
RPAD влево пробелами.
- Числовые поля (суммы, количества) выравнивайте
LPAD вправо нулями.
- Помните про обрезку: длинное
name будет урезано до 20 символов. Если это недопустимо, заранее проверяйте LENGTH(name).
SELECT RPAD(name, 25, ' ') || LPAD(salary::text, 10, ' ') AS row_line
FROM employees
ORDER BY dept, name;
Отличия MySQL и ClickHouse
Базовый синтаксис LPAD/RPAD совпадает в PostgreSQL и MySQL, но детали расходятся:
- В MySQL у
LPAD/RPAD все три аргумента обязательны — заполнитель по умолчанию не подставляется.
- MySQL так же обрезает строки, которые длиннее цели.
- MySQL мягче с типами: число часто приводится к строке неявно, но надёжнее писать
CAST(id AS CHAR).
SELECT LPAD(CAST(id AS CHAR), 6, '0') AS user_code
FROM users;
В ClickHouse функции называются leftPad и rightPad (есть и алиасы LPAD/RPAD), но логика та же:
SELECT leftPad(toString(id), 6, '0') AS user_code
FROM users;
Расхождения между движками прячутся не в обычных значениях, а в краях: что вернёт LPAD для NULL, для уже слишком длинной строки, для пустого заполнителя '' и для строки с многобайтовыми символами. По длине здесь важна разница между числом символов и числом байтов: в PostgreSQL и ClickHouse LPAD/leftPad отсчитывают символы, так что кириллица или эмодзи дают ту ширину, которую вы ожидаете, тогда как байтовый подсчёт в выгрузке фиксированной ширины ломает разметку колонок. Перед переносом между PostgreSQL, MySQL и ClickHouse прогоните маленькую таблицу с NULL, пустой строкой, значением длиннее цели и Unicode-именем — на чистых данных движки совпадают, а ровно на этих случаях расходятся.
И ещё одно про производительность: LPAD(id::text, 6, '0') в WHERE или JOIN считается по каждой строке и закрывает путь к обычному индексу по id. Если вы фильтруете или соединяете именно по дополненному коду, держите сравнение на стороне исходного числа (id = 42), а LPAD оставляйте для вывода. Когда без поиска по готовому коду не обойтись, материализуйте его в generated column и постройте индекс уже по ней — тогда дополнение считается один раз, а не на каждый запрос.
Вывод: LPAD дополняет слева, RPAD — справа, по умолчанию пробелом. Приводите числа к тексту перед дополнением, помните про молчаливую обрезку длинных значений и используйте эти функции для генерируемых кодов и выгрузок фиксированной ширины, а не для хранения уже отформатированных строк.
Когда нужно превратить
42в000042или выровнять имена в колонке фиксированной ширины, в дело вступаютLPADиRPAD. Это функции дополнения строк: они добивают значение до заданной длины слева или справа символами-заполнителями. Разберём типичные сценарии и подводные камни.У дополнения две практические задачи. Первая — ведущие нули:
LPADпревращаетidвроде7в код000007, чтобы счета, заказы и платёжные ссылки имели одинаковую ширину и сортировались как числа. Вторая — выравнивание:RPADдобивает имена и коды стран пробелами до фиксированной длины, когда выгрузка идёт без разделителей, как в банковских и legacy-форматах. В обоих случаях исходное значение не меняется — мы лишь строим из него представление нужной ширины, поэтому форматирование держат вSELECT, view или generated column, а не в хранимой колонке.Сигнатура и базовый принцип
Обе функции принимают три аргумента: исходную строку, целевую длину и строку-заполнитель.
-- LPAD pads on the LEFT, RPAD pads on the RIGHT SELECT LPAD('42', 6, '0') AS padded_left, -- '000042' RPAD('John', 10, '.') AS padded_right; -- 'John......'Ключевые правила:
LPAD(string, length, fill)добавляетfillслева, пока строка не достигнетlength.RPAD(string, length, fill)делает то же самое, но справа.id::text.-- Cast numeric ids to text before padding SELECT LPAD(id::text, 6, '0') AS user_code FROM users ORDER BY id LIMIT 5;Zero-padding для id и номеров счетов
Самый частый случай — человекочитаемые коды с ведущими нулями. Идентификатор пользователя
7превращается вUSR-000007, номер заказа — в счёт фиксированного формата.-- Build a stable user code like USR-000007 SELECT id, 'USR-' || LPAD(id::text, 6, '0') AS user_code FROM users ORDER BY id; -- Invoice numbers with a year prefix: INV-2026-000123 SELECT o.id, 'INV-2026-' || LPAD(o.id::text, 6, '0') AS invoice_no FROM orders o WHERE o.status = 'paid' ORDER BY o.id;Почему именно
LPAD, а не хранение готовой строки? Код всегда выводится из числовогоid, остаётся отсортированным как число и не занимает лишнего места в таблице. Форматирование — забота слоя представления, а не хранения.Обрезка: когда строка длиннее цели
Главная ловушка: если исходная строка длиннее
length,LPADиRPADне расширяют результат, а обрезают его до целевой длины. Никакой ошибки при этом не возникает.-- The string is longer than the target -> it gets TRUNCATED SELECT LPAD('1234567', 6, '0') AS clipped; -- '123456', not '1234567'Это критично для счётчиков: когда id вырастет до семи цифр, поле в шесть символов молча потеряет первую цифру. Защищайтесь явной проверкой ширины:
-- GREATEST guarantees the field never silently clips SELECT LPAD(id::text, GREATEST(6, LENGTH(id::text)), '0') AS safe_code FROM users;Ловушка: заполнитель может быть длиннее одного символа, и тогда он повторяется и обрезается по символам.
LPAD('7', 5, 'ab')дастabab7— заполнитель циклически дополняется слева, а лишние символы отбрасываются.Колонки фиксированной ширины для выгрузок
Старые системы и банковские форматы часто требуют файлы с колонками фиксированной ширины, без разделителей. Здесь
RPADвыравнивает текст влево, аLPAD— числа вправо.-- Fixed-width export line: name(20) + country(2) + amount(12) SELECT RPAD(u.name, 20, ' ') || RPAD(u.country, 2, ' ') || LPAD(o.amount::text, 12, '0') AS export_line FROM users u JOIN orders o ON o.user_id = u.id ORDER BY u.id;Полезные приёмы:
RPADвлево пробелами.LPADвправо нулями.nameбудет урезано до 20 символов. Если это недопустимо, заранее проверяйтеLENGTH(name).-- Pad employee names and right-align salary for a report SELECT RPAD(name, 25, ' ') || LPAD(salary::text, 10, ' ') AS row_line FROM employees ORDER BY dept, name;Отличия MySQL и ClickHouse
Базовый синтаксис
LPAD/RPADсовпадает в PostgreSQL и MySQL, но детали расходятся:LPAD/RPADвсе три аргумента обязательны — заполнитель по умолчанию не подставляется.CAST(id AS CHAR).-- MySQL: fill argument is required, cast id explicitly SELECT LPAD(CAST(id AS CHAR), 6, '0') AS user_code FROM users;В ClickHouse функции называются
leftPadиrightPad(есть и алиасыLPAD/RPAD), но логика та же:-- ClickHouse: leftPad / rightPad SELECT leftPad(toString(id), 6, '0') AS user_code FROM users;Расхождения между движками прячутся не в обычных значениях, а в краях: что вернёт
LPADдляNULL, для уже слишком длинной строки, для пустого заполнителя''и для строки с многобайтовыми символами. По длине здесь важна разница между числом символов и числом байтов: в PostgreSQL и ClickHouseLPAD/leftPadотсчитывают символы, так что кириллица или эмодзи дают ту ширину, которую вы ожидаете, тогда как байтовый подсчёт в выгрузке фиксированной ширины ломает разметку колонок. Перед переносом между PostgreSQL, MySQL и ClickHouse прогоните маленькую таблицу сNULL, пустой строкой, значением длиннее цели и Unicode-именем — на чистых данных движки совпадают, а ровно на этих случаях расходятся.И ещё одно про производительность:
LPAD(id::text, 6, '0')вWHEREилиJOINсчитается по каждой строке и закрывает путь к обычному индексу поid. Если вы фильтруете или соединяете именно по дополненному коду, держите сравнение на стороне исходного числа (id = 42), аLPADоставляйте для вывода. Когда без поиска по готовому коду не обойтись, материализуйте его в generated column и постройте индекс уже по ней — тогда дополнение считается один раз, а не на каждый запрос.Вывод:
LPADдополняет слева,RPAD— справа, по умолчанию пробелом. Приводите числа к тексту перед дополнением, помните про молчаливую обрезку длинных значений и используйте эти функции для генерируемых кодов и выгрузок фиксированной ширины, а не для хранения уже отформатированных строк.