跳到主要内容

开启分表(窄表)

窄表模式是在分表基础上更进一步的存储形态:数据不仅存到独立的物理表,而且每个字段对应一个独立的列(列名即字段的 normalized_storage_field),不再使用 valueN / labelN 预留列。相比宽表,窄表的表结构更贴近传统数据库设计,便于直接用 SQL 查询、建索引、对接 BI 工具。

三种存储模式对比

CETA 表单数据有三种物理存储形态,由 form_entity 上的三个标志位组合决定:

模式开关组合物理表业务列结构
A. 全局宽表(默认/旧)standalone=0全局共享 form_entity_datavalue1..valueN / label1..labelN(多表单共用预留列)
B. 分表宽表standalone=1standalone_table_normalized=0独立表 <standalone_table_name>仍是 valueN / labelN(只是物理表独立了)
C. 窄表(本节目标)standalone=1standalone_table_normalized=1standalone_table_created=1独立表 <standalone_table_name>每字段一列,列名 = normalized_storage_field

关于这些标志位(重要)standalone_table_normalizedstandalone_table_created 都由 CETA 后端在开启窄表 / 建表成功后自动置 1不需要也不应该手动改 SQL

  • 开启窄表时后端调用 enableFormEntityDataStandaloneTableNormalized(...),把 standalone_table_normalized 置 1,并自动为每个字段回填 normalized_storage_field
  • 建表方法 createOrUpdateTable(...) 执行成功后,后端调用 updateFormEntityStandaloneTableCreated(id, 1)standalone_table_created 置 1。

也就是说,你只要触发「开启窄表 + 建表」的动作,三个标志位会由后端负责设置到位。本文中出现的 UPDATE form_entity SET ... SQL 仅用于排查/回滚等特殊场景,正常流程无需手动执行。

字段与列的映射关系:

  • 宽表列(A、B 通用):form_entity_field.storage_field(如 value43);label 列 = value 列名对应的 labelNvalue43label43)。
  • 窄表列(C):form_entity_field.normalized_storage_field(如 applicant_name);label 列 = value 列名 + _labelapplicant_nameapplicant_name_label)。
  • 系统列三种模式同名:id / form_entity_id / deleted / created_time / updated_time / user_id / updated_user_id / flow_instance_id / thread_id / node_id / object_version

_label 列(value + label 两列)的字段类型:SELECTACLCASCADERTREE_SELECT,以及 DATE。其余字段类型只有单个 value 列,LOOKUP 字段无存储列(跳过)。

开启步骤

  1. 后端配置:确保配置文件中 form_entity_data.standalone.enable = true(与宽表分表相同的前置开关)。

  2. 项目 + 表单设置:在项目上允许独立数据表,并在目标表单上设置独立表名(standalone_table_name)。

  3. 开启窄表 + 建表(后端自动置标志位):触发平台的「开启窄表」动作,后端会:

    • 调用 enableFormEntityDataStandaloneTableNormalized(...),把 standalone_table_normalized 置 1,并为每个字段回填 normalized_storage_field
    • 调用 FormEntityDataStandaloneTableNormalizedService.createOrUpdateTable(formEntity) 生成窄表,建表成功后自动把 standalone_table_created 置 1

    在表单上开启窄表(截图占位)

    触发方式:

    • 通过表单设计器把该表单标记为 standalone + normalized 并保存(平台会自动完成置标志位 + 建表);

    • 或在目标环境用一次性调用触发上述流程(需 form_entity_data.standalone.enable=true)。

    ⚠️ 窄表结构必须由平台建表逻辑生成,不要手写 DDL,也不要手动 UPDATE 这些标志位。因为列名(normalized_storage_field)、label 列、类型映射、以及 standalone_table_normalized / standalone_table_created 标志都由后端统一负责。建表是幂等的:表已存在则只补缺列,不会重建。

  4. 建表产物确认

    • 库中存在表 <standalone_table_name>,含 id + 全部系统列 + 每字段业务列。
    • 执行 SELECT token, storage_field, normalized_storage_field, type FROM form_entity_field WHERE form_entity_id = <feid>,确认 normalized_storage_field 均非空。

    窄表在数据库中的结构(截图占位)

长文本字段的存储差异(重要)

长文本类字段(TEXTAREA / RICH_TEXT / LONG_TEXT)在两种模式下存储位置不同:

模式长文本主值存哪
宽表(A/B)子表 form_entity_data_textlong_text 列,locale IS NULL 为主值),宽表的 valueN 列此时通常为空
窄表(C)内联在窄表列(normalized_storage_field,类型 TEXT)

这一差异直接影响数据迁移:主迁移只搬 valueN 列,长文本必须单独回填。详见下一节。

如果表单已有数据

如果表单在切换到窄表前已经积累了宽表数据(模式 A 或 B),需要把这些历史数据迁移到窄表,才能无损切换。完整的迁移流程见:怎么迁移宽表数据到窄表

开启之后:日常演进(加字段 / 删字段 / 删表)

窄表建好后,表单还会持续演进。加字段由后端自动加列;但删字段、删表属于破坏性操作,后端不会自动执行,需 DBA 手动清理。完整的生命周期说明见:窄表的全生命周期(加字段 / 删字段 / 加表 / 删表)