一张表的三种身份证:金仓数据库 OID vs ROWID vs 自增主键选型指南

写在前面

你有没有想过,数据库里成千上万行数据,它是怎么做到"精准定位"某一行的?

答案就藏在行标识符里。金仓数据库(KingbaseES,下文简称 KES)在这方面有两套方案------一套是 OID ,跟了数据库很多年的"老将";另一套是 ROWID,KES 自己搞出来的"独门武器"。两套东西名字不一样,用法不一样,脾气也不太一样。这篇文章就带你把它们掰开揉碎看清楚。

读完之后,你会明白:

  • OID 和 ROWID 到底有什么区别,什么时候该用哪个
  • ROWID 背后的编码长什么样,为什么它能做到物理存储
  • 三种行标识方案(OID / ROWID / 自增主键)怎么选
  • 怎么用 OID 串联系统目录,做高效的元数据查询
  • GUC 参数怎么配才不会踩坑

文章目录

    • 写在前面
    • [一、先说 OID------数据库的"身份证号"](#一、先说 OID——数据库的"身份证号")
      • [OID 是个什么东西](#OID 是个什么东西)
      • [系统表里的 OID:全局一把尺](#系统表里的 OID:全局一把尺)
      • [普通表里的 OID:各管各的](#普通表里的 OID:各管各的)
      • [regclass:OID 的"翻译官"](#regclass:OID 的"翻译官")
      • [OID 的几个"坑"](#OID 的几个"坑")
    • [二、再说 ROWID------KES 自己的"独门方案"](#二、再说 ROWID——KES 自己的"独门方案")
      • [ROWID 跟 OID 不一样在哪](#ROWID 跟 OID 不一样在哪)
      • [怎么把 ROWID 用起来](#怎么把 ROWID 用起来)
      • [ROWID 和 OID 的"互斥"问题](#ROWID 和 OID 的"互斥"问题)
    • [三、ROWID 的内部结构------拆开看看](#三、ROWID 的内部结构——拆开看看)
      • [23 个字符里藏着什么](#23 个字符里藏着什么)
      • [实际存储:看起来 23 字符,存起来没那么胖](#实际存储:看起来 23 字符,存起来没那么胖)
      • [ROWID 能做比较吗?能](#ROWID 能做比较吗?能)
      • [COPY 导入导出也没问题](#COPY 导入导出也没问题)
      • [ROWID 使用速查表](#ROWID 使用速查表)
    • 四、三套方案摆在一起------OID、ROWID、自增主键怎么选
    • [五、用 OID 串联系统目录------元数据查询实战](#五、用 OID 串联系统目录——元数据查询实战)
    • [六、GUC 参数怎么配------生产环境避坑指南](#六、GUC 参数怎么配——生产环境避坑指南)
    • 七、收个尾

一、先说 OID------数据库的"身份证号"

OID 是个什么东西

OID,全称 Object Identifier,翻译过来就是"对象标识符"。你可以把它理解成数据库内部的一张身份证------每张表、每个视图、每个索引、每个函数,在系统里都有一个 OID 和它一一对应。

它本质上是一个 4 字节的无符号整数,系统表里默认就带着它,不需要你操心。

不过有个细节要注意:你平时用 \d 看表结构的时候,OID 是不显示的。普通用户表在创建时也默认不带 OID------得你主动要,它才会给。

系统表里的 OID:全局一把尺

系统表中的 OID 是"全局通用的"。什么意思呢?同一个数据库集群里,不管你查的是 sys_typesys_proc 还是 sys_class,OID 都不会撞车------它是一个集群级的全局编号。

来看个实际例子:

sql 复制代码
-- 看看 sys_type 里有多少条记录
test=# SELECT count(*) FROM sys_type;
 count
-------
 1208
(1 row)

-- 其中超过半数的 OID 都大于 1208,说明系统预分配了大量 OID
test=# SELECT count(*) FROM sys_type WHERE oid > 1208;
 count
-------
 1120
(1 row)

-- 再看看 OID 最大值附近,还有在持续分配的
test=# SELECT oid FROM sys_type WHERE oid > 33990;
  oid
-------
 33992
 33993
 33995
(3 rows)

再做一个更有说服力的验证------创建一个自定义类型和一个自定义函数,看看它们的 OID 是不是会冲突:

sql 复制代码
-- 创建一个复合类型
cpbd_test=> CREATE TYPE aatyp IS TABLE OF int;
CREATE TYPE

cpbd_test=> SELECT oid, typname FROM sys_type WHERE typname = 'aatyp';
  oid  | typname
-------+---------
 55136 | aatyp
(1 row)

-- 再创建一个函数
cpbd_test=> CREATE OR REPLACE FUNCTION func_test05(i int)
            RETURN int AS vv int; BEGIN RETURN 1; END;
CREATE FUNCTION

cpbd_test=> SELECT oid, proname FROM sys_proc WHERE proname = 'func_test05';
  oid  |  proname
-------+------------
 55137 | func_test05
(1 row)

看到了吗?aatyp 拿到 55136,func_test05 拿到 55137,它俩压根不在同一张系统表里,但 OID 依然错开了一位,没有任何冲突。这就是"全局"的含义。

普通表里的 OID:各管各的

但到了普通表这里,画风就变了。

普通表的 OID 是局部的------每张表从 1 开始自己数,表 A 的 OID=1 和表 B 的 OID=1 完全不是一回事。

默认建表是不带 OID 的,你硬查会报错:

sql 复制代码
test=# CREATE TABLE tt5(id int);
CREATE TABLE

test=# INSERT INTO tt5 VALUES(10);
INSERT 0 1

test=# SELECT oid, id FROM tt5;
ERROR:  column "oid" does not exist
LINE 1: select oid,id from tt5;
^
HINT: Perhaps you meant to reference the column "tt5.id".

想要 OID,你得主动开口。有两种方式:

方法一:打开全局开关

sql 复制代码
test=# SET default_with_oids TO true;
SET

test=# CREATE TABLE tt6(id int);
CREATE TABLE

test=# INSERT INTO tt6 VALUES(10);
INSERT 0 1

test=# SELECT oid, id FROM tt6;
 oid | id
-----+----
   1 | 10
(1 row)

方法二:建表时直接声明

sql 复制代码
test=# SET default_with_oids TO false;
SET

test=# CREATE TABLE tt7(id int) WITH OIDS;
CREATE TABLE

test=# INSERT INTO tt7 VALUES(10);
INSERT 0 1

test=# SELECT oid, id FROM tt7;
 oid | id
-----+----
   1 | 10
(1 row)

tt6 从 1 开始,tt7 也从 1 开始,各走各的路,互不干涉。

regclass:OID 的"翻译官"

说了这么多 OID 的编号,你可能会有一个疑问:我知道一张表叫 teachers,但我怎么知道它的 OID 是多少?

这时候 regclass 类型就派上用场了。它是 OID 的"别名类型",能自动帮你把表名翻译成 OID,省得你到处查。

sql 复制代码
-- 直接把 OID 翻译成表名
test=# SELECT 16700::regclass;
 regclass
----------
 teachers
(1 row)

来看一个真实的查询场景:我想知道 teachers 表里有哪些字段。

不用 regclass,你得写子查询:

sql 复制代码
SELECT attrelid, attname, atttypid, attlen, attnum, attnotnull
  FROM sys_attribute
 WHERE attrelid = (SELECT oid FROM sys_class WHERE relname = 'teachers');

用了 regclass,一行就搞定:

sql 复制代码
SELECT attrelid, attname, atttypid, attlen, attnum, attnotnull
  FROM sys_attribute
 WHERE attrelid = 'teachers'::regclass;

输出结果一模一样:

复制代码
 attrelid |  attname    | atttypid | attlen | attnum | attnotnull
----------+-------------+----------+--------+--------+------------
    16700 | tableoid    |       26 |      4 |     -6 | t
    16700 | cmax        |       29 |      4 |     -5 | t
    16700 | xmax        |       28 |      4 |     -4 | t
    16700 | cmin        |       29 |      4 |     -3 | t
    16700 | xmin        |       28 |      4 |     -2 | t
    16700 | ctid        |       27 |      6 |     -1 | t
    16700 | teacher_id  |    17000 |     -1 |      1 | t
    16700 | teacher_name|     1043 |     -1 |      2 | t
    16700 | age         |    17000 |     -1 |      3 | f
    16700 | sal         |    17000 |     -1 |      4 | f
    16700 | gender      |     1043 |     -1 |      5 | f
    16700 | title       |     1043 |     -1 |      6 | f
    16700 | position    |     1043 |     -1 |      7 | f
    16700 | department  |     1043 |     -1 |      8 | t
(14 rows)

'teachers'::regclass 背后干的事情,其实就是帮你跑了一遍 SELECT oid FROM sys_class WHERE relname = 'teachers',只不过写起来更利索。

OID 的几个"坑"

用到 OID 的时候,有四件事要心里有数:

事项 说明
系统表全局 vs 普通表局部 系统表的 OID 集群内不会重复;普通表每张表从 1 开始,互相独立
4 字节有天花板 OID 是 4 字节无符号整数,上限大约 42.9 亿。数据量特别大的表可能会用完,一旦用完就会出现回卷重复
没有唯一性兜底 WITH OIDS 不会自动建唯一索引,不能指望它来保证行级唯一
老老实实用自增列 需要唯一行标识的场合,用 SERIALBIGSERIAL 才是正道

二、再说 ROWID------KES 自己的"独门方案"

ROWID 跟 OID 不一样在哪

如果说 OID 是数据库的"内部编号",那 ROWID 更像是每一行数据的"家庭住址"------它不仅标记了你是谁,还标记了你在哪。

ROWID 是 KES 独有的行标识机制,有三个跟 OID 不一样的关键点:

  1. 真实存储:ROWID 不是虚拟的,它物理存在于表中,以隐藏列的方式存储
  2. 自动建索引:启用 ROWID 后,KES 会自动为它创建一个 B-tree 唯一索引
  3. 支持范围查询 :因为有了索引和排序规则,ROWID 可以做 >< 这类范围过滤

简单说,OID 更像是"查户口"用的,ROWID 则是"找人"用的。

怎么把 ROWID 用起来

KES 给了三种方式,从简单到灵活都有。

第一种:打开全局参数(最省事)

default_with_rowid 设成 true,之后所有新建的表都会自动带上 ROWID:

sql 复制代码
test=# SHOW default_with_oids;
 default_with_oids
-------------------
 on
(1 row)

test=# SHOW default_with_rowid;
 default_with_rowid
--------------------
 on
(1 row)

test=# CREATE TABLE tt11(id int);
CREATE TABLE

test=# INSERT INTO tt11 VALUES(10);
INSERT 0 1

-- ROWID 列直接可用
test=# SELECT rowid, id FROM tt11;
            rowid             | id
-----------------------------+----
 AAAAAAAAAADQCAAAAAAAAAAA    | 10
(1 row)

-- 但 OID 列不存在------ROWID 的优先级比 OID 高
test=# SELECT oid, id FROM tt11;
ERROR:  column "oid" does not exist

这里有个重要细节:当 default_with_oidsdefault_with_rowid 同时为 true 时,ROWID 会胜出。最终创建的表只带 ROWID,不带 OID。

第二种:建表时直接指定(最明确)

sql 复制代码
test=# CREATE TABLE student (
         sno        int,
         name       varchar(10),
         birthday   date,
         department varchar(10),
         sex        varchar(10)
       ) WITH ROWID;
CREATE TABLE

test=# INSERT INTO student VALUES
         (1, 'li',    '2018-1-1', 'physics',  'boy'),
         (5, 'lu',    '2018-1-2', 'chinese',  'boy'),
         (3, 'wang',  '2018-1-3', 'english',  'girl'),
         (4, 'zhang', '2018-1-4', 'history',  'boy'),
         (2, 'jack',  '2018-1-5', 'history',  'boy');

test=# SELECT rowid, * FROM student;
            rowid             | sno | name  |      birthday       | department | sex
-----------------------------+-----+-------+---------------------+------------+------
 AAAAAAAAAADP5AAAAAAAAAAA    |   1 | li    | 2018-01-01 00:00:00 | physics    | boy
 AAAAAAAAAADP5AAAAAAAAAAB    |   5 | lu    | 2018-01-02 00:00:00 | chinese    | boy
 AAAAAAAAAADP5AAAAAAAAAAC    |   3 | wang  | 2018-01-03 00:00:00 | english    | girl
 AAAAAAAAAADP5AAAAAAAAAAD    |   4 | zhang | 2018-01-04 00:00:00 | history    | boy
 AAAAAAAAAADP5AAAAAAAAAAE    |   2 | jack  | 2018-01-05 00:00:00 | history    | boy
(5 rows)

\d 看看表结构,你会发现 KES 已经帮你把索引建好了:

复制代码
test=# \d student
              Table "public.student"
  Column   |         Type          | Collation | Nullable | Default
-----------+-----------------------+-----------+----------+---------
 sno       | integer               |           |          |
 name      | character varying(10) |           |          |
 birthday  | date                  |           |          |
 department| character varying(10) |           |          |
 sex       | character varying(10) |           |          |
Indexes:
    "student_rowid_key" UNIQUE CONSTRAINT, btree ("rowid")

看到了吧?student_rowid_key,B-tree 唯一索引,自动就位,不用你操心。

第三种:给已有表补上 ROWID(最灵活)

表已经建好了、数据也在了,想加 ROWID?没问题:

sql 复制代码
-- 直接用 ALTER 就行,不影响已有数据
test=# ALTER TABLE t_row SET WITH ROWID;

-- 验证一下
test=# SELECT rowid, * FROM t_row LIMIT 3;
            rowid             | id | name
-----------------------------+----+------
 AAAAAAAAAADP5AAAAAAAAAAA    |  1 | test1
 AAAAAAAAAADP5AAAAAAAAAAB    |  2 | test2
 AAAAAAAAAADP5AAAAAAAAAAC    |  3 | test3
(3 rows)

执行 ALTER TABLE ... SET WITH ROWID 时,KES 会在表中新增一个名为 rowidtype 的隐藏列,同时把现有行的 ROWID 值回填进去,并自动创建唯一索引。

ROWID 和 OID 的"互斥"问题

这两位不是"想同时用就能同时用"的。一个经典冲突场景:

sql 复制代码
-- 关掉 ROWID,打开 OID
test=# SET default_with_rowid TO false;
SET
test=# SET default_with_oids TO true;
SET

-- 然后尝试创建带 ROWID 的表------直接报错
test=# CREATE TABLE tt16(id int) WITH ROWID;
ERROR:  cannot create table with rowid
       (default_with_rowid is false, and default_with_oids is true)!

原因很简单:KES 内部做了互斥处理,一张表不能同时拥有 OID 列和 ROWID 列。这个报错信息已经说得很明白了------你想用 ROWID,但全局参数把 ROWID 关了又把 OID 开了,这就矛盾了。

三、ROWID 的内部结构------拆开看看

23 个字符里藏着什么

ROWID 在 KES 里是一种独立的数据类型(叫 rowidtype),长得像一串随机字符串,比如 AAAAAAAAADP5AAAAAAAAAAB。实际上它用 Base64 编码 (字符集 A-Za-z0-9+/),固定 23 个字符长。

这 23 个字符分三段,每一段都有明确的含义:

复制代码
 AAAAAA  AAAAAA  BAAAAAAAAAAA
 ──────  ──────  ────────────
 块号     事务ID   命令计数器
 [0~5]   [6~11]   [12~22]
  32位     32位      64位
字符位置 位数 干什么用的
存储位置(块号) 第 1~6 字符 32 位 这行数据存在哪个存储块
事务 ID(xid) 第 7~12 字符 32 位 插入这行数据的事务编号
命令计数器 第 13~23 字符 64 位 同一个事务里第几条命令

取值范围

块号 事务 ID 命令计数器
最小值 AAAAAA AAAAAA BAAAAAAAAAAA
最大值 D////// D////// P//////////

有个小细节:每次开始一个新事务(包括执行匿名块),命令计数器都会重新计算。所以如果你看到两行的前 12 个字符一样、后 11 个字符重新从 BAAAAAAAAAAA 开始,多半是同一事务里的操作。

实际存储:看起来 23 字符,存起来没那么胖

虽然 ROWID 显示出来固定 23 个字符,但 KES 内部用的是变长编码 ,实际物理存储只需要 4 到 18 个字节。对于行标识这种高频使用的信息,这个开销算是相当克制了。

ROWID 能做比较吗?能

ROWID 不只是能查,还能比较。支持的操作符有:=!=>>=<<=

比较规则也不复杂:

  • 等于 / 不等于:逐位比对全部信息
  • 大于 / 小于:先比块号(高位),块号相同再比事务 ID,再相同就比命令计数器
sql 复制代码
-- 准备测试数据
CREATE TABLE rowid_tt1(id rowid);

-- 插入几条 ROWID 值
COPY rowid_tt1 FROM '/tmp/rowid_tt1.txt';

test=# SELECT * FROM rowid_tt1;
            id
-------------------------
 AAAAAAAAAAABAAAAAAAAAAA
 AAAAAAAAAAABAAAAAAAAAAB
 AAAAAAAAAAABAAAAAAAAAAC
(3 rows)

-- 范围查询:找出大于等于第二条的记录
test=# SELECT * FROM rowid_tt1 WHERE id >= 'AAAAAAAAAAABAAAAAAAAAAB';
            id
-------------------------
 AAAAAAAAAAABAAAAAAAAAAB
 AAAAAAAAAAABAAAAAAAAAAC
(2 rows)

COPY 导入导出也没问题

ROWID 类型完全支持标准的 COPY 操作:

sql 复制代码
-- 从文件导入 ROWID 数据
COPY rowid_tt1 FROM '/tmp/rowid_tt1.txt';

-- 导出后再比对,数据完全一致
COPY rowid_tt1 TO '/tmp/rowid_tt1_to.txt';

到操作系统层面验证:

bash 复制代码
[root@localhost tmp]# cat rowid_tt1.txt
AAAAAAAAAAABAAAAAAAAAAA

[root@localhost tmp]# cat rowid_tt1_to.txt
AAAAAAAAAAABAAAAAAAAAAA

一模一样,没有任何信息丢失。

不过要注意格式校验------ROWID 必须严格是 23 个字符,多一个少一个都不行:

sql 复制代码
-- 太长了,报错
test=# INSERT INTO rowid_tt1 VALUES('AAAAAAAAAAABAAAAAAAAAAA44444444');
ERROR:  invalid input syntax for type rowid: "AAAAAAAAAAABAAAAAAAAAAA44444444"

-- 太短了,也报错
test=# INSERT INTO rowid_tt1 VALUES('AAAAAAAAAAABAAAAA');
ERROR:  invalid input syntax for type rowid: "AAAAAAAAAAABAAAAA"

ROWID 使用速查表

能不能 具体说明
SELECT 里用 ROWID 可以,直接 SELECT rowid, * FROM 表名
WHERE 里用 ROWID 过滤 可以,支持 ROWID > 常量 这种写法
ORDER BY / GROUP BY 可以,ROWID 有排序规则
存储过程里引用 可以,用数字 1, 2, 3 或字符串 '1', '2', '3' 都行
ROWID 之间做运算 不行,ROWID 不支持加减乘除
建索引 支持 B-tree 和 HASH 两种索引类型

四、三套方案摆在一起------OID、ROWID、自增主键怎么选

前面把 OID 和 ROWID 各自的用法都过了一遍,但实际干活的时候你还得做一个选择:这三种方案,到底该用哪个?

先看对比:

OID ROWID SERIAL / BIGSERIAL
是什么 系统伪列,可选开启 物理存储的隐藏列 用户自己定义的显式列
数据类型 4 字节无符号整数 Base64 字符串(rowidtype) 整数(int4 或 int8)
唯一范围 系统表全局唯一;普通表仅表内 仅表内 仅表内
物理存储 元组头部,不占用户列空间 隐藏列,变长 4~18 字节 用户列,固定 4 或 8 字节
索引 无自动索引 自动建 B-tree 唯一索引 需要手动建主键或唯一约束
上限 ~42.9 亿(有回卷风险) 编码空间远大于 OID SERIAL ~21 亿;BIGSERIAL ~9.2×10^18
适合干什么 系统目录查询 行级定位、快速查找 业务主键、外键关联
参数控制 default_with_oids default_with_rowid 不需要 GUC 参数

怎么选?一句话版

  • 查系统目录、做 DBA 运维 → OID + regclass
  • 要快速定位某一行、调试数据 → ROWID
  • 业务需要唯一标识 → SERIAL / BIGSERIAL + PRIMARY KEY

下面用一段完整的代码,把三种方案在同一张表上的表现都演示出来:

sql 复制代码
-- ========== 方案一:OID ==========
-- 开启 OID 参数后建表
SET default_with_oids TO true;

CREATE TABLE demo_oid (
    id    int,
    name  varchar(20)
);
INSERT INTO demo_oid VALUES (1, 'alice'), (2, 'bob');

-- OID 从 1 开始自增,但注意:没有唯一索引保障
test=# SELECT oid, id, name FROM demo_oid;
 oid | id | name
-----+----+-------
   1 |  1 | alice
   2 |  2 | bob
(2 rows)

-- ========== 方案二:ROWID ==========
SET default_with_rowid TO true;
SET default_with_oids TO false;

CREATE TABLE demo_rowid (
    id    int,
    name  varchar(20)
) WITH ROWID;
INSERT INTO demo_rowid VALUES (1, 'alice'), (2, 'bob');

-- ROWID 自动生成,自带唯一索引
test=# SELECT rowid, id, name FROM demo_rowid;
            rowid             | id | name
-----------------------------+----+-------
 AAAAAAAAAADP5AAAAAAAAAAA    |  1 | alice
 AAAAAAAAAADP5AAAAAAAAAAB    |  2 | bob
(2 rows)

-- ========== 方案三:自增主键(推荐业务场景) ==========
CREATE TABLE demo_serial (
    id    BIGSERIAL   PRIMARY KEY,
    name  varchar(20)
);
INSERT INTO demo_serial (name) VALUES ('alice'), ('bob');

-- id 由序列自动分配,主键约束保证唯一
test=# SELECT id, name FROM demo_serial;
 id | name
----+-------
  1 | alice
  2 | bob
(2 rows)

五、用 OID 串联系统目录------元数据查询实战

KES 的系统目录(System Catalog)是整个数据库运行的基础设施。在这些目录表中,OID 扮演着"粘合剂"的角色------不同的系统表通过 OID 互相关联,形成了完整的元数据网络。

系统表之间的关系

先看一张关系图,搞清楚核心系统表是怎么连起来的:

复制代码
                    ┌──────────────┐
                    │  sys_class   │  ← 所有"关系"(表/索引/视图)的注册表
                    │              │
                    │  oid ←───── │──→ sys_type.oid    (行类型)
                    │  reltype     │
                    │  relam  ─────│──→ sys_am.oid      (访问方法)
                    └──────┬───────┘
                           │
                    oid 被引用
                           │
                           ▼
                ┌────────────────────┐
                │  sys_attribute     │  ← 每张表的每个列都在这里有一条记录
                │                    │
                │  attrelid ──────── │──→ sys_class.oid  (这列属于哪张表)
                │  atttypid ──────── │──→ sys_type.oid   (这列是什么类型)
                └────────────────────┘

记住这条主线:sys_class (表本身)→ sys_attribute (表的列)→ sys_type(列的类型)。OID 就是把它们串起来的那根线。

实战查询:手把手查元数据

场景一:查一张表有哪些列

sql 复制代码
SELECT a.attname    AS column_name,
       t.typname    AS data_type,
       a.attlen     AS type_length,
       a.attnum     AS column_order,
       a.attnotnull AS not_null
  FROM sys_attribute a
  JOIN sys_type t ON a.atttypid = t.oid
 WHERE a.attrelid = 'teachers'::regclass    -- regclass 直接翻译表名
   AND a.attnum > 0                          -- 过滤掉系统隐藏列
 ORDER BY a.attnum;

场景二:查一张表的索引信息

sql 复制代码
SELECT c.relname  AS index_name,
       am.amname  AS index_type
  FROM sys_index i
  JOIN sys_class c  ON i.indexrelid = c.oid    -- 索引对象
  JOIN sys_am am    ON c.relam = am.oid        -- 访问方法
 WHERE i.indrelid = 'teachers'::regclass;      -- 被索引的表

场景三:查数据库里有哪些自定义类型

sql 复制代码
SELECT oid, typname, typtype
  FROM sys_type
 WHERE typname NOT LIKE 'sys_%'
 ORDER BY oid;

场景四:查某张表上绑定了哪些触发器

sql 复制代码
SELECT t.tgname     AS trigger_name,
       c.relname    AS table_name,
       p.proname    AS function_name,
       CASE t.ttype
         WHEN 'a' THEN 'AFTER'
         WHEN 'b' THEN 'BEFORE'
         ELSE 'OTHER'
       END AS trigger_timing
  FROM sys_trigger t
  JOIN sys_class c ON t.tgrelid = c.oid
  JOIN sys_proc p ON t.tgfoid = p.oid
 WHERE c.relname = 'teachers';

小贴士:::CAST 是一回事

在 KES 里做类型转换,有两种写法,效果完全一样:

sql 复制代码
-- 写法一:双冒号(简洁,KES 风格)
test=# SELECT '25'::integer, sys_typeof('25'::integer),
              '12-oct-2023'::date, sys_typeof('12-oct-2023'::date);
 int4 | sys_typeof |        date         | sys_typeof
------+------------+---------------------+------------
   25 | integer    | 2023-10-12 00:00:00 | date
(1 row)

-- 写法二:CAST(标准 SQL,跨数据库通用)
test=# SELECT CAST('25' AS integer),
              CAST('12-oct-2023' AS date);
 int4 |        date
------+---------------------
   25 | 2023-10-12 00:00:00
(1 row)

自己用、写脚本,:: 更省事;写需要跨数据库兼容的代码,用 CAST 更稳。

六、GUC 参数怎么配------生产环境避坑指南

GUC(Grand Unified Configuration)是 KES 的参数配置体系。跟 OID 和 ROWID 相关的参数就两个,但配不好就容易踩雷。

两个参数

参数 类型 默认值 作用
default_with_oids BOOL false 新建表是否默认带 OID 列
default_with_rowid BOOL false 新建表是否默认带 ROWID 列

四种组合的建表行为

default_with_oids default_with_rowid 建出来的表
false false 什么都没有------普通表
true false 带 OID,没有 ROWID
false true 带 ROWID,没有 OID
true true ROWID 赢------带 ROWID,没有 OID

唯一会报错的组合

sql 复制代码
test=# SET default_with_rowid TO false;
SET
test=# SET default_with_oids TO true;
SET
test=# CREATE TABLE bad_idea(id int) WITH ROWID;
ERROR:  cannot create table with rowid
       (default_with_rowid is false, and default_with_oids is true)!

错误信息已经很直白了:你全局关了 ROWID 又开了 OID,然后还想建带 ROWID 的表------不好意思,不行。

生产环境五条建议

1. 集群级别统一配置

不要让不同会话的参数值不一样,否则同样的建表语句跑出不同的结果,排查起来非常痛苦。建议在 kingbase.conf 中统一设置:

ini 复制代码
# kingbase.conf
default_with_oids  = false
default_with_rowid = true

2. 需要行标识?优先 ROWID

ROWID 有物理存储、有自动索引、支持范围查询------这些都是 OID 做不到的。除非你只做系统目录查询,否则 ROWID 是更好的选择。

3. DDL 里写清楚,别依赖全局参数

sql 复制代码
-- 推荐:显式声明,一眼就知道这张表带了什么
CREATE TABLE orders (
    order_id  BIGSERIAL PRIMARY KEY,
    amount    numeric(10,2),
    status    varchar(20)
) WITH ROWID;

-- 不推荐:依赖全局参数,换台机器可能行为就不一样了
CREATE TABLE orders (
    order_id  BIGSERIAL PRIMARY KEY,
    amount    numeric(10,2),
    status    varchar(20)
);

4. 不要在同一数据库里混用 OID 和 ROWID

混着用只会增加运维的心智负担,而且一旦参数配置不当就会遇到上面说的报错。选一种,全库统一。

5. 不管用 OID 还是 ROWID,业务主键都不能省

sql 复制代码
-- 正确做法:ROWID 做行定位,BIGSERIAL + PRIMARY KEY 做业务标识
CREATE TABLE products (
    product_id   BIGSERIAL    PRIMARY KEY,
    product_name varchar(100) NOT NULL,
    price        numeric(10,2)
) WITH ROWID;

OID 和 ROWID 是数据库内部的技术手段,业务层面的唯一性还是得靠主键约束来保障。

七、收个尾

到这,OID 和 ROWID 的底牌基本都亮出来了。简单回顾一下:

OID 是 KES 系统内部的"编号系统",在系统表里全局唯一,配合 regclass 做元数据查询非常顺手。但它有 4 字节的天花板,普通表里又是局部的,不适合做业务主键。

ROWID 是 KES 自己搞出来的行标识方案------物理存储、自动建唯一索引、支持范围查询和排>序分组,定位一行数据比 OID 精准得多。

SERIAL / BIGSERIAL 则是业务主键的标准选择,配合 PRIMARY KEY 约束,适合绝大多数应>用场景。

三者各管一段,搞清楚谁该在什么场合出场,你就算是把金仓数据库的行标识机制真正吃透了。

相关推荐
鸽芷咕2 小时前
从边缘到云端:2026年工业物联网时序数据库选型策略
数据库·物联网·时序数据库
雨墨✘2 小时前
CSS如何实现不同屏幕下的字体缩放_利用clamp函数动态调整
jvm·数据库·python
hef2882 小时前
Go语言如何刷LeetCode_Go语言LeetCode刷题教程【速学】
jvm·数据库·python
渡我白衣2 小时前
【MySQL基础】(4):MySQL 数据类型
数据库·人工智能·深度学习·神经网络·mysql·机器学习·自然语言处理
u0107475462 小时前
HTML5中SVG描边虚线Stroke-dasharray的配置技巧
jvm·数据库·python
Dontla2 小时前
JWT认证流程(JSON Web Token)
前端·数据库·json
Mike117.7 小时前
GBase 8a 日期边界写法和时间窗口取数偏差
数据库
SPC的存折8 小时前
1、Redis数据库基础
linux·运维·服务器·数据库·redis·缓存
MatrixOrigin12 小时前
数据库没有死,只是范式变了
数据库·oracle