表键、枚举与数据关联
在配表系统中,主键、唯一键、枚举和入口都是访问表中数据行的不同方式,而外键则用于建立表之间的关联关系。
核心概念:
- 主键、唯一键、枚举、入口:访问表中数据行的不同方式
- 外键:建立表之间的关联关系,形成数据网络
每个表(table)必须定义主键,逻辑代码通过主键来访问表中的具体数据行。
1. 简单主键
Section titled “1. 简单主键”使用单个字段作为主键,这是最常见的主键形式。
table task[taskid] { taskid:int; text:text;}特点:
- 绝大多数表都使用简单主键
- 支持的类型:
int、long、str - 简单高效,易于理解和使用
2. 复合结构主键
Section titled “2. 复合结构主键”使用自定义结构体作为主键。
struct LevelRank { level:int; rank:int;}
table jewelryrandom[lvlRank] { lvlRank:LevelRank; attack:int;}特点:
lvlRank结构体作为主键- 重要限制:在生成 Lua 代码时,复合结构主键是基于引用(内存地址)而非内容比较的,因此如果需要生成 Lua 代码,不建议使用这种形式
3. 多字段主键
Section titled “3. 多字段主键”使用多个字段组合作为主键。
table jewelryrandom[level,rank] { level:int; rank:int; attack:int;}特点:
level和rank两个字段共同构成主键- Lua 实现细节:当两个字段都是数字类型时,底层会使用
k + j * 100000000作为键值 - 约束条件:第一个字段值必须小于 1 亿,第二个字段值必须小于 1 万
当逻辑代码需要通过多种不同方式访问同一行数据时,可以选择一种方式作为主键,其他方式作为唯一键。
唯一键定义在字段定义之前,使用 [ 和 ] 包围。
table task[taskid] { [nexttask]; taskid:int; // 任务id text:text; nexttask:int; rewardItems:list<RewardItem> (block=1); // 物品奖励}说明:
- 在表定义中,以
[开头的行表示定义唯一键 - 上面的例子中,
nexttask被定义为唯一键 - 唯一键的使用场景相对较少,主要用于需要多种访问方式的特殊情况
Table 枚举与入口
Section titled “Table 枚举与入口”注意:本节介绍的是通过 Table 的
enum属性定义的枚举。 如果需要在 schema 文件中直接定义编译时常量枚举(只有 name 和 comment), 请参考 结构定义 - 枚举。
为了避免在代码中出现大量的”魔数”(magic numbers),配表系统提供了枚举和入口机制, 直接在配置表中定义程序使用的英文名称,这些名称会被生成到代码中。
枚举(Enum)
Section titled “枚举(Enum)”枚举用于定义一组固定的、不重复的英文名称。
table itemtype[id] (enum='type'){ id:int; type:str;}特点:
- 使用
enum='type'声明枚举 type列中的所有值必须是唯一的英文名称- 不能为空值
- 由程序代码填写和维护
入口(Entry)
Section titled “入口(Entry)”入口用于标识表中的特定行,通常用于配置系统的入口点。
table scene[id] (entry='entry') { id:int; entry:str; info:text;}特点:
- 使用
entry='entry'声明入口 entry列中大部分行为空值- 只有少数几行包含英文名称
- 由程序代码填写和维护
外键用于建立表之间的关联关系,支持多种引用方式。
外键声明方式
Section titled “外键声明方式”1. 字段级声明
Section titled “1. 字段级声明”直接在字段定义时声明外键。
struct RewardItem { chance:int; // 掉落概率 itemids:list<int> ->item.item; // 掉落物品 range:Range; // 数量范围}说明:->item.item 表示 list<int> 中的每个整数都引用 item 表的主键
索引到唯一键
Section titled “索引到唯一键”struct AA { taskid:int ->task[nexttask];}说明:->task[nexttask] 表示引用 task 表的 nexttask 唯一键
索引到任意字段(生成列表引用)
Section titled “索引到任意字段(生成列表引用)”table drop[dropid] { dropid:int =>dropItem[dropid]; name:text;}说明:
=>dropItem[dropid]使用=>表示指向任意字段- 生成的程序代码会返回
listRefDropId,这是一个列表
2. 单独行声明
Section titled “2. 单独行声明”在字段定义完成后,单独声明外键关系。
table monster[id] { id:int; lootId:int; // loot lootItemId:int; // item ->Loot:[lootId,lootItemId] ->lootitem; ->AllLoot:[lootId] ->loot[id];}语法解析:
->Loot:[lootId,lootItemId] ->lootitem->:外键定义标识Loot:外键名称[lootId,lootItemId]:本地键由两个字段组成->lootitem:引用目标表
table drop[dropid] { dropid:int; name:text; ->AllDrops:[dropid] =>dropItem[id];}说明:
- 会生成
listRefAllDrops - 与字段级声明的主要区别:外键名称后缀
AllDrops可以自定义
容器类型外键
Section titled “容器类型外键”外键不仅支持单个字段,还支持容器类型中的每个元素。
list 外键
Section titled “list 外键”列表中每个元素都可以作为外键引用。
示例:
struct RewardItem { // 掉落物品列表,每个物品ID都指向item表的item字段 itemids:list<int> ->item.item (fix=2);}说明:
list<int>中的每个整数都作为外键引用item表的item字段- 系统会验证列表中的每个引用是否有效
map 外键
Section titled “map 外键”map 中每个 value 都可以作为外键引用(不支持 key 的外键)。
示例:
struct MonsterDrops { // 怪物掉落表,key是掉落位置ID,value是物品ID drops:map<int,int> =>item.item;}说明:
map<int,int>中每个 value 都作为外键引用item表的item字段- key(第一个 int)不支持外键
容器外键验证
Section titled “容器外键验证”系统会自动验证容器中的每个外键引用:
- list<T>: 验证列表中每个 T 类型的引用
- map<K,V>: 验证每个 value 的引用(key 不支持外键)
外键类型总结
Section titled “外键类型总结”| 符号 | 指向目标 | 生成类型 | 描述 |
|---|---|---|---|
-> | 唯一键(主键或唯一键) | refXxx | 生成单个引用 |
=> | 任意字段 | listRefXxx | 生成列表引用 |
可空外键(Nullable)
Section titled “可空外键(Nullable)”可空外键允许外键引用不存在的情况,为数据关联提供更大的灵活性。
table task[taskid] (entry='entry') { taskid:int ->task.taskextraexp (nullable); // 任务id entry:str; text:text; nexttask:int ->task (nullable);}-
代码生成:
- 在配置了外键引用的字段上添加
(nullable)修饰符 - 生成的代码会使用
nullableRefXXX类型
- 在配置了外键引用的字段上添加
-
Excel 约束规则:
- 当 Excel 单元格为空时,允许外键不存在
- 当 Excel 单元格有内容时,必须能找到对应的外键
-
例外情况:
- 主键/唯一键字段:如果字段是当前表主键或唯一键的一部分(如
task.taskid),不受上述约束限制 - 数值类型为 0:如果字段类型是数值(
int、long、float)且内容为 0(如task.nexttask),也不受约束限制
- 主键/唯一键字段:如果字段是当前表主键或唯一键的一部分(如
最佳实践建议
Section titled “最佳实践建议”-
主键选择:
- 优先使用简单主键(
int、long、str) - 避免在需要生成 Lua 代码的项目中使用复合结构主键
- 多字段主键在 Lua 中有数值范围限制,注意设计时考虑这些约束
- 优先使用简单主键(
-
外键使用:
- 明确区分
->(单个引用)和=>(列表引用)的使用场景 - 合理使用
nullable修饰符提高数据灵活性
- 明确区分
-
枚举与入口:
- 使用它们消除代码中的”魔数”