SELECT (ctid::text::point)[0]::bigint AS page_number FROM t;
Votre violon avec ma solution.
@bma a déjà fait allusion à quelque chose de similaire dans un commentaire. Voici une ...
Justification du type
ctid
est de type tid
(tuple identifier), appelé ItemPointer
dans le code C. Par documentation:
Il s'agit du type de données de la colonne système ctid
. Un ID de tuple est une paire ( numéro de bloc , index de tuple dans le bloc ) qui identifie l'emplacement physique de la ligne dans sa table.
Accentuation sur moi. Et:
( ItemPointer
, également connu sous le nom de CTID
)
Un bloc fait 8 Ko dans les installations standard. La taille maximale de la table est de 32 To . Il s'ensuit logiquement que les numéros de bloc doivent contenir au moins un maximum de (calcul fixé selon le commentaire de @Daniel):
SELECT (2^45 / 2^13)::int -- = 2^32 = 4294967294
Ce qui rentrerait dans un non signé integer
. Après une enquête plus approfondie, j'ai trouvé dans le code source que ...
les blocs sont numérotés séquentiellement, de 0 à 0xFFFFFFFE .
Accentuation sur moi. Ce qui confirme le premier calcul:
SELECT 'xFFFFFFFE'::bit(32)::int8 -- max page number: 4294967294
Postgres utilise un entier signé et est donc un peu court. Je ne pouvais pas encore déterminer si la représentation du texte était décalée pour s'adapter à un entier signé. Jusqu'à ce que quelqu'un puisse éclaircir ce point, je reviendrais sur bigint
ce qui fonctionne dans tous les cas.
Jeter
Il n'y a pas de distribution enregistrée pour le tid
type dans Postgres 9.3:
SELECT *
FROM pg_cast
WHERE castsource = 'tid'::regtype
OR casttarget = 'tid'::regtype;
castsource | casttarget | castfunc | castcontext | castmethod
------------+------------+----------+-------------+------------
(0 rows)
Vous pouvez toujours lancer sur text
. Il y a une représentation textuelle pour tout dans Postgres :
Une autre exception importante est que les "conversions d'E / S automatiques", celles effectuées à l'aide des propres fonctions d'E / S d'un type de données pour convertir vers ou à partir de texte ou d'autres types de chaînes, ne sont pas explicitement représentées dans
pg_cast
.
La représentation textuelle correspond à celle d'un point, composé de deux float8
nombres, que la distribution est sans perte.
Vous pouvez accéder au premier numéro d'un point avec l'index 0. Cast to bigint
. Voilá.
Performance
J'ai effectué un test rapide sur une table avec 30k lignes (le meilleur de 5) sur quelques expressions alternatives qui me sont venues à l'esprit, y compris l'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
au lieu d’ bigint
ici, ce qui n’est généralement pas pertinent aux fins du test. Je n'ai pas répété pour bigint
.
Le cast à t_tid
s'appuie sur un type composite défini par l'utilisateur, comme @Jake a commenté.
L'essentiel: le casting a tendance à être plus rapide que la manipulation de cordes. Les expressions régulières coûtent cher. La solution ci-dessus est la plus courte et la plus rapide.