Ctid'i sayfa ve satır numaralarına nasıl ayrıştırabilirim?


16

Bir tablodaki her satır, bir sahiptir sistem sütununu ctid Çeşidi tidsatır fiziksel konumunu temsil eder:

create table t(id serial);
insert into t default values;
insert into t default values;
select ctid
     , id
from t;
ctid | İD
: ---- | -:
(0,1) | 1
(0,2) | 2

dbfiddle burada

ctidEn uygun türdeki (ör integer. bigintVeya numeric(1000,0)) yalnızca sayfa numarasını almanın en iyi yolu nedir ?

Aklıma gelen tek yol çok çirkin.


1
IIRC bir vektör türüdür ve bunlarda erişimci yöntemlerimiz yoktur. Bunu bir C fonksiyonundan yapıp yapamayacağınızdan emin değilim. Craig kesin olarak söyleyecek :)
dezso

2
POINT olarak yayınlayabilir misiniz? Örneğin. select ct[0], ct[1] from (select ctid::text::point as ct from pg_class where ...) y;
bma

1
Başlık, sayfa numarasının ve grup dizininin peşinde olduğunuzu, daha sonra sayfa numarasına daldığınızı gösterir. Vücuttaki versiyonla gittim, tuple indeksi önemsiz bir uzantı.
Erwin Brandstetter

Yanıtlar:


21
SELECT (ctid::text::point)[0]::bigint AS page_number FROM t;

Sizin keman benim çözeltisi ile.

@bma zaten bir yorumda benzer bir şey ima etti. Burada bir ...

Tür için gerekçe

ctidC kodunda tidçağrılan tiptir (grup tanımlayıcısı) ItemPointer. Belgelere göre:

Bu, sistem sütununun veri türüdür ctid. Bir grup kimliği, satırın tablosundaki fiziksel konumunu tanımlayan bir çifttir ( blok numarası , blok içindeki grup dizini ).

Cesur vurgu benim. Ve:

( ItemPointer, olarak da bilinir CTID)

Standart kurulumlarda bir blok 8 KB'dir . Maksimum Tablo Boyutu 32 TB'dir . Mantıksal olarak, blok numaralarının en az bir sayıda (@Daniel tarafından yapılan yoruma göre sabitlenmiş hesaplama) barındırması gerektiği izlenir:

SELECT (2^45 / 2^13)::int      -- = 2^32 = 4294967294

Hangi imzasız bir sığacak integer. Daha fazla araştırma üzerine kaynak kodunda buldum ...

bloklar sırayla 0 ile 0xFFFFFFFE arasında numaralandırılır .

Cesur vurgu benim. İlk hesaplamayı doğrulayan:

SELECT 'xFFFFFFFE'::bit(32)::int8 -- max page number: 4294967294

Postgres imzalı tamsayı kullanır ve bu nedenle bir bit kısadır. Metin temsilinin işaretli tamsayıyı alacak şekilde kaydırılıp kaydırılmadığını henüz anlayamadım. Birisi bunu temizleyene kadar, biginther durumda işe yarayacaktım .

Oyuncular

Orada kayıtlı hiçbir döküm için tidPostgres 9.3 tip:

SELECT *
FROM   pg_cast
WHERE  castsource = 'tid'::regtype
OR     casttarget = 'tid'::regtype;

 castsource | casttarget | castfunc | castcontext | castmethod
------------+------------+----------+-------------+------------
(0 rows)

Hala yayınlayabilirsiniz text. Postgres'teki her şey için bir metin temsili vardır :

Bir başka önemli istisna, metin veya diğer dize türlerine dönüştürmek veya veri türünden dönüştürmek için bir veri türünün kendi G / Ç işlevleri kullanılarak gerçekleştirilen "otomatik G / Ç dönüştürme dökümleri" nin açıkça temsil edilmemesidir pg_cast.

Metin gösterimi, iki float8sayıdan oluşan bir noktanın dökümüyle eşleşmez;

0 noktasından bir noktanın ilk numarasına erişebilirsiniz bigint. Voila.

Verim

Orijinaliniz de dahil olmak üzere aklınıza gelen birkaç alternatif ifadede 30k satırlı (5'in en iyisi) bir tablo üzerinde hızlı bir test yaptım:

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;

intbunun yerine bigint, çoğunlukla testin amacı ile alakasız. Ben tekrar etmedim bigint. @Jake gibi kullanıcı tanımlı bir kompozit tür üzerine inşa edilecek
döküm t_tidyorumladı.
Bunun özü: Döküm, dizi manipülasyonundan daha hızlı olma eğilimindedir. Düzenli ifadeler pahalıdır. Yukarıdaki çözüm en kısa ve en hızlıdır.


1
Teşekkürler Erwin, faydalı şeyler. Gönderen burada gibi görünüyor ctidsatır için sayfanın 4 ve 2 ile 6 bayt. Oyunculuk konusunda endişeliydim floatama sanırım burada söylediklerinden birine ihtiyacım yok. Kullanıcı tanımlı bir kompozit tür kullanıldıktan sonra çok daha yavaş görünüyor point, bunu da buluyor musunuz?
Jack diyor ki topanswers.xyz

@JackDouglas: Daha fazla soruşturma üzerine geri döndüm bigint. Güncellemeyi düşünün.
Erwin Brandstetter

1
@JackDouglas: Kompozit tipte bir oyuncu seçimi fikrinizi seviyorum. Temiz ve çok iyi bir performans sergiliyor - al pointve geri atma int8hala daha hızlı olsa bile ). Önceden tanımlanmış türlere yayın her zaman biraz daha hızlı olacaktır. Karşılaştırmak için testime ekledim. (page_number bigint, row_number integer)Emin olmak için yapardım .
Erwin Brandstetter

1
2^40Sadece 1TB değil, 32TB olduğu 2^45bölü, 2^13verir 2^32sayfa numarası için gerekli olan dolayısıyla tam 32 bit.
Daniel Vérité

1
Ayrıca Not belki layık olduğunu pg_freespacemap kullandığı bigintblkno için
Jack topanswers.xyz deneyin diyor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.