Agent 写完一段业务逻辑,跑出来的结果表有 500 行。预期表也有 500 行。哪些行对了、哪些值错了、多了还是少了?

你打开 SQL 编辑器,开始写 LEFT JOIN + CASE WHEN + UNION ALL……写完发现漏了 NULL 的情况,改。改完发现排序不对,再改。折腾半小时,终于拿到差异,复制粘贴给 Agent,让它改代码。Agent 改完,你再跑一遍这套 SQL……

代码有 git diff,一行命令。数据呢?

DIFF TABLE actual_result AGAINST expected_result;

这是 seekdb 1.2.0 引入的 SQL 原语。

Agent 写代码,用它验证输出是否符合预期;Agent 改数据,用它审核改了哪些行、值对不对,再决定要不要合回主表。思路和 Git 一样——Fork 建分支,DIFF 看差异,MERGE 合回去。

立即试用 OceanBase 企业版,体验国产数据库能力

180 天免费试用,零门槛开通

你正在经历这些痛点吗?

Agent 参与开发后暴露出的问题,以及多分支开发、多团队协作、实验回收,本质上都是同一个问题:数据变更缺少像代码一样的分支、对比和合并能力。

如果以上有一条戳中你,接着往下看。

seekdb 的做法:先 Fork 出副本,让 Agent 在副本上执行;再用 DIFF 看清差异;确认无误后,用 MERGE 按策略合回。把数据层也变成像 Git 一样可分支、可比对、可合并。

seekdb 1.2.0 带来了三个新的 SQL 原语——Fork Database、DIFF TABLE、MERGE TABLE——在数据库内核层面补齐了数据版本控制的完整闭环。配合此前已有的 Fork Table 能力,seekdb 构建起了从单表分支到整库克隆、从差异对比到策略合并的完整数据版本控制体系。

先看一个最贴近 coding agent 的例子:

-- 基线库:输入表 + 结果模板表
CREATE TABLE test.orders_input (id INT PRIMARY KEY, amount DECIMAL(10,2), status VARCHAR(20));
INSERT INTO test.orders_input VALUES(1, 100, 'pending'), (2, 200, 'pending');
CREATE TABLE test.result_template (id INT PRIMARY KEY, final_amount DECIMAL(10,2));
-- 每次测试先 Fork 一份隔离环境
FORK DATABASE test TO test_run;
-- Agent 写的业务逻辑跑出来的实际结果
FORK TABLE test_run.result_template TO test_run.actual_result;
INSERT INTO test_run.actual_result
SELECT id, CASE WHEN amount >= 150 THEN amount * 0.9 ELSE amount END
FROM test_run.orders_input;
-- 人工或规则系统给出的预期结果
FORK TABLE test_run.result_template TO test_run.expected_result;
INSERT INTO test_run.expected_result VALUES(1, 100), (2, 180);
-- 对比实际结果和预期结果
DIFF TABLE test_run.actual_result AGAINST test_run.expected_result;

DIFF 没有输出,说明 Agent 写的逻辑符合预期;有输出,差异精确到具体行和具体值,可以直接反馈给 Agent 继续修改。同样的思路也适用于 Agent 改数据:先在 Fork 出的副本上执行修改,用 DIFF 审核改动,确认后 MERGE … STRATEGY THEIRS 合回主表。

下面逐一介绍 Fork Database、DIFF TABLE、MERGE TABLE 三个原语的设计、语法和实战用法。

Fork Database:从单表分支到整库克隆

回顾 Fork Table

seekdb 1.1.0 引入了 Fork Table,通过写时复制(Copy-on-Write)和快照隔离,实现了毫秒级的单表数据分支。一条 FORK TABLE t1 TO t1_fork;,即可在一致性快照点上创建一个逻辑独立、完全可读写的表副本,源表和分支表互不干扰。

关于 Fork Table 的技术原理和使用方式,我们此前已有专文详细介绍,这里不再展开。

Fork Table 解决了单表沙箱问题。但真实业务往往不止一张表。

Fork Database 解决什么新问题

