SELECT (ctid::text::point)[0]::bigint AS page_number FROM t;
Biola Anda dengan solusi saya.
@ bma sudah mengisyaratkan sesuatu yang mirip dalam komentar. Ini adalah ...
Dasar pemikiran untuk tipenya
ctid
adalah tipe tid
(tuple identifier), yang disebut ItemPointer
dalam kode C. Per dokumentasi:
Ini adalah tipe data dari kolom sistem ctid
. ID tuple adalah pasangan ( nomor blok , indeks tuple dalam blok ) yang mengidentifikasi lokasi fisik baris dalam tabelnya.
Penekanan berani saya. Dan:
( ItemPointer
, juga dikenal sebagai CTID
)
Satu blok adalah 8 KB dalam instalasi standar. Ukuran Tabel Maksimal adalah 32 TB . Secara logis berikut bahwa nomor blok harus mengakomodasi setidaknya maksimum (perhitungan ditetapkan sesuai dengan komentar oleh @Daniel):
SELECT (2^45 / 2^13)::int -- = 2^32 = 4294967294
Yang cocok dengan yang tidak ditandatangani integer
. Pada penyelidikan lebih lanjut saya menemukan dalam kode sumber yang ...
blok diberi nomor secara berurutan, 0 hingga 0xFFFFFFFE .
Penekanan berani saya. Yang mengkonfirmasi perhitungan pertama:
SELECT 'xFFFFFFFE'::bit(32)::int8 -- max page number: 4294967294
Postgres menggunakan integer bertanda tangan dan karenanya sedikit pendek. Saya tidak bisa menjelaskan, apakah representasi teks digeser untuk mengakomodasi integer yang ditandatangani. Sampai seseorang dapat membereskan ini, saya akan kembali ke bigint
, yang bekerja dalam hal apa pun.
Pemeran
Tidak ada pemeran terdaftar untuk tid
jenis di Postgres 9.3:
SELECT *
FROM pg_cast
WHERE castsource = 'tid'::regtype
OR casttarget = 'tid'::regtype;
castsource | casttarget | castfunc | castcontext | castmethod
------------+------------+----------+-------------+------------
(0 rows)
Anda masih bisa memilih text
. Ada representasi teks untuk semua yang ada di Postgres :
Pengecualian penting lainnya adalah bahwa "gips konversi I / O otomatis", yang dilakukan dengan menggunakan fungsi I / O tipe data untuk mengonversi ke atau dari teks atau tipe string lainnya, tidak diwakili secara eksplisit dalam
pg_cast
.
Representasi teks cocok dengan titik, yang terdiri dari dua float8
angka, yang dilemparkan adalah lossless.
Anda dapat mengakses angka pertama dari suatu titik dengan indeks 0. Cast to bigint
. Voila.
Performa
Saya menjalankan tes cepat di atas meja dengan baris 30rb (terbaik 5) pada beberapa ekspresi alternatif yang muncul di pikiran, termasuk yang asli:
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
alih-alih di bigint
sini, sebagian besar tidak relevan untuk tujuan tes. Saya tidak mengulanginya bigint
.
Para pemain t_tid
membangun berdasarkan tipe komposit yang ditentukan pengguna, seperti @Jake berkomentar.
Inti dari itu: Casting cenderung lebih cepat daripada manipulasi string. Ekspresi reguler mahal. Solusi di atas adalah yang terpendek dan tercepat.