文档用途
oracle中的rowid伪列,有很重要的应用场景,但是HGDB中没有该伪列,本文档介绍HGDB如何兼容实现oracle中的rowid功能。
详细信息
oracle中的rowid
什么是rowid?
rowid是数据的详细物理地址,通过rowid,oracle可以快速的定位某行具体数据的位置。
创建测试表
create table test(id number,name varchar2(10));
插入测试数据
insert into test values(0,'orcl');
insert into test valurs(1,'orcl');
insert into test values(2,'orcl');
insert into test values(3,'orcl');
查看表内容
select rowid,id,name from test;
ROWID ID NAME
------------------ ---------- ---------------
AAAV5DAAOAAAACWAAA 0 orcl
AAAV5DAAOAAAACWAAB 1 orcl
AAAV5DAAOAAAACWAAC 3 orcl
AAAV5DAAOAAAACWAAD 2 orcl
rowid的结构
我们从rowid伪列里select出来的rowid是基于base64编码,一共有18位,分为4部分:
形如OOOOOOFFFBBBBBBRRR,其中:
- 前6位(OOOOOO)为数据对象ID,一张表、一个索引都是一个数据对象,oracle都会分配给它们一个唯一的数据对象ID。
- 紧跟的3位(FFF)为相对的文件ID,我们知道表空间是由不同的文件组成,对象存储在某个文件里,每个文件会对应一个ID号。
- 再接着的6位(BBBBBB)为块ID,文件是由块组成的,每个块也有一个唯一的ID号。
- 最后3位(RRR)为行ID,每个块可以划分为行,每个行也有一个ID号。
rowid的应用场景
- 去重
delete tt a
where a.rowid <( select max( b.rowid )
from tt b
where a.employee_id = b.employee_id
and a.first_name = b.first_name
and a.last_name = b.last_name
and a.title = b.title
and a.salary = b.salary );
rowid是行的“身份证号”,哪怕两行中的数据完全相同,其rowid值也是不一样的(正如像个同名同姓的人,身份证号码不一样)。为什么要用到MAX这个函数?这就涉及到ROWID的性质了,ROWID是有具体值的,是可以比较大小的,先找出表中重复的行的ROWID的最大值,然后<>表示只保留重复行中ROWID最大值的那一行,其余的那几行全部删除,所以这个地方也可以使用MIN,即只保留ROWID最小的那一行,即可实现去重的作用。
- 作为主键,用于查询
select * from tt where rowid = 'XXXXX';
有些应用会将rowid当作数据主键进行查询,可以作为主键的原因是rowid具有唯一性,甚至是全库唯一性,但是他跟我们自建主键的区别是,在数据修改或者导入导出等操作时,rowid可能会自动变化。所以用rowid作为主键时适合用于对其值不敏感,仅作为区分不同数据的情况下使用。也正因为这种性质接下来的用法“作为外键用于引用”也是完全错误的。另外oracle中的索引也是利用了rowid唯一性。
- 作为外键用于引用
select * from tb_a left join tb_b on tb_b.tbarowid=tb_a.rowid where xxxxx;
虽然这种用法是完全错误的,但是假如迁移过程中碰到这种情况,也只能用最笨且最有效的办法,新建字段,将rowid值更新到字段中并且修改外键映射。当表比较多时,工作量非常大。
HGDB兼容性
- ctid
ctid是HGDB中行版本在其表中的物理位置。尽管ctid可以被用来非常快速地定位行版本,但是一个行的ctid会在被更新或者被VACUUM FULL移动时改变。因此,ctid不能作为一个长期行标识符。OID或者最好一个用户定义的序列号才应该被用来标识逻辑行,它也是由系统自动生成,不过结构和rowid不同,ctid的结构是(blockid,itemid)。
select ctid,* from t2;
ctid | id | name
-------+----+--------
(0,1) | 1 | apple
(0,2) | 1 | apple
(0,3) | 1 | apple
(0,4) | 2 | orange
(0,5) | 2 | orange
(0,6) | 2 | orange
(0,7) | 3 | banana
(0,8) | 3 | banana
--利用ctid去重
delete from t2 where ctid not in (select min(ctid) from t2 group by id);
oid
testdb=> create sequence test_seq; --创建序列发生器-sequence,当然大纲中还有很多其他参数 可选。
CREATE SEQUENCE
testdb=> create table test_db(id bigint default nextval('test_seq') not null,name varchar); -- 字段设置默认值
CREATE TABLE
testdb=> create unique index test_db_idx_1 on test_db(id); --创建唯一索引以增强序列的唯一性
CREATE INDEX
testdb=> insert into test_db(name) values('Tom'),('Jack'),('Jone'); --插入数据测试
INSERT 0 3
testdb=> select * from test_db;
id | name
----+------
1 | Tom
2 | Jack
3 | Jone
(3 rows)
这样就创建好了一个自增的可以保证表唯一的主键,当然在库中的其他表也可以使用这一个序列生成器从而实现数据的全库唯一性,序列是基于bigint算法的,因此他的范围不能超过一个八字节(范围大于oid的4字节)整数的范围-9223372036854775808到9223372036854775807。
- serial
serial是一种数据类型。
testdb=> create table test_t3(id serial not null,name varchar);
CREATE TABLE
testdb=> \ds
List of relations
Schema | Name | Type | Owner
--------+----------------+----------+----------
public | test_seq | sequence | testuser
public | test_t2_id_seq | sequence | testuser
public | test_t3_id_seq | sequence | testuser
public | test_t_id_seq | sequence | testuser
(4 rows)
创建一个serial列时,会自动创建一个sequence,可见他的实现机理跟sequence一样的,当然对应sequence的int类型,serial包含serial,serial2,serial8 3种可选类型。
-
identity
创建identity自增列,类似与mysql的AUTO_INCREMENT,方法:
testdb=> create table test_t(id int generated always as identity not null,name varchar);
--always
CREATE TABLE
testdb=> create table test_t2(id int generated by default as identity not null,name varchar); --by default
CREATE TABLE
--ALWAYS,表示优先使用系统列生成的自增值。
--BY DEFAULT,表示优先使用用户输入的值。
HGDB兼容Oracle rowid实现
1412

被折叠的 条评论
为什么被折叠?