假设你有一个业务库 prod,里面有 orders、users、payments 三张表,它们之间通过外键关联。你想基于当前生产数据创建一个隔离的实验环境。

如果逐表 Fork,每张表的快照时刻可能不同——orders Fork 时 payments 还没写入最新一笔,导致实验库中订单和支付记录对不上。外键约束也无法跨表保持。

Fork Database 正是为了解决这个问题。它在指定时间点(系统自动选取的快照时刻)为源数据库创建一个完整的目标数据库副本,所有用户表共享同一快照版本。

核心特性

库级原子快照:目标库内所有表共享同一 fork 快照版本,多表关联查询、外键约束在 fork 后保持逻辑一致

  • 数据库属性继承:目标库继承源库的字符集(charset)、排序规则(collation)等数据库级属性
  • 库内外键完整保留:源库中表间外键关系(仅限库内引用)在目标库中完整保留并生效;跨库外键在 fork 时被跳过,不在目标库中创建
  • 支持多次 fork:支持对同一源库多次 fork 到不同目标库,每次 fork 使用各自快照时刻,各目标库相互独立

语法与示例

FORK DATABASE source_database TO destination_database;

一个最小可执行的流程:

-- 创建库副本
FORK DATABASE db_src TO db_fork;
-- 目标库可查询
USE db_fork;
SELECT count(*) FROM t1;
-- 目标库可写入(写入不会影响源库 db_src)
INSERT INTO t1 VALUES(...);

也可以用于整库分支与版本管理:

-- 1) 基于主版本创建分支版本(数据基线为 fork 时刻快照)
FORK DATABASE db_prod TO db_prod_branch_v2;
-- 2) 在分支库上进行实验性变更
-- ... 对 db_prod_branch_v2 中的表执行 DML/业务流程 ...
-- 3a) 快速回滚:停止使用分支版本
DROP DATABASE db_prod_branch_v2;

Fork 之后可以放心地改。但改了哪些行?值对不对?能不能合回主表?

没有 Diff & Merge,这些问题只能靠手写 JOIN 和对比 SQL 来回答

DIFF TABLE 与 MERGE TABLE:看清改了什么,安全合回去

Fork 出副本 → 在副本上改 → DIFF 看清差异 → MERGE 按策略合回。和 Git 的 branch、diff、merge 一一对应。

先看一个最小、可直接执行的例子:

CREATE TABLE demo.student_base (id INT PRIMARY KEY, name VARCHAR(20), score INT);
INSERT INTO demo.student_base VALUES(1, 'Alice', 80), (2, 'Bob', 90), (3, 'Charlie', 70);
FORK TABLE demo.student_base TO demo.t_current;
FORK TABLE demo.student_base TO demo.t_incoming;
UPDATE demo.t_incoming SET score = 85 WHERE id = 1;
DELETE FROM demo.t_incoming WHERE id = 2;
INSERT INTO demo.t_incoming VALUES(4, 'David', 95);
-- 先看差异
DIFF TABLE demo.t_incoming AGAINST demo.t_current;
-- 再决定是否合并;这里用 THEIRS 表示冲突时采用 incoming 版本
MERGE TABLE demo.t_incoming INTO demo.t_current STRATEGY THEIRS;
-- 合并后查看结果
SELECT * FROM demo.t_current ORDER BY id;

这个例子里会发生什么?

  • id=1:两边都有,但 score 不同,属于冲突行
  • id=2:只在 t_current 里有,属于 current 独有
  • id=3:两边完全相同,不会出现在 DIFF 结果里
  • id=4:只在 t_incoming 里有,属于 incoming 独有

预期 DIFF 输出:

__table      __flag  id  name     score
t_current    INSERT   1  Alice    80
t_incoming   INSERT   1  Alice    85
t_current    INSERT   2  Bob      90
t_incoming   INSERT   4  David    95

执行 MERGE … STRATEGY THEIRS 后,t_current 预期变成:

id  name     score
1   Alice    85
2   Bob      90
3   Charlie  70
4   David    95

