define-vtable.md 4.71 KB

如何定义虚拟表

xly 中的虚拟表是作为元数据声明的“表”,不是 DDL 创建的真实表。框架之所以知道它,是因为 gdsconfigtbmastergdsconfigtbslave 中的行描述了它的形状;实际数据存储在真实物理表中,但框架操作的抽象是元数据声明。

这不同于数据库视图,视图是 CREATE VIEW SQL 对象。两者都可以通过 gdsconfigformmaster.sType = 'table''view' 支撑表单。

虚拟表用途

  • 让 PM 声明“我需要这种形状的东西”,无需工程师执行 CREATE TABLE
  • 定义下游表单可以叠加使用的数据形状。
  • 集中管理租户感知的列定义,使多个读取同一形状的表单共享默认值。

虚拟表覆盖 lookup 表、分类树和可配置参数集。随着 PM 增加新形状,目录会自由增长。

配方

1. 虚拟表 master — gdsconfigtbmaster

每个虚拟表一行:

sId 唯一虚拟表 ID
sChinese / sEnglish / sBig5 显示名
sBrandsId / sSubsidiaryId 租户作用域
sTbName 底层物理名称。实践中它可以指向表、视图或存储过程;该列有唯一键,但没有更严格约束。运行时把它当成通用 SQL 标识符解析。
sParentId 树形分类的父虚拟表;平铺表为空
iOrder BACK 列表中的排序

2. 列 — gdsconfigtbslave

每列一行。每行携带列名、类型、默认值、显示标签、校验规则,以及它是否属于主键。

sTbName 实际指向什么,以及漂移

每个 gdsconfigtbmaster 行都有非空 sTbName,但该列只是带唯一键的字符串;框架不会强制它解析为基础表。已对实时 dev DB 验证:

  • gdsconfigtbmaster 总计 307 行。
  • 296 行(96.4%)解析到 information_schema.tables 中真实存在的 BASE TABLE
  • 11 行(3.6%)无法解析为基础表,而它们的分布本身很有信息量:
未解析的 sTbName 实际指向 数量 示例
视图(viw_*),不是表 4 viw_mftproductionreportviw_mftproductionreportEmployee1
存储过程(Sp_* 3 Sp_Cashier_BankJournalSp_Cashier_SumJournalSp_Sales_NotDeliverGoodNotifyList
大小写折叠后存在的真实表,或已重命名 / 删除对象 4 QlyProcessTestResult(大小写漂移)等

所以 sTbName 不严格等于“物理表名”;它是运行时会替换进读取查询里的通用 SQL 标识符,也可能指向视图或可调用过程。早先把它写成“底层物理表名”的说法过窄。

可用于暴露漂移的审计 SQL:

SELECT sId, sChinese, sTbName
FROM gdsconfigtbmaster
WHERE sTbName NOT IN (
  SELECT TABLE_NAME FROM information_schema.tables
  WHERE TABLE_SCHEMA = DATABASE()
);

何时选择虚拟表、视图或真实表

需求 选择
PM 声明的新形状,并由真实(可能已有)表支撑 虚拟表
跨已有表的只读 join 数据库视图
真正需要新存储且没有现有表可用的新形状 真实 CREATE TABLE(工程任务)

虚拟表通道是框架对数据驱动形状的“类型系统”;物理 schema 才是真正存储行的地方。两者有意解耦。

示例:包装方式 lookup

dev DB 中的一条代表性真实记录:

Mastergdsconfigtbmaster):

sId       = 192116810113315231587698560
sChinese  = 包装方式 (Packing method)
sTbName   = SisPacking
sParentId = (root)

Slave 列gdsconfigtbslave,该 sParentId 下 10 行)声明的是逻辑形状:名称、显示标签、校验。物理形状位于真实的 SisPacking 表中:

Slave 行 SisPacking 上的物理列
iIncrement(自增列) iIncrement int auto_increment PK
sId(标准ID) sId varchar(100) UNIQUE
sBrandsId(加工商Id) sBrandsId varchar(100)
sSubsidiaryId(子公司Id) sSubsidiaryId varchar(100)
tCreateDate(制单日期) tCreateDate datetime DEFAULT CURRENT_TIMESTAMP
sMakePerson(制单人) sMakePerson varchar(255)
iOrder(排序号) iOrder int DEFAULT 0
sName(名称) sName varchar(255)
sNo(编号) sNo varchar(255)
bInvalid(作废) bInvalid bit(1) DEFAULT b'0'

gdsconfigtbslave 中的 10 行与物理 SisPacking 表上的 10 列逐一对应。PM 随后可以让某条 gdsconfigformmaster 指向 sTbName='SisPacking',form-slave 行再按名称引用同一批列。运行时把这两层粘合起来,路径与切片 1 中的元数据驱动读取相同。

本页之前把“补一个 worked example”列为 TODO;这里就是补上的示例。