ctid를 페이지 및 행 번호로 어떻게 분해합니까?


16

테이블의 각 행에는 행 의 실제 위치를 나타내는 유형 의 시스템 열 ctid 이 있습니다 tid.

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

여기 dbfiddle

로부터처럼 페이지 번호 얻을 수있는 가장 좋은 방법은 무엇입니까 ctid가장 적절한 유형 (예를 들어 integer, bigint또는 numeric(1000,0))?

내가 생각할 수 있는 유일한 방법 은 매우 추악합니다.


1
IIRC 이것은 벡터 유형이며 이에 대한 접근 자 메소드가 없습니다. C 함수에서 할 수 있는지 확실하지 않습니다. 크레이그 확실히 :)에 대해 알려드립니다
dezso

2
POINT로 캐스팅 할 수 있습니까? 예 : select ct[0], ct[1] from (select ctid::text::point as ct from pg_class where ...) y;
bma

1
제목은 페이지 번호튜플 색인 뒤에 있음을 나타내며 나중에 페이지 번호로 좁 힙니다. 나는 몸에 버전을 갔다, 튜플 인덱스는 사소한 확장입니다.
Erwin Brandstetter

답변:


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

내 솔루션과 당신의 바이올린 .

@bma는 이미 주석에서 비슷한 것을 암시했습니다. 여기에 ...

유형에 대한 이론적 근거

ctidC 코드에서 tid호출 ItemPointer되는 유형 (튜플 식별자) 입니다. 문서 당 :

이것은 시스템 열의 데이터 유형입니다 ctid. 튜플 ID는 테이블 내 행의 물리적 위치를 식별 하는 쌍 ( 블록 번호 , 블록 내 튜플 색인 )입니다.

대담한 강조 광산. 과:

( ItemPointer라고도 함 CTID)

표준 설치에서 블록은 8KB 입니다. 최대 테이블 크기는 32TB 입니다. 이 블록 번호가 수용해야하는 것이 논리적 다음 적어도 최대 (계산 @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의 모든 것에 대한 텍스트 표현 이 있습니다 .

또 다른 중요한 예외는 텍스트 또는 다른 문자열 유형으로 변환하기 위해 데이터 유형의 자체 I / O 함수를 사용하여 수행 된 "자동 I / O 변환 캐스트"가 명시 적으로 표시되지 않는 것입니다 pg_cast.

텍스트 표현은 두 개의 float8숫자 로 구성된 점의 표현과 일치 하며 캐스트는 무손실입니다.

인덱스가 0 인 포인트의 첫 번째 번호에 액세스 할 수 있습니다 bigint. 캐스트 . 보일라

공연

원본을 포함하여 몇 가지 대체 표현에 대해 30k 행 (최고 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. @Jake와 같은 사용자 정의 복합 유형에 빌드
캐스트는 t_tid주석 처리되었습니다.
요점 : 캐스팅은 문자열 조작보다 빠릅니다. 정규 표현식은 비싸다. 위의 솔루션은 가장 짧고 빠릅니다.


1
감사합니다 Erwin, 유용한 것들. 에서 여기 것 같습니다 ctid행에 대한 페이지 4 2 6 바이트입니다. 나는 캐스팅에 대해 걱정 float했지만 여기서 말한 내용이 필요하지 않은 것 같습니다. 사용자 정의 복합 유형이을 사용하는 것보다 훨씬 느리게 보입니다 point.
Jack Douglas

@JackDouglas : 추가 조사로 나는로 돌아갔다 bigint. 업데이트를 고려하십시오.
Erwin Brandstetter

1
@ JackDouglas : 복합 유형으로 캐스트하는 아이디어를 좋아합니다. 깨끗하고 잘 수행됩니다-캐스트 point및 다시 캐스트 int8가 여전히 빠르더라도). 사전 정의 된 유형으로 캐스트하는 것이 항상 약간 빠릅니다. 비교하기 위해 테스트에 추가했습니다. 나는 그것을 (page_number bigint, row_number integer)확실히 할 것입니다 .
Erwin Brandstetter

1
2^40단 1TB하지 32TB이다 2^45으로 나누어 2^13준다 2^32페이지 번호 필요한되고 따라서 전체 32 비트.
Daniel Vérité

1
또한 pg_freespacemapblkno에 사용 bigint됩니다
Jack Douglas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.