本文是本系列第 4 篇。上一篇已经创建了专栏业务数据库
kb_shop,本文继续在其中规划业务模式,让客户、商品、订单等对象有清晰的归属。
引言
上一篇文章已经创建了专栏业务库kb_shop,解决了业务对象应该放在哪个数据库中的问题。但一个数据库内部仍然可能包含大量表、视图、函数和辅助对象,如果全部放进默认的public模式,随着文章推进和对象增多,结构会很快变得混乱。
本文进一步讨论 KingbaseES 中的 Schema 治理。我们会把kb_shop划分为sales、inventory、ops三个模式,用它们分别承载销售、库存和运维辅助对象。这样做不仅是为了目录清晰,更是为了后续权限控制、对象检索、查询编写和备份维护更加接近真实项目。
很多数据库新手会把所有表都建在public模式下。刚开始只有一两张表时看不出问题,但业务对象一多,就容易出现命名混乱、权限边界不清、查询路径不明确等问题。
KingbaseES 支持模式,也就是 schema。模式可以理解为数据库内部的“逻辑分区”:
数据库 kb_shop ├─ sales 客户、订单、订单明细 ├─ inventory 商品、库存 ├─ ops 运维检查、脚本执行记录 └─ public 默认模式,尽量少放业务对象本文目标:
- 在
kb_shop中创建多个业务模式。 - 理解
search_path的作用。 - 学会使用完整对象名访问表。
- 建立本系列后续文章的对象命名规范。
文章目录
- 引言
- Schema 是数据库对象治理的第一层边界
- 一、连接 kb_shop
- 二、查看当前已有模式
- 三、创建业务模式
- 四、理解 search_path
- 五、完整对象名更适合文章演示
- 六、创建一个 ops 检查表
- 七、设置数据库级默认 search_path
- 八、对象命名规范
- 九、常见问题排查
- 问题 1:报错 schema does not exist
- 问题 2:查询表时报 relation does not exist
- 问题 3:search_path 设置了但没有生效
- 问题 4:不确定某张表在哪个模式
- 十、本文小结
Schema 是数据库对象治理的第一层边界
Schema 通常翻译为“模式”。在一个数据库中,模式用于组织表、视图、函数等数据库对象。它既不是独立数据库,也不是简单文件夹,而是一种数据库内部的命名空间和权限边界。
为什么不能所有对象都放进public?核心原因有三点:
| 问题 | 全放在 public 的后果 | 使用 Schema 的收益 |
|---|---|---|
| 命名冲突 | 不同业务模块容易出现同名表 | 通过sales.customer、crm.customer区分 |
| 权限粗糙 | 很难只授权某一类业务对象 | 可以按模式授予访问范围 |
| 维护困难 | 对象多后缺乏业务分层 | DBA 和开发能快速定位对象归属 |
在企业系统中,模式设计往往对应业务模块设计。销售对象、库存对象、运维对象分开管理,能够让后续权限控制、备份策略、脚本维护和问题排查更加清晰。
本文将kb_shop划分为sales、inventory、ops,本质上是在做数据库对象治理。这个设计会贯穿后续文章:客户和订单归属于sales,商品和库存归属于inventory,检查和辅助表归属于ops。
一、连接 kb_shop
打开命令提示符,进入工具目录:
cd /d D:\Tools\Kingbase\ES\Server\bin连接kb_shop:
ksql -U system -d kb_shop -h localhost -p 54321确认当前数据库:
SELECTcurrent_database()AScurrent_db,current_userASlogin_user;预期结果:
二、查看当前已有模式
执行:
\dn是ksql中查看模式列表的快捷命令。相比直接查询系统视图,它更适合日常交互式检查;而 SQL 查询更适合写进脚本或报表。两种方式并不冲突,前者便捷,后者可编程。
\dn通常会看到public等模式。
也可以使用 SQL 查询:
SELECTschema_nameFROMinformation_schema.schemataORDERBYschema_name;这两种方式都可以。\dn适合日常命令行查看,SQL 查询适合写脚本和报表。
三、创建业务模式
为了后续文章形成清晰结构,我们创建 3 个模式:
这一步不是为了“多分几个目录”,而是提前建立业务边界。sales负责销售域对象,inventory负责库存域对象,ops负责运维辅助对象。后续讲权限时,就可以做到只给某个用户访问某个模式,而不是把整个数据库都开放出去。
| 模式名 | 用途 |
|---|---|
sales | 客户、订单、订单明细等销售业务对象 |
inventory | 商品、库存等库存业务对象 |
ops | 巡检、脚本记录、辅助管理对象 |
执行:
CREATESCHEMAIFNOTEXISTSsales;CREATESCHEMAIFNOTEXISTSinventory;CREATESCHEMAIFNOTEXISTSops;再次查看:
\dn可以看到:
为了让对象用途更清楚,可以加备注:
COMMENTONSCHEMAsalesIS'Customer and order business objects';COMMENTONSCHEMAinventoryIS'Product and stock business objects';COMMENTONSCHEMAopsIS'Operation check and helper objects';四、理解 search_path
创建了模式后,会遇到一个问题:如果执行下面的 SQL:
SELECT*FROMcustomer;数据库应该去哪一个模式下找customer表?
这就涉及search_path。
查看当前搜索路径:
search_path决定了当 SQL 中没有写模式名时,数据库按什么顺序查找对象。它是方便工具,也是潜在风险。方便之处在于 SQL 可以写得短;风险在于不同会话的search_path不一致时,同一句 SQL 可能访问到不同对象。
SHOWsearch_path;常见结果类似:
这表示当你不写模式名时,数据库会按顺序查找对象。
为了让后续操作更方便,可以在当前会话设置:
SETsearch_pathTOsales,inventory,ops,public;再查看:
SHOWsearch_path;此时如果执行:
SELECT*FROMcustomer;数据库会优先在sales模式中寻找customer。
五、完整对象名更适合文章演示
虽然search_path很方便,但在写技术文章、脚本和生产变更时,我更推荐使用完整对象名:
完整对象名能降低上下文依赖。尤其是文章和教程,读者的环境可能和我文章里写的不完全一致,使用sales.customer这样的写法,比只写customer更稳,也更能体现数据库对象归属。
sales.customer inventory.product ops.script_run_log这样读者一眼就知道对象属于哪里,也能减少“当前搜索路径不一致”导致的报错。
例如后续创建客户表时,不写:
CREATETABLEcustomer(...);而写:
CREATETABLEsales.customer(...);查询也尽量写:
SELECT*FROMsales.customer;这会让文章更稳定,读者复制 SQL 时也不容易受当前会话状态影响。
六、创建一个 ops 检查表
为了验证模式是否可用,我们先在ops模式下创建一张检查表。
CREATETABLEIFNOTEXISTSops.schema_check(idSERIALPRIMARYKEY,schema_nameVARCHAR(50)NOTNULL,check_resultVARCHAR(50)NOTNULL,checked_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP);插入检查记录:
INSERTINTOops.schema_check(schema_name,check_result)VALUES('sales','created'),('inventory','created'),('ops','created');查询:
SELECTid,schema_name,check_result,checked_atFROMops.schema_checkORDERBYid;预期结果类似:
七、设置数据库级默认 search_path
如果每次连接kb_shop后都手工执行:
SETsearch_pathTOsales,inventory,ops,public;会比较麻烦。可以给数据库设置默认搜索路径:
ALTERDATABASEkb_shopSETsearch_pathTOsales,inventory,ops,public;设置后,断开重连:
\q重新连接:
ksql -U system -d kb_shop -h localhost -p 54321再查看:
SHOWsearch_path;如果结果中包含sales, inventory, ops, public,说明数据库级默认配置生效。
如果你不想设置数据库级默认值,也可以不执行这一步。后续文章会尽量使用完整对象名,避免依赖搜索路径。
八、对象命名规范
为了让后续文章风格统一,系列文章采用以下命名规则:
| 类型 | 规则 | 示例 |
|---|---|---|
| 数据库 | 小写,下划线 | kb_shop |
| 模式 | 小写,业务含义明确 | sales、inventory |
| 表 | 小写,单数名词或业务短语 | customer、product、customer_order |
| 字段 | 小写,下划线 | customer_id、created_at |
| 主键 | 表名加_id | product_id |
| 检查约束 | ck_表_字段 | ck_product_price |
| 唯一约束 | uk_表_字段 | uk_customer_phone |
| 外键约束 | fk_子表_父表 | fk_order_customer |
这种命名方式的好处是可读性高,后续排查约束报错时也更容易定位。
九、常见问题排查
问题 1:报错 schema does not exist
如果执行:
CREATETABLEsales.customer(...);却提示schema "sales" does not exist,说明模式还没创建。
先执行:
CREATESCHEMAIFNOTEXISTSsales;再建表。
问题 2:查询表时报 relation does not exist
可能原因是表在某个模式下,但查询时没有写完整对象名。
错误示例:
SELECT*FROMcustomer;推荐写法:
SELECT*FROMsales.customer;或者先设置:
SETsearch_pathTOsales,inventory,ops,public;问题 3:search_path 设置了但没有生效
如果执行了:
ALTERDATABASEkb_shopSETsearch_pathTOsales,inventory,ops,public;但当前窗口里SHOW search_path;仍然没变,原因通常是数据库级配置对新连接生效。
处理方式:
\q重新连接后再查看。
问题 4:不确定某张表在哪个模式
可以查询系统信息:
SELECTtable_schema,table_nameFROMinformation_schema.tablesWHEREtable_name='schema_check'ORDERBYtable_schema;如果后续对象多了,这个查询很实用。
十、本文小结
本文承接kb_shop数据库的创建,完成了数据库内部第一层对象治理:Schema 分层。通过sales、inventory、ops三个模式,我们把后续业务对象按照职责分开,避免所有表都堆在public中。
本文在kb_shop中完成了业务模式规划:
sales 客户、订单 inventory 商品、库存 ops 检查、辅助管理 public 默认模式,尽量少放业务对象同时我们掌握了:
\dn CREATE SCHEMA COMMENT ON SCHEMA SHOW search_path SET search_path ALTER DATABASE ... SET search_path 完整对象名访问下一篇会开始创建真正的业务表:客户表、商品表、订单表、订单明细表,并重点讲字段类型、主键、唯一约束、检查约束和外键约束。也就是说,第四篇完成了对象分层,第五篇会进入关系建模和数据完整性设计。