P2 Foundry
Notebook 代码工作簿
分析师在浏览器里把"取数 SQL + 说明文字 + 可视化"组织成一段一段可独立运行、可整体重跑的单元格流,运行结果随工作簿持久化。看完这 3 个故事,你就能把一次分析变成团队可以复现、可以审计、可以交接的"活文档"。
业务分析师
数据工程师
分析沉淀
运行记录
RLS/CLS
共 3 个故事
能 / 不能速览
✅ 这个主题能做
- text / SQL / chart 三类单元格混排,按 ord 顺序执行
- 逐块运行(运行单格)或全量运行(RunAll,落 fn_notebook_runs 运行记录)
- sql 单元格复用 Ontology SQL 执行器:强制只读 + RLS/CLS 注入 + 编辑态读叠加 + LIMIT 钳制
- chart 单元格引用前方 sql 单元格结果集渲染(不重算),SQL 执行器只跑一次
- 运行历史可回溯:谁在何时跑了什么 SQL、拿了什么结果,逐格可查
⛔ 这个主题做不了
- sql 单元格不能写 UPDATE/DELETE,只读 SELECT(写入请走本体 Action 写路径)
- 无调度能力:定时生成报表是报表引擎(智能报表)的职责,Notebook 是交互式分析
- 无单元格级版本/分支,运行历史是唯一"版本"证据
- 无单元格级权限:任意登录用户可读可写全部工作簿(owner 仅记录语义)
- chart 引用关系创建时不校验,运行期才暴露错误
适用角色
本主题面向三个角色:
- 业务分析师:把取数、口径、结论沉淀成工作簿,逐块/全量运行,是核心使用者。
- 数据工程师:复核 sql 单元格口径、修运行失败的单元格、把工作簿作为交接交付物。
- 风控 / 审计人员:通过运行记录(fn_notebook_runs)回溯取数口径与权限范围内的数据。
平台管理员通过审计日志掌握运行情况;Nexus 统一搜索收录工作簿 RID,供团队检索复用。
能力速览(能做什么)
三类单元格混排
markdown 记口径与结论、sql 取数、chart 可视化,按 ord 顺序编排成一条可复现的分析流水线。
逐块 / 全量运行
单格运行不落库、快速迭代;RunAll 顺序执行全部单元格并落 fn_notebook_runs,单格失败不中断整批。
sql 块安全复用
经 query.OntologySQLService.Execute:只读翻译 + RLS/CLS 注入 + 编辑态读叠加 + LIMIT 钳制,不是绕过安全的后门。
chart 不重算
chart 单元格引用前方 sql 单元格的结果集渲染,SQL 执行器对该格只执行一次;独立运行优先复用最近一次 run 缓存。
运行记录可回溯
每次 RunAll 落 fn_notebook_runs(cell_results JSON),created_by / created_at 齐全,取数口径有证据。
调整指南(怎么调整)
- 改单元格:UpdateCell 为指针语义,只改提供的字段;chart 格改 chart_config 即可换图表类型(bar/line/pie/scatter)。
- 改执行顺序:删除单元格后其余格 ord 自动重排保持 1..n 连续;新增格追加到末尾。
- 改取数范围:sql 单元格用对象 api_name / object_ 前缀 / 物理表名三种 FROM 写法;结果多时自带 LIMIT 防全表。
- 改 chart 引用:先建 sql 单元格拿 id,再建 chart 单元格在 chart_config.cell_id 填该 id(前端下拉直接选)。
- 改安全边界:sql 单元格受 RLS/CLS 约束,换账号运行同一工作簿可能看到不同行/列,属于正常安全语义。
做得好的场景
Notebook 把"分析过程"变成平台一等公民,特别适合以下场景:
- 月报口径的活文档:口径、SQL、结果三者同源同版,每月打开点一次"全量运行"即出数,替代"Excel 存 SQL、另存截图"。
- 业务问题逐步排查:"华东营收为什么降了"这类问题,把假设、取数、图表逐步落格,中途改一格重跑即可。
- 合规留痕:取数走 RLS/CLS 只读安全链路,运行记录逐格可查,风控审计有据可依。
- 团队交接:Nexus 搜"华东 营收"命中 notebook://foundry/<rid>,新同事读 markdown 说明 + 跑一遍 RunAll 即可复现前辈分析路径。
限制与不足
以下是明确的边界,使用前先知道:
- 只读约束:sql 单元格强制只读 SELECT(DML/DDL 黑名单 + 单语句 + 参数化),改数据必须走本体 Action 写路径。
- 无调度:Notebook 是交互式分析,不落定时任务;定时生成快照请用报表引擎(智能报表)。
- chart 仅引用 sql 格:引用 markdown/chart 单元格会运行报错;引用关系创建期不校验,运行期才暴露。
- 无版本/分支:运行历史是唯一"版本"证据,无单元格级内容变更留痕。
- 权限不分权:protected 组任意登录用户可读可写全部工作簿,owner 仅记录语义。
- 运行记录累积:fn_notebook_runs 无保留上限/清理策略,高频 RunAll 会持续累积。
场景故事
故事 1
运营分析师把"月报口径"沉淀成可重跑的工作簿
场景:分析沉淀
角色:业务分析师
耗时:约 10 分钟
- 背景
- 王姐是运营分析师,每月 1 号要给管理层出 GMV 月报。口径(哪些订单计入 GMV、时间窗怎么算)和取数 SQL 一直散落在本地 Excel 和聊天记录里。她决定在平台建一本"月报口径"工作簿:markdown 单元格写口径说明,sql 单元格写取数 SQL,chart 单元格引用它出柱状图,让口径、SQL、结果三者同源同版。
- 传统做法对比
- 以前是"Excel 存 SQL、另存一份截图"发给同事,口径每改一次就多一版新文件,月底对数字来回对表还说不清哪份最新。现在同一本工作簿里 markdown 记口径、sql 出数、chart 出图,运行一次即得完整月报。
- 角色
- 业务分析师(建簿 + 每月重跑);数据工程师复核 sql 单元格的取数口径。
- 操作步骤
-
- 登录平台(admin / admin1,端口 18081)
- 侧边栏"Notebook 分析"新建工作簿"月报口径"
- "+ Markdown"写口径说明(计入范围、时间窗)
- "+ SQL"写取数 SQL:SELECT region, SUM(amount) AS total FROM order GROUP BY region
- "+ 图表"选择引用该 sql 单元格、类型 bar
- 点"全量运行"看整本结果
- 系统响应
- 创建工作簿返回
{"code":0,"data":{"id":"<nb_id>","rid":"<rid>","name":"月报口径","owner":"u1"}};全量运行 POST /notebooks/:id/run 返回运行记录:{
"code": 0, "data": {
"id": "run-uuid", "notebook_id": "nb-uuid",
"cell_results": {
"c1...": {"status": "success", "elapsed_ms": 1, "row_count": 0},
"c2...": {"status": "success", "elapsed_ms": 12, "row_count": 2,
"columns": ["region", "total"], "rows": [["华东", 520.5], ["华北", 310.0]]},
"c3...": {"status": "success", "elapsed_ms": 1, "row_count": 2,
"columns": ["region", "total"], "rows": [["华东", 520.5], ["华北", 310.0]],
"chart_type": "bar", "source_cell": "c2..."}
},
"created_by": "u1", "created_at": "..."
}
}
chart 格复用 sql 格结果,标注 source_cell 与 chart_type。
- 结果洞察
- 三类单元格混排成一条可复现流水线:markdown 记口径、sql 出数、chart 出图。关键在"不重算"契约——chart 格直接复用前方 sql 格的结果集,SQL 执行器只执行一次;运行记录落 fn_notebook_runs,每月版本可回溯对比。以前半天的手工拼报变成一次点击。
- 调整建议
- 口径变动改 markdown 格即可,SQL 不必动;结果行多时在 sql 里加 LIMIT 防全表;每月重跑前先单格运行 sql 验证数字,再 RunAll;chart 格想换样式,改 chart_config.chart_type(bar/line/pie/scatter)即可。
- 动手试一试
- 登录:admin / admin1。页面路径:Foundry 侧边栏"Notebook 分析"。输入内容:新建"月报口径",依次加 markdown、sql(SELECT region, SUM(amount) FROM order GROUP BY region)、chart(引用该 sql 格,bar)。预期结果:全量运行返回 3 个 cell_results,chart 格带 source_cell 与 chart_type,运行历史 +1。
- 限制提示
- Notebook 无调度能力,定时出报请用报表引擎;sql 单元格强制只读,写数据必须走本体 Action;任意登录用户可读可写全部工作簿(无资源级权限);fn_notebook_runs 无清理策略,长期高频运行会累积。
故事 2
"华东区营收为什么降了":分步排查,改一格重跑一格
场景:问题排查
角色:业务分析师
耗时:约 15 分钟
- 背景
- 王姐发现华东区营收环比下降 20%,她建一本"华东营收排查"工作簿把排查过程逐格落下来:markdown 写假设、sql 查区域趋势、sql 查华东异常订单明细、chart 引用明细画散点。中途发现假设不对,只改一格重跑即可,不必重跑整本。
- 传统做法对比
- 以前在本地写一堆临时 SQL,中间结果散在多个文件里,排查完不留痕迹,下周再遇到同样问题又从头查。现在每一步的假设、SQL、结果都在同一本工作簿里,过程即文档,改哪格跑哪格。
- 角色
- 业务分析师(建簿、逐步排查、可视化和结论)。
- 操作步骤
-
- 新建工作簿"华东营收排查"
- "+ Markdown"记录假设:"华东缺货导致订单量下降"
- "+ SQL"写 SELECT region, month, SUM(amount) FROM order GROUP BY region, month,单格运行看趋势
- "+ SQL"写华东异常订单明细(按金额倒序取前 20 条),单格运行
- "+ 图表"引用该明细格画散点
- 结论写入 markdown 格,全量运行留档
- 系统响应
- 单格运行 POST /notebooks/:id/cells/:cid/run 返回
{"code":0,"data":{"status":"success","elapsed_ms":12,"row_count":2,"columns":["name","amount"],"rows":[["apple",3.5],["banana",4.0]]}};sql 执行失败时该格返回 {"status":"error","error":"SQL 执行失败: <详情>"}(HTTP 200,不中断)。
- 结果洞察
- 排查过程"零浪费":chart 格复用前方 sql 格结果,SQL 执行器只跑一次;单格运行不落库、秒级反馈,改一格重跑一格即可收敛结论。最后全量运行把整条排查链落成运行记录,结论和证据留在工作簿里,下周同类问题直接复用。
- 调整建议
- 每步先写假设再跑数据,避免无目标乱查;图表用散点看异常分布、用柱状看趋势;结论必须落成 markdown 格,否则交接时还得口头讲;单格失败先修 sql 再重跑该格,不必等整本。
- 动手试一试
- 登录:admin / admin1。页面路径:Notebook 分析 → 新建"华东营收排查"。输入内容:sql 格 SELECT region, month, SUM(amount) FROM order GROUP BY region, month;再一格查华东订单;chart 引用明细格。预期结果:单格运行各自出结果,chart 格引用成功且 elapsed_ms 极小(未重算)。
- 限制提示
- chart 只能引用"前方已运行且成功"的 sql 格,引用未运行/失败/后方格会返回 error 状态;单元格无并发运行互斥,两用户同时 RunAll 会各自落一条记录;sql 结果行数受 LIMIT 钳制(无 LIMIT 补 500)。
故事 3
取数口径的合规留痕:RLS/CLS 生效,运行记录可审计
场景:合规审计
角色:风控审计
耗时:约 6 分钟
- 背景
- 风控同学要审计"GMV"这个指标的口径和取数过程。工作簿里的 sql 单元格跑的就是 Ontology SQL——RLS/CLS 全程生效,只能看到自己有权限的行/列;每次运行落 fn_notebook_runs,什么时间谁跑了什么 SQL 拿了什么结果,逐格可查。
- 传统做法对比
- 以前口径审计靠翻聊天记录和邮件,取数 SQL 在哪跑的、有没有绕权限都不清楚。现在工作簿即交付物,运行记录即证据——谁、何时、跑了什么 SQL、拿到什么结果,全部在运行历史里。
- 角色
- 风控 / 审计人员(查看工作簿与运行历史)+ 新入职分析师(接手复现)。
- 操作步骤
-
- 打开既有"月报口径"工作簿,读 markdown 格的口径说明
- 打开"运行历史",逐条看 created_by / created_at 与 cell_results
- 在 Nexus 统一搜索输入"华东 营收",看能否命中工作簿
- 点击命中结果跳回工作簿页面
- 新同事点"全量运行"复现完整分析路径
- 系统响应
- GET /notebooks/:id/runs 返回运行历史
{
"code": 0, "data": {
"runs": [
{"id": "run-uuid", "notebook_id": "nb-uuid",
"cell_results": {"c1...": {"status": "success", "elapsed_ms": 1, "row_count": 0}},
"created_by": "u1", "created_at": "2026-08-01T09:00:00Z"}
],
"total": 1
}
}
每条运行带 created_by 与 created_at,可回放当时取数结果。
- 结果洞察
- 工作簿不是绕过安全的暗箱:sql 格继承 RLS/CLS 注入与编辑态读叠加,审计看到的是与本体查询完全一致的数据视图;运行记录即取数证据。新同事接手成本从"讲一遍"变成"跑一遍"——读 markdown 口径、RunAll 复现、对照运行历史核对数字。
- 调整建议
- 关键口径务必写进 markdown 格并保持最新,作为审计依据;交接时把运行历史一并导出核对;Nexus 搜索靠 name/description 文本匹配,工作簿命名写清楚业务语义便于检索;运行记录无保留上限,重要口径建议人工归档。
- 动手试一试
- 登录:admin / admin1。页面路径:Notebook 分析 → 打开工作簿 → 运行历史 / Nexus 搜索。输入内容:Nexus 输入"华东 营收"。预期结果:运行历史按时间倒序列出每次运行的 created_by 与结果;Nexus 命中工作簿并跳转,全量运行可复现。
- 限制提示
- RLS/CLS 按当前登录用户生效,换账号看同一工作簿可能行/列不同,属正常安全语义;CellResult 不透出 edited_fields(编辑态叠加标记),叠加明细需到本体查询确认;运行记录无清理策略,长期累积。
常见问题
sql 单元格能写 UPDATE / DELETE 吗?
不能。Ontology SQL 执行器强制只读 SELECT(DML/DDL 关键字黑名单 + 单语句 + 标识符白名单 + 字符串参数化)。要改数据,请走本体 Action 写路径,那是平台上唯一受控的写入口。
chart 单元格展示的是什么时间的数据?
取决于触发方式:RunAll 用同批内存结果(最新);独立运行 chart 格优先复用最近一次 run 的缓存,无缓存才回退执行被引用的 sql 单元格。数据新鲜度 = 最近一次 run 的时间。
RunAll 单格失败会中断整批吗?
不会。失败格以 status=error 的 CellResult 记录,其余单元格照常执行,整批运行照常落库。仅配置/归属校验错误(工作簿不存在、chart 配置非法等)才返回非 200 错误。
为什么主键是 uuid 不是自增数字?
uuid 跨环境/重建稳定,工作簿的 rid 还能被 Nexus 统一搜索与 Marketplace 收录引用(URI 形如 notebook://foundry/<rid>)。删除工作簿会级联删除其单元格与运行记录。
工作簿能绕过 RLS/CLS 吗?
不能。sql 单元格经 Ontology SQL 执行器注入行级/列级安全,只读路径全程生效。工作簿的安全底线不是"谁能看工作簿",而是"工作簿里的 sql 能查到什么"——后者由 RLS/CLS 兜底。
主题小结
一句话:Notebook 把"分析过程"变成平台一等公民——text/SQL/chart 单元格混排成可复现流水线,逐块或全量运行,sql 格复用 Ontology SQL 安全执行器(RLS/CLS + 编辑态叠加),chart 格引用 sql 结果不重算,运行记录 fn_notebook_runs 即证据。记住几个边界:只读约束、无调度、无版本、无分权。