PostgreSQL ctid与Oracle rowid的区别

来源:这里教程网 时间:2026-03-03 17:47:22 作者:

Oracle:搞过Oracle的一定对rowid比较熟悉啦,由下面基本含义组成

SELECT ROWID,
       DBMS_ROWID.ROWID_OBJECT(ROWID) AS OBJECT,
       DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) AS FILENUM,
       DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) AS BLOCK,
       DBMS_ROWID.ROWID_ROW_NUMBER(ROWID) AS ROWN
  FROM emp;
  ROWID                  OBJECT    FILENUM      BLOCK       ROWN
------------------ ---------- ---------- ---------- ----------
AAAUIRAABAAAg8pAAA      82449          1     134953          0
AAAUIRAABAAAg8pAAB      82449          1     134953          1
AAAUIRAABAAAg8pAAC      82449          1     134953          2
AAAUIRAABAAAg8pAAD      82449          1     134953          3
AAAUIRAABAAAg8pAAE      82449          1     134953          4
AAAUIRAABAAAg8pAAF      82449          1     134953          5
AAAUIRAABAAAg8pAAG      82449          1     134953          6
AAAUIRAABAAAg8pAAH      82449          1     134953          7
AAAUIRAABAAAg8pAAI      82449          1     134953          8
AAAUIRAABAAAg8pAAJ      82449          1     134953          9
AAAUIRAABAAAg8pAAK      82449          1     134953         10
AAAUIRAABAAAg8pAAL      82449          1     134953         11
AAAUIRAABAAAg8pAAM      82449          1     134953         12
AAAUIRAABAAAg8pAAN      82449          1     134953         13
SQL>select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where OBJECT_NAME='EMP';
OBJECT_NAME           OBJECT_ID DATA_OBJECT_ID
-------------------- ---------- --------------
EMP                       82449          82449
SQL>alter table emp move; 
SQL>select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID from dba_objects where OBJECT_NAME='EMP';
OBJECT_NAME           OBJECT_ID DATA_OBJECT_ID
-------------------- ---------- --------------
EMP                       82449          84675  
ROWID                  OBJECT    FILENUM      BLOCK       ROWN
------------------ ---------- ---------- ---------- ----------
AAAUrDAABAAAhSpAAA      84675          1     136361          0
AAAUrDAABAAAhSpAAB      84675          1     136361          1
AAAUrDAABAAAhSpAAC      84675          1     136361          2
AAAUrDAABAAAhSpAAD      84675          1     136361          3
AAAUrDAABAAAhSpAAE      84675          1     136361          4
AAAUrDAABAAAhSpAAF      84675          1     136361          5
AAAUrDAABAAAhSpAAG      84675          1     136361          6
AAAUrDAABAAAhSpAAH      84675          1     136361          7
AAAUrDAABAAAhSpAAI      84675          1     136361          8
AAAUrDAABAAAhSpAAJ      84675          1     136361          9
AAAUrDAABAAAhSpAAK      84675          1     136361         10
AAAUrDAABAAAhSpAAL      84675          1     136361         11
AAAUrDAABAAAhSpAAM      84675          1     136361         12
AAAUrDAABAAAhSpAAN      84675          1     136361         13
14 rows selected.

可以看到data object number和块号变了,如果move到另外的tablespace那么 file number也会变 PostgreSQL:

lightdb@postgres=# select relname,oid,relfilenode from pg_class where relname = 'emp';
 relname |  oid  | relfilenode 
---------+-------+-------------
 emp     | 31396 |       31396
(1 row)
lightdb@postgres=# truncate table emp;
TRUNCATE TABLE
lightdb@postgres=# select relname,oid,relfilenode from pg_class where relname = 'emp';
 relname |  oid  | relfilenode 
---------+-------+-------------
 emp     | 31396 |       31407
(1 row)

可以看到PostgreSQL的行为和Oracle很类似呀,oid对应Oracle的object_id,relfilenode对应Oracle的data_object_id,前者是逻辑对象,后者是实体对象 那么看下PostgreSQL的ctidvacuum full之后,数据行在块内的物理位置就会移动,即ctid会发生变化,所以ctid不能作为长期的行标识符,应该使用主键来标识一个逻辑行。ctid由两个数字组成,第一个数字表示物理块号,第二个数字表示在物理块中的行号,所以说 PostgreSQL的ctid是表级别唯一的行,而Oracle中是整个实例中唯一。

lightdb@postgres=# select ctid,empno from emp;
  ctid  | empno 
--------+-------
 (0,1)  |  7369
 (0,2)  |  7499
 (0,3)  |  7521
 (0,4)  |  7566

Oracle可以用rowid 删除表中重复的数据,那么PostgreSQL同样也是可以的

lightdb@postgres=# select * from t_test;
 id 
----
  1
  2
  2
  3
(4 rows)
delete from t_test where ctid in (
select ctid from
(SELECT ctid, id, count(*) OVER (PARTITION BY id ORDER BY ctid) as cnt
                            FROM t_test
                           ) where cnt > 1) returning *;

相关推荐