SELECT (ctid::text::point)[0]::bigint AS page_number FROM t;
Il tuo violino con la mia soluzione.
@bma ha già suggerito qualcosa di simile in un commento. Ecco un ...
Razionale per il tipo
ctid
è di tipo tid
(identificatore tupla), chiamato ItemPointer
nel codice C. Per documentazione:
Questo è il tipo di dati della colonna di sistema ctid
. Un ID tupla è una coppia ( numero di blocco , indice di tupla all'interno del blocco ) che identifica la posizione fisica della riga all'interno della sua tabella.
Enorme enfasi sulla mia. E:
( ItemPointer
, noto anche come CTID
)
Un blocco è 8 KB in installazioni standard. La dimensione massima della tabella è di 32 TB . Ne consegue logicamente che i numeri di blocco devono contenere almeno un massimo di (calcolo fissato in base al commento di @Daniel):
SELECT (2^45 / 2^13)::int -- = 2^32 = 4294967294
Che si adatterebbe in un unsigned integer
. Su ulteriori indagini ho trovato nel codice sorgente che ...
i blocchi sono numerati in sequenza, da 0 a 0xFFFFFFFE .
Enorme enfasi sulla mia. Il che conferma il primo calcolo:
SELECT 'xFFFFFFFE'::bit(32)::int8 -- max page number: 4294967294
Postgres utilizza numeri interi con segno ed è quindi un po 'corto. Non potrei ancora stabilire se la rappresentazione del testo viene spostata per adattarsi all'intero con segno. Fino a quando qualcuno non riuscirà a chiarire questo, vorrei tornare indietrobigint
, il che funziona in ogni caso.
Fusioni
Non esiste alcun cast registrato per il tid
tipo in Postgres 9.3:
SELECT *
FROM pg_cast
WHERE castsource = 'tid'::regtype
OR casttarget = 'tid'::regtype;
castsource | casttarget | castfunc | castcontext | castmethod
------------+------------+----------+-------------+------------
(0 rows)
Puoi ancora lanciare text
. In Postgres esiste una rappresentazione testuale per tutto :
Un'altra importante eccezione è che i "cast di conversione I / O automatici", quelli eseguiti utilizzando le funzioni I / O proprie di un tipo di dati per convertire da o verso testo o altri tipi di stringa, non sono rappresentati esplicitamente
pg_cast
.
La rappresentazione del testo corrisponde a quella di un punto, che consiste di due float8
numeri, che il cast è senza perdita.
È possibile accedere al primo numero di un punto con indice 0. Trasmetti a bigint
. Ecco.
Prestazione
Ho eseguito un rapido test su un tavolo con 30k righe (la migliore delle 5) su un paio di espressioni alternative che mi sono venute in mente, incluso il tuo originale:
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
anziché bigint
qui, per lo più irrilevante ai fini del test. Non ho ripetuto per bigint
.
Il cast si t_tid
basa su un tipo composito definito dall'utente, come ha commentato @Jake.
L'essenza: il casting tende ad essere più veloce della manipolazione delle stringhe. Le espressioni regolari sono costose. La soluzione sopra è la più breve e veloce.