SELECT (ctid::text::point)[0]::bigint AS page_number FROM t;
Твоя скрипка с моим решением.
@bma уже намекнул нечто подобное в комментарии. Вот ...
Обоснование типа
ctid
имеет тип tid
(идентификатор кортежа), вызываемый ItemPointer
в коде C. По документации:
Это тип данных системного столбца ctid
. Идентификатор кортежа - это пара ( номер блока , индекс кортежа в блоке ), который идентифицирует физическое местоположение строки в своей таблице.
Жирный акцент мой. И:
( ItemPointer
также известный как CTID
)
Блок составляет 8 КБ в стандартных установках. Максимальный размер таблицы составляет 32 ТБ . Логически следует, что номера блоков должны соответствовать как минимум максимуму (расчет фиксируется согласно комментарию @Daniel):
SELECT (2^45 / 2^13)::int -- = 2^32 = 4294967294
Который вписался бы в неподписанного integer
. При дальнейшем расследовании я обнаружил в исходном коде, что ...
блоки нумеруются последовательно, от 0 до 0xFFFFFFFE .
Жирный акцент мой. Что подтверждает первый расчет:
SELECT 'xFFFFFFFE'::bit(32)::int8 -- max page number: 4294967294
Postgres использует целое число со знаком и, следовательно, на один бит меньше. Я пока не могу определить, смещено ли текстовое представление для размещения целого числа со знаком. Пока кто-нибудь не сможет это прояснить, я бы отступилbigint
, что работает в любом случае.
В ролях
В Postgres 9.3 не зарегистрировано приведение tid
типа:
SELECT *
FROM pg_cast
WHERE castsource = 'tid'::regtype
OR casttarget = 'tid'::regtype;
castsource | casttarget | castfunc | castcontext | castmethod
------------+------------+----------+-------------+------------
(0 rows)
Вы все еще можете бросить на text
. В Postgres есть текстовое представление для всего :
Другое важное исключение заключается в том, что «автоматическое преобразование ввода-вывода», те, которые выполняются с использованием собственных функций ввода-вывода для преобразования данных в текстовые или другие строковые типы или из них, явно не представлены в
pg_cast
.
Текстовое представление соответствует представлению точки, состоящей из двух float8
чисел, которое приведено без потерь.
Вы можете получить доступ к первому номеру точки с индексом 0. Приведение к bigint
. Вуаля.
Производительность
Я провел быстрый тест для таблицы с 30 тыс. Строк (лучше всего из 5) на нескольких альтернативных выражениях, которые пришли в голову, включая ваш исходный:
SELECT (ctid::text::point)[0]::int -- 25 ms
,right(split_part(ctid::text, ',', 1), -1)::int -- 28 ms
,ltrim(split_part(ctid::text, ',', 1), '(')::int -- 29 ms
,(ctid::text::t_tid).page_number -- 31 ms
,(translate(ctid::text,'()', '{}')::int[])[1] -- 45 ms
,(replace(replace(ctid::text,'(','{'),')','}')::int[])[1] -- 51 ms
,substring(right(ctid::text, -1), '^\d+')::int -- 52 ms
,substring(ctid::text, '^\((\d+),')::int -- 143 ms
FROM tbl;
int
вместо этого bigint
, в основном не имеет значения для целей теста. Я не повторил для bigint
.
Приведение к t_tid
основывается на пользовательском составном типе, как, например, прокомментировал @Jake.
Суть этого: кастинг, как правило, быстрее, чем манипуляции со строками. Регулярные выражения стоят дорого. Вышеуказанное решение является самым коротким и быстрым.