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
ctidest de type tid(tuple identifier), appelé ItemPointerdans 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 bigintce qui fonctionne dans tous les cas.
Jeter
Il n'y a pas de distribution enregistrée pour le tidtype 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 float8nombres, 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;
intau lieu d’ bigintici, 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_tids'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.