如何定义虚拟表
xly 中的虚拟表是作为元数据声明的“表”,不是 DDL 创建的真实表。框架之所以知道它,是因为 gdsconfigtbmaster 和 gdsconfigtbslave 中的行描述了它的形状;实际数据存储在真实物理表中,但框架操作的抽象是元数据声明。
这不同于数据库视图,视图是 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_mftproductionreport、viw_mftproductionreportEmployee1
|
存储过程(Sp_*) |
3 |
Sp_Cashier_BankJournal、Sp_Cashier_SumJournal、Sp_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 中的一条代表性真实记录:
Master(gdsconfigtbmaster):
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;这里就是补上的示例。