SELECT (ctid::text::point)[0]::bigint AS page_number FROM t;
Tu violín con mi solución.
@bma ya insinuó algo similar en un comentario. Aquí hay un ...
Justificación del tipo
ctid
es de tipo tid
(identificador de tupla), llamado ItemPointer
en el código C. Por documentación:
Este es el tipo de datos de la columna del sistema ctid
. Una ID de tupla es un par ( número de bloque , índice de tupla dentro del bloque ) que identifica la ubicación física de la fila dentro de su tabla.
El énfasis en negrita es mío. Y:
( ItemPointer
, también conocido como CTID
)
Un bloque es de 8 KB en instalaciones estándar. El tamaño máximo de la mesa es de 32 TB . Se deduce lógicamente que los números de bloque deben acomodar al menos un máximo de (cálculo fijado según el comentario de @Daniel):
SELECT (2^45 / 2^13)::int -- = 2^32 = 4294967294
Lo que encajaría en un sin firmar integer
. En una investigación adicional encontré en el código fuente que ...
Los bloques están numerados secuencialmente, de 0 a 0xFFFFFFFE .
El énfasis en negrita es mío. Lo que confirma el primer cálculo:
SELECT 'xFFFFFFFE'::bit(32)::int8 -- max page number: 4294967294
Postgres usa un entero con signo y, por lo tanto, es un poco corto. No pude precisar, aún, si la representación de texto se desplaza para acomodar un entero con signo. Hasta que alguien pueda aclarar esto, recurriría a bigint
, lo que funciona en cualquier caso.
Emitir
No hay elenco registrado para el tid
tipo en Postgres 9.3:
SELECT *
FROM pg_cast
WHERE castsource = 'tid'::regtype
OR casttarget = 'tid'::regtype;
castsource | casttarget | castfunc | castcontext | castmethod
------------+------------+----------+-------------+------------
(0 rows)
Todavía puedes lanzar a text
. Hay una representación de texto para todo en Postgres :
Otra excepción importante es que las "conversiones de conversión de E / S automáticas", aquellas realizadas utilizando las propias funciones de E / S de un tipo de datos para convertir hacia o desde texto u otros tipos de cadenas, no están explícitamente representadas en
pg_cast
.
La representación de texto coincide con la de un punto, que consta de dos float8
números, ese reparto no tiene pérdidas.
Puede acceder al primer número de un punto con índice 0. Transmitir a bigint
. Voilá
Actuación
Realicé una prueba rápida en una tabla con 30k filas (la mejor de 5) en un par de expresiones alternativas que me vinieron a la mente, incluido el 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
en lugar de bigint
aquí, mayormente irrelevante para el propósito de la prueba. No repetí para bigint
.
El elenco para t_tid
construir sobre un tipo compuesto definido por el usuario, como @Jake comentó.
La esencia de esto: el casting tiende a ser más rápido que la manipulación de cuerdas. Las expresiones regulares son caras. La solución anterior es más corta y más rápida.