Тази статия в момента е на руски — английският превод е в процес на изготвяне.
Функция to_hex(int) в PostgreSQL берёт целое число и возвращает его шестнадцатеричное представление строкой в нижнем регистре, без префикса 0x. Применяют её там, где число по смыслу — это набор байтов или битов: цвет, маска прав доступа, упакованные флаги, бинарный идентификатор или хеш. В таком виде значение читается ближе к формату протокола, дампа или отладочного вывода, чем десятичное. Ниже — практические сценарии to_hex и то, как сделать обратное преобразование hex обратно в число.
Важно сразу очертить границы функции. to_hex принимает только integer или bigint и ничего больше — ни numeric, ни text, ни bytea. Для отрицательных значений она не ставит минус, а отдаёт hex дополнительного кода (two's complement), причём его ширина зависит от типа входа: 32-битного или 64-битного. Эти два свойства — допустимые типы и поведение со знаком — определяют почти все подводные камни, которые встретятся дальше, поэтому держите их в голове при каждом вызове.
Базовый to_hex
Функция принимает integer или bigint и возвращает text:
SELECT to_hex(255);
SELECT to_hex(4096);
SELECT to_hex(16777215);
Полезные детали:
- Результат всегда в нижнем регистре и без ведущих нулей.
- Префикса
0x нет — если он нужен для вывода, добавьте его сами: '0x' || to_hex(255).
- Для отрицательных чисел PostgreSQL отдаёт hex дополнительного кода (two's complement), а не минус:
to_hex(-1) даёт 'ffffffff' для 32-битного входа.
Если нужна фиксированная ширина (например, два символа на байт), дополните строку слева через lpad:
SELECT lpad(to_hex(15), 2, '0');
Цвета и битовые маски
Классический случай — собрать hex-цвет из трёх каналов RGB. Допустим, в таблице пользователей мы храним цвет аватара как целое число:
SELECT id,
'#' || lpad(to_hex(theme_color), 6, '0') AS css_color
FROM users
WHERE country = 'DE';
Так число 16711680 превращается в #ff0000 — чистый красный. lpad до шести символов гарантирует корректный CSS, даже когда старшие байты нулевые.
Второй частый сценарий — флаги прав, упакованные в одно число через побитовое ИЛИ. Hex делает такие маски читаемыми:
SELECT id,
status,
to_hex(status) AS status_hex,
(status & 4) <> 0 AS is_admin
FROM orders
WHERE (status & 4) <> 0;
Отладка значений флагов
Когда непонятно, какие биты выставлены, выводите число сразу в трёх формах — десятичной, hex и двоичной:
SELECT salary,
to_hex(salary::int) AS hex,
(salary::int)::bit(32) AS bits
FROM employees
WHERE dept = 'engineering';
Ловушка: to_hex работает только с целыми типами. Если передать numeric или text, получите ошибку — приводите вход явно: to_hex(salary::int). И помните про знак: для bigint дополнительный код шире, поэтому to_hex(-1::bigint) вернёт 'ffffffffffffffff', а не 'ffffffff'.
Обратное преобразование hex в число
Прямой функции from_hex(int) в PostgreSQL нет, но есть аккуратный трюк через тип bit: префикс x перед hex-литералом задаёт битовую строку, которую можно привести к целому.
SELECT ('x' || lpad('ff', 8, '0'))::bit(32)::int;
SELECT ('x' || lpad('1000', 8, '0'))::bit(32)::int;
lpad до восьми символов важен: bit(32) требует ровно 32 бита, то есть 8 hex-цифр. Для bigint берите bit(64) и lpad(..., 16, '0'). Полный цикл туда-обратно:
SELECT ('x' || lpad(to_hex(48879), 8, '0'))::bit(32)::int;
bytea и аналоги в MySQL
Для двоичных данных (bytea), а не одиночного числа, используйте encode и decode с форматом hex:
SELECT encode('PG'::bytea, 'hex');
SELECT decode('5047', 'hex');
encode(..., 'hex') идеален для хешей и сырых байтов; to_hex — для чисел.
В MySQL картина другая:
HEX(255) возвращает 'FF' (в верхнем регистре!) и работает и с числами, и со строками.
- Обратно —
CONV('ff', 16, 10) переводит hex в десятичное, или UNHEX('5047') для байтов.
CONV(255, 10, 16) — гибкая замена с любым основанием.
SELECT HEX(255),
CONV('ff', 16, 10),
LPAD(HEX(15), 2, '0');
ClickHouse даёт hex(255) (тоже верхний регистр) и unhex('FF') для обратного хода. При сравнении hex-строк между движками учитывайте регистр: to_hex в PostgreSQL отдаёт нижний, а HEX в MySQL и hex в ClickHouse — верхний, поэтому одинаковые по смыслу значения не совпадут при прямом сравнении. Если данные собираются из нескольких источников, приводите обе стороны к одному регистру через lower(...). Ещё одно отличие — сигнатура: PostgreSQL to_hex берёт только число, тогда как CONV в MySQL требует исходное и целевое основание, и их легко перепутать местами.
Итог по выбору функции прост: to_hex — для целых чисел, encode/decode с форматом 'hex' — для байтов и хешей, а обратное преобразование делается через тип bit в PostgreSQL и через CONV/UNHEX в MySQL. Перед тем как полагаться на hex-вывод, проверьте три вещи, на которых чаще всего ломается перенос: знаковые числа и ширину дополнительного кода (bit(32) против bit(64)), наличие или отсутствие префикса 0x и ведущих нулей, а также регистр между движками. Прогоните отдельный пример с отрицательным числом и с граничным значением вроде to_hex(2147483647), потому что именно на знаке и ширине типа to_hex ведёт себя иначе, чем кажется по to_hex(255). Если зафиксировать эти три параметра, hex-строки останутся стабильными между запросами, экспортом и сравнениями.
Функция
to_hex(int)в PostgreSQL берёт целое число и возвращает его шестнадцатеричное представление строкой в нижнем регистре, без префикса0x. Применяют её там, где число по смыслу — это набор байтов или битов: цвет, маска прав доступа, упакованные флаги, бинарный идентификатор или хеш. В таком виде значение читается ближе к формату протокола, дампа или отладочного вывода, чем десятичное. Ниже — практические сценарииto_hexи то, как сделать обратное преобразование hex обратно в число.Важно сразу очертить границы функции.
to_hexпринимает толькоintegerилиbigintи ничего больше — ниnumeric, ниtext, ниbytea. Для отрицательных значений она не ставит минус, а отдаёт hex дополнительного кода (two's complement), причём его ширина зависит от типа входа: 32-битного или 64-битного. Эти два свойства — допустимые типы и поведение со знаком — определяют почти все подводные камни, которые встретятся дальше, поэтому держите их в голове при каждом вызове.Базовый to_hex
Функция принимает
integerилиbigintи возвращаетtext:SELECT to_hex(255); -- 'ff' SELECT to_hex(4096); -- '1000' SELECT to_hex(16777215); -- 'ffffff'Полезные детали:
0xнет — если он нужен для вывода, добавьте его сами:'0x' || to_hex(255).to_hex(-1)даёт'ffffffff'для 32-битного входа.Если нужна фиксированная ширина (например, два символа на байт), дополните строку слева через
lpad:-- Always two hex digits, e.g. for a single byte channel SELECT lpad(to_hex(15), 2, '0'); -- '0f'Цвета и битовые маски
Классический случай — собрать hex-цвет из трёх каналов RGB. Допустим, в таблице пользователей мы храним цвет аватара как целое число:
SELECT id, '#' || lpad(to_hex(theme_color), 6, '0') AS css_color FROM users WHERE country = 'DE';Так число
16711680превращается в#ff0000— чистый красный.lpadдо шести символов гарантирует корректный CSS, даже когда старшие байты нулевые.Второй частый сценарий — флаги прав, упакованные в одно число через побитовое ИЛИ. Hex делает такие маски читаемыми:
-- 1=read, 2=write, 4=admin packed into orders.status as a bitmask SELECT id, status, to_hex(status) AS status_hex, (status & 4) <> 0 AS is_admin FROM orders WHERE (status & 4) <> 0;Отладка значений флагов
Когда непонятно, какие биты выставлены, выводите число сразу в трёх формах — десятичной, hex и двоичной:
SELECT salary, to_hex(salary::int) AS hex, (salary::int)::bit(32) AS bits FROM employees WHERE dept = 'engineering';Ловушка:
to_hexработает только с целыми типами. Если передатьnumericилиtext, получите ошибку — приводите вход явно:to_hex(salary::int). И помните про знак: дляbigintдополнительный код шире, поэтомуto_hex(-1::bigint)вернёт'ffffffffffffffff', а не'ffffffff'.Обратное преобразование hex в число
Прямой функции
from_hex(int)в PostgreSQL нет, но есть аккуратный трюк через типbit: префиксxперед hex-литералом задаёт битовую строку, которую можно привести к целому.-- Parse hex text back into an integer SELECT ('x' || lpad('ff', 8, '0'))::bit(32)::int; -- 255 SELECT ('x' || lpad('1000', 8, '0'))::bit(32)::int; -- 4096lpadдо восьми символов важен:bit(32)требует ровно 32 бита, то есть 8 hex-цифр. Дляbigintберитеbit(64)иlpad(..., 16, '0'). Полный цикл туда-обратно:-- Round trip: int -> hex -> int SELECT ('x' || lpad(to_hex(48879), 8, '0'))::bit(32)::int; -- 48879bytea и аналоги в MySQL
Для двоичных данных (
bytea), а не одиночного числа, используйтеencodeиdecodeс форматомhex:-- Bytes to hex text and back SELECT encode('PG'::bytea, 'hex'); -- '5047' SELECT decode('5047', 'hex'); -- bytea \x5047encode(..., 'hex')идеален для хешей и сырых байтов;to_hex— для чисел.В MySQL картина другая:
HEX(255)возвращает'FF'(в верхнем регистре!) и работает и с числами, и со строками.CONV('ff', 16, 10)переводит hex в десятичное, илиUNHEX('5047')для байтов.CONV(255, 10, 16)— гибкая замена с любым основанием.-- MySQL flavor SELECT HEX(255), -- 'FF' CONV('ff', 16, 10),-- '255' LPAD(HEX(15), 2, '0'); -- '0F'ClickHouse даёт
hex(255)(тоже верхний регистр) иunhex('FF')для обратного хода. При сравнении hex-строк между движками учитывайте регистр:to_hexв PostgreSQL отдаёт нижний, аHEXв MySQL иhexв ClickHouse — верхний, поэтому одинаковые по смыслу значения не совпадут при прямом сравнении. Если данные собираются из нескольких источников, приводите обе стороны к одному регистру черезlower(...). Ещё одно отличие — сигнатура: PostgreSQLto_hexберёт только число, тогда какCONVв MySQL требует исходное и целевое основание, и их легко перепутать местами.Итог по выбору функции прост:
to_hex— для целых чисел,encode/decodeс форматом'hex'— для байтов и хешей, а обратное преобразование делается через типbitв PostgreSQL и черезCONV/UNHEXв MySQL. Перед тем как полагаться на hex-вывод, проверьте три вещи, на которых чаще всего ломается перенос: знаковые числа и ширину дополнительного кода (bit(32)противbit(64)), наличие или отсутствие префикса0xи ведущих нулей, а также регистр между движками. Прогоните отдельный пример с отрицательным числом и с граничным значением вродеto_hex(2147483647), потому что именно на знаке и ширине типаto_hexведёт себя иначе, чем кажется поto_hex(255). Если зафиксировать эти три параметра, hex-строки останутся стабильными между запросами, экспортом и сравнениями.