web-dev-qa-db-ja.com

ctidをページ番号と行番号に分解するにはどうすればよいですか?

テーブルの各行には、行の物理的な場所を表すctidタイプの システム列tidがあります。

_create table t(id serial);
insert into t default values;
insert into t default values;
_
_select ctid
     , id
from t;
_
 ctid | id 
:- -:
(0,1)| 1 
(0,2)| 2 

dbfiddle ---(ここ

最も適切なタイプのctidからページ番号のみを取得する最良の方法は何ですか(例integerbigintまたはnumeric(1000,0))?

私が考えることができる唯一の方法 は非常に醜いです。

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

Yourfiddlewith my solution。

@bmaはすでにコメントで同様のことを示唆しています。がここにあります ...

タイプの根拠

ctidのタイプはtid(タプル識別子)で、CコードではItemPointerと呼ばれます。 ドキュメントごと:

これは、システム列ctidのデータ型です。タプルIDはペアです(ブロック番号ブロック内のタプルインデックス )テーブル内の行の物理的な場所を識別します。

大胆な強調鉱山。 および:

ItemPointer、別名CTID

標準インストールでは、ブロックは8 KBです。 最大テーブルサイズは32 TB 論理的には、ブロック番号はに対応する必要があるということになります最大値(@Danielのコメントに従って計算を修正):

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

これは、符号なしintegerに収まります。さらなる調査の結果、ソースコードに が見つかりました それ...

ブロックには順番に番号が付けられます0〜0xFFFFFFFE

大胆な強調鉱山。これにより、最初の計算が確認されます。

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

Postgresは符号付き整数を使用するため、1ビット短いです。まだピン留めできませんでしたが、テキスト表現が符号付き整数に対応するようにシフトされているかどうかはわかりません。誰かがこれを解決できるまで、私はbigintにフォールバックします。これはどの場合でも機能します。

キャスト

Postgres 9.3のtidタイプには キャストが登録されていません があります:

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

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

まだtextにキャストできます。 Postgresのすべてのテキスト表現

もう1つの重要な例外は、「自動I/O変換キャスト」、つまりデータ型自体のI/O関数を使用してテキストまたは他の文字列型との間で変換を行うものであり、pg_castでは明示的に表されません。

テキスト表現は、2つのfloat8番号で構成されるポイントの表現と一致し、キャストはロスレスです。

インデックス0のポイントの最初の数にアクセスできます。bigintにキャストします。ボイラ。

パフォーマンス

私は、あなたのオリジナルを含め、頭に浮かんだいくつかの代替式について、3万行(ベスト5)のテーブルで簡単なテストを実行しました。

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の代わりにbigintを使用します。ほとんどの場合、テストの目的には関係ありません。 bigintは繰り返しませんでした。
t_tidへのキャストは、@ Jakeがコメントしたように、ユーザー定義の複合型に基づいています。
要点:キャストは文字列操作よりも高速になる傾向があります。正規表現は高価です。上記のソリューションが最短で最速です。

21