DIFF TABLE

DIFF TABLE incoming_db.incoming_t AGAINST current_db.current_t;

行为:

  • 语句顺序:固定为 incoming 在前、current 在后;这和结果集中同一主键下的展示顺序是两件事
  • 只读:不修改任何表,仅输出差异结果集
  • 对比规则:按主键逐行对比 current 与 incoming。主键相同、非主键列值不同视为冲突,两表各输出一行;主键仅在一表存在视为独有;主键和值都相同则不输出
  • 结果排序:按主键排序,同一主键下 current 行排在前面
  • NULL 比较:使用 <=> 运算符,NULL 与 NULL 视为相等
  • 前置条件:两表列定义需完全一致(列名、类型、顺序),且均有主键

MERGE TABLE

MERGE TABLE incoming_db.incoming_t INTO current_db.current_t
  STRATEGY {FAIL | THEIRS | OURS};

行为:

  • 目标:将 incoming 的变更合并到 current 表,会修改 current 的数据
  • 事务:在单个事务中执行,失败则自动回滚(包括 FAIL 策略检测到冲突时)
  • 非冲突行(所有策略一致):incoming 有而 current 无的行,插入到 current;current 有而 incoming 无的行,保留不动。MERGE 不删除任何行
  • 冲突行(主键相同、值不同):按所选策略处理——FAIL 报错回滚,THEIRS 用 incoming 覆盖,OURS 保留 current 不动

限制:暂不支持 schema 不同的表。DIFF 和 MERGE 要求两表列定义完全一致(列名、类型、顺序),否则会报错。

典型应用场景

开篇展示了 Agent 写代码和 Agent 改数据两个场景。实际上,Fork + DIFF + MERGE 的组合能覆盖更多数据工作流。下面逐一展开。

场景 1:AI Agent 写代码→测试→Debug 闭环

开篇的 coding agent 示例已经展示了完整流程(参见上文 SQL)。这里补充做法要点:

做法:先准备输入表和结果模板 → 每轮测试 Fork 一份隔离库 → 在隔离库里再 Fork 两张结果表 → 表 A 执行业务逻辑(AI 写的代码)→ 表 B 用 SQL 写入预期结果 → DIFF 对比 A 与 B → 无差异则通过,有差异则把差异点反馈给 Agent 改代码,重新跑测试。

价值:AI 写代码 → 自动化测试 → 差异可定位 → 反馈 Agent 修改 → 再测,完成「写代码→测试→debug」闭环。DIFF 没有输出即通过,有输出则冲突行 = 值不对,独有行 = 多了或少了一条,差异精确到行和值,可直接回传给 Agent。

场景 2:AI Agent 开发与上线(数据修改)

内核:与场景 1 不同,这里 Agent 改的不是代码而是数据本身(如标注、清洗)。需要验证改动是否符合预期,并支持灰度上线。

做法:Agent 在 Fork 出的库或表上改;自测时 DIFF 验证数据变动;上生产时 Fork 灰度副本 → 小流量 → DIFF 检查 → MERGE 切回。

-- Agent 开发:先 Fork 一份实验库,在副本上改
FORK DATABASE prod TO exp_labeled_data_v2;
-- Agent 执行标注逻辑,写入 exp_labeled_data_v2.labeled_data ...
-- 自测:DIFF 验证改动是否符合预期
DIFF TABLE exp_labeled_data_v2.labeled_data AGAINST prod.labeled_data;
-- 上生产:Fork 灰度库,切小流量,跑一段时间后
FORK DATABASE prod TO prod_gray;
-- 小流量写入 prod_gray.labeled_data ...
-- 检查无异常后
DIFF TABLE prod_gray.labeled_data AGAINST prod.labeled_data;
MERGE TABLE prod_gray.labeled_data INTO prod.labeled_data STRATEGY THEIRS;

整个流程和代码的 PR Review → Merge 一一对应:Fork 是建分支,DIFF 是看 diff,MERGE 是合并。数据变更也有了 Code Review 的能力。

场景 3:实验结果回收与分析

