SELECT (ctid::text::point)[0]::bigint AS page_number FROM t;
Seu violino com a minha solução.
A @bma já sugeriu algo semelhante em um comentário. Aqui está um ...
Justificativa para o tipo
ctid
é do tipo tid
(identificador de tupla), chamado ItemPointer
no código C. Por documentação:
Este é o tipo de dados da coluna do sistema ctid
. Um ID de tupla é um par ( número de bloco , índice de tupla dentro de bloco ) que identifica a localização física da linha em sua tabela.
Negrito ênfase minha. E:
( ItemPointer
, também conhecido como CTID
)
Um bloco tem 8 KB em instalações padrão. O tamanho máximo da tabela é 32 TB . Segue-se logicamente que os números de bloco devem acomodar pelo menos um máximo de (cálculo corrigido de acordo com o comentário de @Daniel):
SELECT (2^45 / 2^13)::int -- = 2^32 = 4294967294
O que caberia em um não assinado integer
. Em uma investigação mais aprofundada, encontrei no código fonte que ...
blocos são numerados seqüencialmente, 0 a 0xFFFFFFFE .
Negrito ênfase minha. O que confirma o primeiro cálculo:
SELECT 'xFFFFFFFE'::bit(32)::int8 -- max page number: 4294967294
O Postgres usa inteiro assinado e, portanto, é um pouco curto. Ainda não pude determinar se a representação do texto foi alterada para acomodar um número inteiro assinado. Até que alguém possa esclarecer isso, eu voltaria a bigint
, que funciona em qualquer caso.
Fundida
Não há elenco registrado para o tid
tipo no Postgres 9.3:
SELECT *
FROM pg_cast
WHERE castsource = 'tid'::regtype
OR casttarget = 'tid'::regtype;
castsource | casttarget | castfunc | castcontext | castmethod
------------+------------+----------+-------------+------------
(0 rows)
Você ainda pode transmitir para text
. Existe uma representação de texto para tudo no Postgres :
Outra exceção importante é que "conversões de conversão de E / S automática", aquelas executadas usando as próprias funções de E / S de um tipo de dados para converter para ou de texto ou outros tipos de sequência, não são explicitamente representadas
pg_cast
.
A representação de texto corresponde à de um ponto, que consiste em dois float8
números, que é convertido sem perdas.
Você pode acessar o primeiro número de um ponto com o índice 0. Transmitir para bigint
. Voilá.
atuação
Fiz um teste rápido em uma tabela com 30k linhas (melhor de 5) em algumas expressões alternativas que vieram à mente, incluindo o original:
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
em vez de bigint
aqui, principalmente irrelevantes para a finalidade do teste. Eu não repeti para bigint
.
A t_tid
conversão a partir de um tipo composto definido pelo usuário, como o @Jake comentou.
A essência disso: o elenco tende a ser mais rápido que a manipulação de cordas. Expressões regulares são caras. A solução acima é mais curta e mais rápida.