P2 Foundry
对象化 SQL 工作台与编辑态叠加
在 SQL 工作台里,你写的 FROM order 是业务对象而不是物理表:翻译器把对象名 → 物理表、属性名 → 物理列,注入 RLS/CLS 只读安全,强制外层 LIMIT(无则补 500、上限钳 5000);查询结果叠加编辑态列值并高亮;聚合回退行级 + 内存聚合保证与行级一致,超上限诚实标注 approximate。看完这 4 个故事,你就能安全地用业务语言直查本体数据。
数据工程师
业务分析师
对象翻译
RLS/CLS
编辑态叠加
指标叠加
共 4 个故事
能 / 不能速览
✅ 这个主题能做
- 对象名直查:FROM 写对象 api_name(order)/ object_ 前缀(object_order)/ 物理表名(orders)均可
- 翻译预览:物理参数化 SQL + 绑定参数 + meta(对象 / 物理表 / 生效 LIMIT / RLS/CLS)摊开审计
- 字符串字面量全参数化,RLS/CLS 按当前用户注入,LIMIT 无则补 500、超 5000 与 LIMIT ALL 钳 5000
- 编辑态列叠加:Action 写回后查询立即返回编辑后值并高亮(edited_fields),编辑为 null 也覆盖
- 聚合一致:编辑态聚合回退行级 + 内存聚合(上限 10000),超限返回 approximate 诚实标注
⛔ 这个主题做不了
- 只读受限 SQL:单条 SELECT,DML/DDL 关键字、多语句 / 残留分号、CTE(WITH)全拒绝
- link 关联属性(linkName.prop)本批不支持,需显式 JOIN 目标对象后用 别名.属性
- 属性名与 SQL 保留字同名不支持;黑名单关键字同名函数(如 REPLACE())不可用
- 编辑态仅叠加 FROM 主对象,JOIN 关联对象的编辑态为后续层
- ratio / derived / cumulative 类指标聚合叠加标 P2,当前不叠加(EditedOverlay=false)
适用角色
本主题面向三个角色:
- 业务分析师:用对象属性直写 SQL 自助取数,翻译预览 + CSV 导出,不依赖物理表结构。
- 数据工程师:验证翻译产物、核对 RLS/CLS 注入,把验证过的 SQL 搬到 Notebook 复用。
- 运营人员:通过 Action 改数据后,在 SQL 工作台立即看到编辑后值并核对。
平台管理员在审计里看到查询人、SQL 与安全注入情况;对象必须先建模(本体工作台)才能被 SQL 引用。
能力速览(能做什么)
对象名直查
FROM/JOIN 写对象名即自动翻译为物理表;属性当列名写,映射列名 ≠ 属性名时自动注入 AS 属性名 对齐输出列。
翻译预览
物理参数化 SQL + 绑定参数 + meta(对象 / 物理表 / 查询属性列 / 生效 LIMIT 与模式 / RLS/CLS / 编辑态叠加)摊开给你审计,不执行。
四层安全叠防
标识符白名单 + 关键字黑名单 + 字符串参数化 + RLS/CLS 注入;手写 ? 拒绝、注释先剥离再校验、LIMIT 强制钳制。
编辑态行级叠加
查询结果按主键叠加 ontology_edits 最新编辑值(编辑态永久优先),edited_fields 定位列高亮,编辑为 null 也覆盖。
聚合一致性
编辑态聚合回退行级查询 + 内存聚合,与行级读到同一份数据(上限 10000,超限返回未叠加值并标注 approximate)。
指标叠加标注
simple 指标接入 EditedOverlay 真实值(edited_overlay=true、edit_overlay=exact);不叠加也如实标注,不假装精确。
调整指南(怎么调整)
- 改书写:FROM 三种写法都行(order / object_order / orders);多对象 JOIN 同名属性要用 别名.属性 限定否则 422 歧义;SELECT * 会带出全部属性,建议显式列名。
- 改 LIMIT:不写自动补 500;execute 的 limit 参数在 0~500 之间时作为默认值;>5000 与 LIMIT ALL 钳 5000;MySQL LIMIT m,n 形态保留 m、第二段钳 5000。
- 改安全:RLS/CLS 由 security 配置自动生效,翻译预览里确认 rls_injected / cls_injected;未配置 security 或缺 user_id 会跳过并记 security_note。
- 改叠加:编辑态按 FROM 主对象主键列定位,结果未含主键列会跳过叠加并注记原因;想查关联对象编辑态等后续版本。
- 改消费:验证过的 SQL 可搬到 Notebook 的 sql 单元格复用(共用翻译 / 安全 / 叠加链路);宏 {{macro:name}} 在 execute 前展开。
做得好的场景
对象化 SQL 让“业务语言直查”和“安全可控”同时成立,特别适合以下场景:
- 业务自助取数:分析师不知道物理表结构,用对象属性写 WHERE / ORDER BY 即出结果,翻译预览可审计。
- 翻译审计:跑敏感查询前先看物理 SQL + RLS/CLS 注入 + LIMIT 生效值,安全透明不黑盒。
- 编辑态即时可见:Action 改完数据,SQL 工作台查询立即返回编辑后值并高亮,不用等同步。
- 口径一致:指标聚合与行级查询读到同一份叠加后数据,改完 SUM 就对上,不再“行级与口径对不上”。
限制与不足
以下是明确的边界,使用前先知道:
- 受限只读 SQL:非通用 SQL 引擎——单条 SELECT、无子查询白名单校验、无 link 关联属性。
- WHERE 与编辑态边界:过滤作用于源表值、叠加在查询后,对被编辑字段过滤存在“过滤值 / 展示值不一致”。
- 仅 FROM 主对象叠加:JOIN 关联对象的编辑态为后续层;对象未定义主键列会跳过叠加。
- 黑名单副作用:与黑名单关键字同名的属性 / 函数不可用(REPLACE() 等,安全优先取舍)。
- 聚合上限:编辑态聚合内存计算上限 10000 行,超限返回未叠加值并标注 approximate。
场景故事
故事 1
王姐用对象名直查已发货订单,翻译预览确认物理 SQL 再执行
场景:自助取数
角色:业务分析师
耗时:约 5 分钟
- 背景
- 业务分析师王姐要查“已发货订单里金额最高的三笔”。她不会物理表结构,但知道 order 对象有 amount、status 属性。她在 SQL 工作台用对象名直写 SELECT,先点“翻译预览”确认翻译成什么再执行。
- 传统做法对比
- 以前要写 SQL 得先查表结构(desc / ER 图)、写 join、对列名,再让 DBA 建只读账号;现在 FROM order 就是对象名,属性当列名写,翻译器自动映射物理表与列,翻译预览把物理 SQL 摊开给你审计。
- 角色
- 业务分析师(只读权限即可);数据工程师配置 RLS/CLS 后自动生效。
- 操作步骤
-
- 侧边栏“SQL 工作台”(路由 /foundry/sql),对象下拉选 order,注入模板 SELECT * FROM order LIMIT 100
- 改写为 SELECT order_id, amount, status FROM order WHERE status = 'shipped' ORDER BY amount DESC
- 点“翻译预览”看物理 SQL、绑定参数与 meta
- 点“运行”执行,看结果行与行数 / 耗时
- (可选)点 CSV 导出当页结果
- 系统响应
- 翻译预览返回示例:
POST /api/v1/ontology/sql/translate {"sql":"SELECT order_id, amount, status FROM order WHERE status = 'shipped' ORDER BY amount DESC"}
→ {"code":0,"data":{"physical_sql":"SELECT order_id, amount, status FROM orders WHERE status = ? ORDER BY amount DESC LIMIT 500","args":["shipped"],"meta":{"objects":["order"],"base_tables":["orders"],"columns":["order_id","amount","status"],"limit_applied":500,"limit_mode":"defaulted","rls_injected":true,"cls_injected":true}}}
- 结果洞察
- 翻译器做了四件事:对象名 order → 物理表 orders;属性 amount / status → 物理列;字符串 'shipped' 参数化为 ?(args 一并返回);没写 LIMIT 自动补 500(limit_mode=defaulted)。RLS/CLS 按当前用户注入成功(rls_injected / cls_injected=true)。执行返回 3 行已发货订单(995、1990、1492.5),王姐完全不需要知道物理结构。
- 调整建议
- 对象名三种写法都行(order / object_order / orders 物理表名);多对象同名属性要用 别名.属性 限定否则 422 歧义;SELECT * 会带出全部属性,建议显式列名;属性映射列名 ≠ 属性名时翻译器自动注入 AS 属性名 对齐输出列;翻译预览是免费的审计工具,跑敏感查询前先看物理 SQL。
- 动手试一试
- 登录:admin / admin1。页面路径:SQL 工作台。输入内容:SELECT order_id, amount, status FROM order WHERE status = 'shipped' ORDER BY amount DESC。预期结果:翻译预览物理 SQL 为 orders 表、args=[“shipped”]、limit 补 500,执行返回 3 行。
- 限制提示
- 只读受限 SQL:单条 SELECT,DML/DDL 关键字与多语句拒绝;属性名不能与保留字 / 黑名单关键字同名(REPLACE() 不可用);link 关联属性(linkName.prop)本批不支持,请显式 JOIN 后用 别名.属性;派生表内部列不做白名单校验。
故事 2
小赵连碰三次壁:DELETE 被拒、手写 ? 被拒、LIMIT ALL 被钳,改对后导出 CSV
场景:安全边界
角色:数据工程师
耗时:约 8 分钟
- 背景
- 数据工程师小赵习惯手写 SQL,第一次用工作台时连续碰壁:SELECT 前带了 DELETE 关键字、字符串里手写 ? 占位符、还试了 LIMIT ALL。他逐一改对后把结果导出成 CSV 交给业务。
- 传统做法对比
- 以前在本机连库随便跑,误跑 UPDATE/DELETE 直接污染源表(出过事故);工作台把安全前置:只读白名单、注释先剥离再校验、字符串全参数化、LIMIT 强制外层钳制,怎么作都写不坏库,出问题当场 422 讲清楚原因。
- 角色
- 数据工程师(安全验证与导出);业务分析师接收 CSV。
- 操作步骤
-
- 输入 SELECT * FROM order; DELETE FROM order 点运行,观察 422
- 输入含手写 ? 的查询,观察“不允许手写 ? 占位符”
- 输入 SELECT order_id, amount FROM order LIMIT ALL,观察被钳制
- 改写为合法查询 LIMIT 5,运行看结果
- 点“CSV 导出”,用 Excel 打开确认中文不乱码
- 系统响应
- 错误与钳制返回示例:
POST /api/v1/ontology/sql/translate
→ {"code":"VALIDATION_ERROR","error":"仅允许只读 SELECT,检测到禁用关键字 \"DELETE\""}
→ {"code":"VALIDATION_ERROR","error":"SQL 不允许手写 ? 占位符(字符串字面量将自动参数化)"}
LIMIT ALL → {"code":0,"data":{"physical_sql":"SELECT order_id, amount FROM orders LIMIT 5000","meta":{"limit_applied":5000,"limit_mode":"clamped"}}}
- 结果洞察
- 受限不是“难用”而是“防呆”:DML/DDL 关键字以整词判定(created_by 不受影响)、注释先剥离再校验(注释里写 DROP 不误杀、正文关键字不因注释漏杀)、手写 ? 拒绝防参数错位注入、LIMIT ALL 钳到 5000(不存在无限制出口)。CSV 导出带 BOM,Excel 打开中文正常,合法查询秒级出结果。
- 调整建议
- 想导出大结果先确认 LIMIT 生效值;LIMIT m,n(MySQL 形态)保留 m、第二段钳 5000;未闭合引号 / 块注释会 422,先格式化 SQL;SQL 工作台与 Notebook 的 sql 单元格共用同一翻译链路,验证过的 SQL 可搬到 Notebook 复用。
- 动手试一试
- 登录:admin / admin1。页面路径:SQL 工作台。输入内容:SELECT order_id, amount FROM order LIMIT ALL。预期结果:翻译预览 limit_applied=5000、limit_mode=clamped;再输 SELECT order_id, amount FROM order LIMIT 5,点 CSV 导出。
- 限制提示
- 结果集 LIMIT 钳 5000 封顶,无分页 / 游标语义;黑名单关键字同名函数不可用;WHERE 过滤作用于源表值、编辑态叠加发生在查询后,对被编辑字段过滤存在“过滤值 / 展示值不一致”边界。
故事 3
编辑态列叠加:Action 改完立即查到新值,行级与聚合保持一致
场景:编辑态叠加
角色:运营人员
耗时:约 8 分钟
- 背景
- 运营小陈用 Action 把 order_id=2 的状态从 pending 改成 cancelled(编辑态写入 ontology_edits)。随后她在 SQL 工作台查 order_id=2,结果里的 status 直接显示 cancelled 且列高亮;她又对库存表做了 SUM 聚合,聚合值同样反映编辑态(stock_level 30 改成 45)。
- 传统做法对比
- 以前改完源表还要等同步 / 缓存生效,或者查出来还是旧值,行级与口径对不上要扯皮;现在编辑态永久优先:Action 写回后,SQL 工作台与对象查询读到同一份叠加后数据,行级与聚合一致,改完立即可查。
- 角色
- 运营人员(发起编辑);数据工程师验证叠加链路。
- 操作步骤
-
- 先在“Action 测试”用 update_order_status 把 order_id=2 改为 cancelled(带 expected_updated_at)
- 打开 SQL 工作台,查询 SELECT order_id, amount, status FROM order WHERE order_id = 2
- 看结果 status=cancelled、edited_fields 高亮“status”
- 用 update_inventory 把 inventory_id=2 的 stock_level 从 30 改成 45
- 运行 SELECT SUM(stock_level) AS total_stock FROM inventory,看聚合值是否反映编辑
- 系统响应
- 执行与聚合返回示例:
POST /api/v1/ontology/sql/execute {"sql":"SELECT order_id, amount, status FROM order WHERE order_id = 2"}
→ {"code":0,"data":{"columns":["order_id","amount","status"],"rows":[[2,995,"cancelled"]],"edited_fields":["status"],"row_count":1,"meta":{"objects":["order"],"base_tables":["orders"],"limit_applied":500,"limit_mode":"defaulted","overlay_note":"编辑态叠加属性: status"}}}
聚合 → SUM(stock_level) = 170(120+45+5),EditOverlay=exact
- 结果洞察
- 编辑态列叠加以“编辑态永久优先”覆盖结果列,含编辑为 null 也覆盖;edited_fields 与 overlay_note 诚实标注,不会假装精确。聚合侧走“回退行级查询 + 内存聚合”:与行级查到同一份叠加后数据,保证 SUM 与逐行加总一致(120+45+5=170)。参与聚合行数超过 OverlayRowLimit(默认 10000)时返回未叠加聚合值并标注 approximate——小表是 exact,大表诚实降级。
- 调整建议
- 编辑态叠加按 FROM 主对象主键列定位,结果要含主键列否则跳过并注记原因;SELECT * 时叠加按全属性处理;对象未定义主键列会跳过叠加;对被编辑字段做 WHERE 过滤有“过滤值 / 展示值不一致”边界,先想清楚过滤语义。
- 动手试一试
- 登录:admin / admin1。页面路径:Action 测试 → SQL 工作台。输入内容:先把 order 2 改 cancelled,再查 SELECT order_id, amount, status FROM order WHERE order_id = 2。预期结果:status 显示 cancelled、edited_fields=[“status”]、overlay_note 出现;改库存后 SUM 聚合反映新值。
- 限制提示
- 仅 FROM 主对象叠加,JOIN 关联对象的编辑态为后续层;行级与聚合一致性依赖 OverlayRowLimit(默认 10000),超限返回 approximate;WHERE 过滤基于源表值,与叠加后展示值可能不一致。
故事 4
指标查询接入 EditedOverlay:simple 叠加为 exact,ratio/cumulative 标注 P2
场景:指标叠加
角色:数据工程师
耗时:约 8 分钟
- 背景
- V5 把指标查询接入了编辑态聚合叠加(B1-5 第 2 层)。老赵用 update_inventory 把上海仓 2 号库存 stock_level 从 30 改成 45 后,分别查询 simple 指标 total_stock(SUM(stock_level))与 ratio 指标 aov、cumulative 指标 cumulative_gmv,验证哪些指标能反映编辑态。
- 传统做法对比
- 以前指标口径与行级查询“各说各话”:Action 改了数据,行级能看到新值,指标聚合还是旧值,对不上要反复核对;现在 simple 指标聚合回退行级 + 内存聚合,与行级完全一致,且 meta 用 edited_overlay / edit_overlay 诚实标注叠加状态。
- 角色
- 数据工程师(建指标、验证叠加);业务分析师消费指标。
- 操作步骤
-
- “Action 测试”用 update_inventory 把 inventory_id=2 的 stock_level 改为 45
- 指标管理确认 total_stock 是 simple 指标(SUM(stock_level))
- POST /metrics/query {“name”:“total_stock”} 看结果
- 再查 aov(ratio)与 cumulative_gmv(cumulative),对比 meta 标注
- 到审计确认编辑态记录(ontology_edits)
- 系统响应
- 指标查询返回示例:
POST /api/v1/metrics/query {"name":"total_stock"}
→ {"code":0,"data":{"columns":["total_stock"],"rows":[[170]],"metric":{"name":"total_stock","formula":"SUM(stock_level)","metric_type":"simple","edited_overlay":true,"edit_overlay":"exact"}}}
POST /api/v1/metrics/query {"name":"aov"} → {"code":0,"data":{"metric":{"metric_type":"ratio","edited_overlay":false,"edit_overlay":""}}}
- 结果洞察
- simple 指标命中编辑态聚合叠加:total_stock 从 155 变成 170(库存 120+45+5),与行级 SUM 完全一致(edited_overlay=true、edit_overlay=exact);聚合走“回退行级查询 + 内存聚合”,上限 OverlayRowLimit=10000,超过则返回未叠加值并标注 approximate。ratio(aov)、derived、cumulative(cumulative_gmv)类指标聚合叠加为 P2,当前保持未叠加并如实标注 EditedOverlay=false——不叠加也明说,不会把旧值冒充精确值。
- 调整建议
- 关键指标配 simple 度量即可享受叠加;无相关字段编辑时走 SQL 聚合零叠加开销(不损失性能);大表聚合(超 1 万行)会标注 approximate,注意行数规模;想对 ratio / derived 也叠加,等 P2 扩展并在 meta 里跟踪 edit_overlay 字段。
- 动手试一试
- 登录:admin / admin1。页面路径:Action 测试 → 指标管理 → 指标查询。输入内容:update_inventory 改 stock_level=45 后查 total_stock。预期结果:total_stock=170、metric.edited_overlay=true、edit_overlay=“exact”;查 aov 则 edited_overlay=false。
- 限制提示
- 仅 simple 指标叠加,ratio / derived / cumulative 标 P2;无编辑态读取器注入或无相关字段编辑时直接走 SQL 聚合(EditedOverlay=false);超 OverlayRowLimit(10000)返回 approximate;编辑态叠加仅作用于单实体聚合。
常见问题
FROM 到底能写什么?
对象 api_name(order)、object_ 前缀(object_order)、对象物理表名(orders)三种均可,翻译器统一解析为物理表。写不存在的名字会 422:“标识符 xxx 无法解析为本体对象”。
LIMIT 没写会怎样?
自动补默认 500(execute 的 limit 参数在 0~500 之间时作为默认值);写 >5000 或 LIMIT ALL 钳 5000;LIMIT m,n(MySQL 形态)保留 m、第二段钳 5000。不存在无限制出口。
为什么有时列名会自动加 AS?
当属性映射列名 ≠ 属性名(如属性 amount 映射物理列 order_amount)时,翻译器自动注入 order_amount AS amount,输出列对齐属性 api_name,编辑态叠加也按属性名对齐。
编辑态叠加是“覆盖”还是“只读展示”?
是查询层叠加:结果列按主键对齐后用最新编辑值覆盖(编辑态永久优先,编辑为 null 也覆盖),edited_fields 与 overlay_note 诚实标注。它不改源表,只是让“改完立即可查”。
为什么指标 aov 不叠加编辑态?
aov 是 ratio 类指标(分子 / 分母组合聚合),与 derived / cumulative 一样,聚合叠加为 P2 扩展;当前保持未叠加并如实标注 EditedOverlay=false、edit_overlay 为空,不假装精确。
工作台和 Notebook 的 SQL 有什么区别?
Notebook 的 sql 单元格复用同一 Ontology SQL 服务实例,翻译 / 安全 / 编辑态叠加 / 宏展开链路完全一致;区别只是入口与编排方式(Notebook 可多单元格串成分析流程)。
主题小结
一句话:对象化 SQL 工作台让“业务语言直查”与“安全可控”同时成立——对象名 → 物理表、属性 → 物理列自动翻译,RLS/CLS 注入,LIMIT 补 500 / 钳 5000;编辑态列叠加(行级 + 聚合一致,上限 10000 超限 approximate),simple 指标接入 EditedOverlay 真实值。记住几个边界:只读受限、link 属性不支持、仅 FROM 主对象叠加、ratio/derived/cumulative 标 P2。