内核:新功能上线前要做实验(随机分流或按标签人群),传统做法是在代码里埋逻辑,实验回收时专门人写一堆 SQL 结合前端埋点做分析,流程重、周期长。用 DIFF 可以直接对比实验组与对照组的结果表,差异行里带 age、标签等维度,一眼看出「哪个年龄段/人群差异大」。

做法:先基于同一份基线用户集 Fork 出实验组和对照组两张表(结构相同,含 user_id、age、标签等);实验流量分别写入这两个副本;DIFF 对比两表,冲突行 = 同一用户在不同组的行为差异,独有行 = 仅在一组出现的用户;差异行里带 age 等信息,可直接分析「点击这个按钮的人哪个年龄段比较多」。

-- 基线用户集:先落一份对照组数据
CREATE TABLE exp.clicks_base (user_id INT PRIMARY KEY, age INT, click_count INT);
INSERT INTO exp.clicks_base VALUES(1, 25, 2), (2, 35, 4), (4, 45, 1);
-- 基于同一份基线 Fork 出实验组和对照组
FORK TABLE exp.clicks_base TO exp.control_clicks;
FORK TABLE exp.clicks_base TO exp.experiment_clicks;
-- 实验组写入新按钮流量后的结果
UPDATE exp.experiment_clicks SET click_count = 3 WHERE user_id = 1;
UPDATE exp.experiment_clicks SET click_count = 5 WHERE user_id = 2;
DELETE FROM exp.experiment_clicks WHERE user_id = 4;
INSERT INTO exp.experiment_clicks VALUES(3, 25, 2);
-- DIFF 对比:差异行直接带 age,可分析「哪个年龄段在实验组有更多点击」
DIFF TABLE exp.experiment_clicks AGAINST exp.control_clicks;
-- 冲突行 = 同一 user_id 在两组的 click_count 不同(如 user_id=1: 2 vs 3)
-- 独有行 = 仅在一组出现的用户(如 user_id=3 实验组独有,user_id=4 对照组独有),含 age
-- 差异行自带 age,按 age 聚合即可回答「哪个年龄段差异大」

价值:实验回收不用再写一堆 JOIN 和对比 SQL,DIFF 直接给出差异,差异行自带维度信息,分析效率提升。

场景 4:多分支开发——拉分支同时 Fork 新库

内核:代码从 master 拉分支,测试库却共用一套。A 的测试数据污染 B 的环境,B 改的表让 A 的用例挂掉;CI 跑不通、问题难复现、「本地能跑,一合就乱」。

做法:拉新分支时 Fork 新库,每人独享;随时 DIFF 查看改动;合并代码时 MERGE 合回数据。

-- 开发者 A:Fork 并修改
FORK DATABASE test_db TO test_db_feature_a;
UPDATE test_db_feature_a.orders SET status = 'shipped' WHERE id = 1;
INSERT INTO test_db_feature_a.orders VALUES(10, 100, 99.9, 'pending');
-- 合并前查看改动
DIFF TABLE test_db_feature_a.orders AGAINST test_db.orders;
-- 确认后合回
MERGE TABLE test_db_feature_a.orders INTO test_db.orders STRATEGY THEIRS;

场景 5:多团队并行改同一份数据

-- 风控线 Fork 并修改
FORK DATABASE prod TO prod_riskfix;
UPDATE prod_riskfix.orders SET risk_flag = 1 WHERE order_id = 10002;
-- 活动线 Fork 并修改
FORK DATABASE prod TO prod_promo;
UPDATE prod_promo.orders SET amount = amount * 0.9 WHERE order_id IN (10001, 10002);
-- 先合风控
MERGE TABLE prod_riskfix.orders INTO prod.orders STRATEGY THEIRS;
-- 再合活动(若有冲突,选 THEIRS 或 OURS)
DIFF TABLE prod_promo.orders AGAINST prod.orders;
MERGE TABLE prod_promo.orders INTO prod.orders STRATEGY THEIRS;

内核:风控、运营、算法同时改同一张表,「你先改还是我先改」协调成本高,冲突全靠人工对表、拉群沟通。

