业务故事站
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 运营分析师把"月报口径"沉淀成可重跑的工作簿
背景
王姐是运营分析师,每月 1 号要给管理层出 GMV 月报。口径(哪些订单计入 GMV、时间窗怎么算)和取数 SQL 一直散落在本地 Excel 和聊天记录里。她决定在平台建一本"月报口径"工作簿:markdown 单元格写口径说明,sql 单元格写取数 SQL,chart 单元格引用它出柱状图,让口径、SQL、结果三者同源同版。
传统做法对比
以前是"Excel 存 SQL、另存一份截图"发给同事,口径每改一次就多一版新文件,月底对数字来回对表还说不清哪份最新。现在同一本工作簿里 markdown 记口径、sql 出数、chart 出图,运行一次即得完整月报。
角色
业务分析师(建簿 + 每月重跑);数据工程师复核 sql 单元格的取数口径。
操作步骤
  1. 登录平台(admin / admin1,端口 18081)
  2. 侧边栏"Notebook 分析"新建工作簿"月报口径"
  3. "+ Markdown"写口径说明(计入范围、时间窗)
  4. "+ SQL"写取数 SQL:SELECT region, SUM(amount) AS total FROM order GROUP BY region
  5. "+ 图表"选择引用该 sql 单元格、类型 bar
  6. 点"全量运行"看整本结果
系统响应
创建工作簿返回 {"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 "华东区营收为什么降了":分步排查,改一格重跑一格
背景
王姐发现华东区营收环比下降 20%,她建一本"华东营收排查"工作簿把排查过程逐格落下来:markdown 写假设、sql 查区域趋势、sql 查华东异常订单明细、chart 引用明细画散点。中途发现假设不对,只改一格重跑即可,不必重跑整本。
传统做法对比
以前在本地写一堆临时 SQL,中间结果散在多个文件里,排查完不留痕迹,下周再遇到同样问题又从头查。现在每一步的假设、SQL、结果都在同一本工作簿里,过程即文档,改哪格跑哪格。
角色
业务分析师(建簿、逐步排查、可视化和结论)。
操作步骤
  1. 新建工作簿"华东营收排查"
  2. "+ Markdown"记录假设:"华东缺货导致订单量下降"
  3. "+ SQL"写 SELECT region, month, SUM(amount) FROM order GROUP BY region, month,单格运行看趋势
  4. "+ SQL"写华东异常订单明细(按金额倒序取前 20 条),单格运行
  5. "+ 图表"引用该明细格画散点
  6. 结论写入 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 生效,运行记录可审计
背景
风控同学要审计"GMV"这个指标的口径和取数过程。工作簿里的 sql 单元格跑的就是 Ontology SQL——RLS/CLS 全程生效,只能看到自己有权限的行/列;每次运行落 fn_notebook_runs,什么时间谁跑了什么 SQL 拿了什么结果,逐格可查。
传统做法对比
以前口径审计靠翻聊天记录和邮件,取数 SQL 在哪跑的、有没有绕权限都不清楚。现在工作簿即交付物,运行记录即证据——谁、何时、跑了什么 SQL、拿到什么结果,全部在运行历史里。
角色
风控 / 审计人员(查看工作簿与运行历史)+ 新入职分析师(接手复现)。
操作步骤
  1. 打开既有"月报口径"工作簿,读 markdown 格的口径说明
  2. 打开"运行历史",逐条看 created_by / created_at 与 cell_results
  3. 在 Nexus 统一搜索输入"华东 营收",看能否命中工作簿
  4. 点击命中结果跳回工作簿页面
  5. 新同事点"全量运行"复现完整分析路径
系统响应
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 即证据。记住几个边界:只读约束、无调度、无版本、无分权。