做法:风控、运营、算法各 Fork 分支,各自改完;DIFF 检查冲突,按业务选 THEIRS 或 OURS 合并。

每个团队在自己的分支里工作,互不干扰。合并时 DIFF 清楚地列出冲突行,按业务规则选择策略,不再需要人工逐行对比。

场景 6:AI 模型训练与标注迭代

内核:与场景 2 类似,但侧重模型训练迭代。标注、调参、重训直接动主表,风险大;改了多少、该不该合回,说不清,也不敢合。

做法:Fork 实验库,在副本上做标注、调参,主表不动;DIFF 量化改动,验证通过后 MERGE 合回。

-- Fork 出实验库
FORK DATABASE prod TO exp_training_data_v2;
-- 在 exp_training_data_v2.training_data 上做新标注、调参 ...
-- 查看改动量
DIFF TABLE exp_training_data_v2.training_data AGAINST prod.training_data;
-- 验证通过后合回
MERGE TABLE exp_training_data_v2.training_data INTO prod.training_data STRATEGY THEIRS;

场景 7:数据同步与跨环境合并

内核:远端数据导回来,新增多少、有没有冲突,要手写一堆 SQL 才能对比,容易漏、容易错。

做法:先 Fork 一本地工作库,再把远端快照导入工作库中的隔离表;DIFF 预览新增与冲突,MERGE 按策略合入。

-- 先 Fork 一本地工作库
FORK DATABASE local TO sync_workspace;
-- 远端数据已导入 sync_workspace.remote_snapshot
-- 预览差异
DIFF TABLE sync_workspace.remote_snapshot AGAINST local.main_table;
-- 以远端为准合入
MERGE TABLE sync_workspace.remote_snapshot INTO local.main_table STRATEGY THEIRS;
-- 或以本地为准,只补新增
MERGE TABLE sync_workspace.remote_snapshot INTO local.main_table STRATEGY OURS;

场景 8:数据质量校验与审计

内核:预期表和实际表的对比是质量校验和回归测试中的常见需求。传统做法是写复杂的 JOIN 和 CASE WHEN 来发现不一致,维护成本高,容易漏。

做法:先 Fork 一份待审数据副本,再用一条 DIFF 发现预期表与实际表的差异。

-- 预期表 qa.expected,待审副本 qa.actual_review 由生产表 Fork 而来
FORK TABLE prod.actual TO qa.actual_review;
DIFF TABLE qa.actual_review AGAINST qa.expected;
-- 有输出即表示存在不一致(冲突=值不同,独有=缺失或多余)

一条 DIFF 替代一堆 JOIN,差异结果精确到行和值,冲突行代表值不同,独有行代表缺失或多余。

落地建议

  • 先小表试流程,再扩到核心链路
  • 把 DIFF 当评审材料,数据变更也能 Code Review
  • 关键表要有主键——DIFF 和 MERGE 依赖主键做逐行对比
  • 分支表命名要能一眼看懂用途(如 prod_riskfix、exp_training_data_v2)

告别手写 JOIN,数据变更也能 Code Review

从 seekdb 1.1.0 的 Fork Table,到 1.2.0 的 Fork Database + DIFF TABLE + MERGE TABLE,seekdb 正在系统性地构建数据库内核级的版本控制能力。

这套能力的设计思路很明确:

  • Fork(Table / Database)提供分支能力——安全地创建隔离副本
  • DIFF TABLE 提供对比能力——精确地看清差异
  • MERGE TABLE 提供合并能力——按策略安全地合回

三者组合,形成了和 Git 的 branch / diff / merge 对应的数据版本控制闭环。当数据像代码一样频繁迭代——可观测、可控制的变更路径,才能让数据管理更安全、可审计、易协作。

立即试用 OceanBase 企业版,体验国产数据库能力

180 天免费试用,零门槛开通

Logo

了解最新的技术洞察和前沿趋势,参与 OceanBase 定期举办的线下活动,与行业开发者互动交流

更多推荐