DATA BRANCH DIFF

语法说明

DATA BRANCH DIFF 语句用于比较两个表之间的数据差异。该功能类似于 Git 的 diff 命令,可以显示两个数据分支之间的插入、删除和更新操作。

系统会自动识别两个表之间的最近公共祖先(LCA,Lowest Common Ancestor)。存在 LCA 时,按血缘计算以下增量差异:

  • INSERT:目标表中存在但基准表中不存在的行

  • DELETE:基准表中存在但目标表中不存在的行

  • UPDATE:两个表中主键相同但其他列值不同的行

如果两个表没有共同祖先,系统把两侧视为独立数据镜像:只存在于一侧的行,以及同一键在两侧值不同的行,都会以各自侧的 INSERT 输出。DELETEUPDATE 是有血缘场景的语义,不适用于无 LCA 的全量比较。

语法结构

DATA BRANCH DIFF target_table [{ SNAPSHOT = 'snapshot_name' }]
    AGAINST base_table [{ SNAPSHOT = 'snapshot_name' }]
    [COLUMNS ( col1 [, col2 ...] )]
    [OUTPUT output_option]

COLUMNS 投影(v3.0.10 起支持)

COLUMNS (col1, col2, ...) 将可见值列严格限定为显式请求的列。主键仍用于内部匹配和最终排序,但不会自动加入输出;只有显式列出时才会显示。列名按大小写不敏感匹配和去重,并保留第一次出现,因此 COLUMNS(name, NAME, name) 只输出一个 name 列。COLUMNS 不能与 OUTPUT FILE 组合。

输出选项

output_option:
    COUNT                           -- 仅返回差异行数
  | LIMIT number                    -- 限制返回的差异行数
  | FILE 'directory_path'           -- 将差异导出为 SQL 文件
  | AS table_name                   -- 将差异持久化到新的普通表
  | SUMMARY                         -- 返回聚合的 INSERT / DELETE / UPDATE 计数

语法释义

参数说明

参数

说明

target_table

目标表(要比较的表)

base_table

基准表(作为比较基准的表)

SNAPSHOT = 'snapshot_name'

可选参数,指定使用某个快照时刻的数据进行比较

OUTPUT COUNT

仅返回差异的行数统计

OUTPUT LIMIT number

按主键完成最终排序后,最多返回 number 行差异。0 合法并返回零行;超过 9223372036854775807 时返回 OUTPUT LIMIT is out of range

OUTPUT FILE 'path'

将差异导出为 SQL 文件到指定目录,支持本地路径或 Stage 路径(如 stage://stage_name/

OUTPUT AS table_name

新建普通表并持久化差异。结果表包含 __mo_diff_source__mo_diff_flag 和选定的值列;目标表必须不存在,不能指定目标快照,并要求目标数据库的 CREATE TABLE 权限。

OUTPUT SUMMARY

返回三行(INSERTEDDELETEDUPDATED),列为 metric、目标表计数、基准表计数,不返回逐行差异。

COLUMNS (col1, col2, ...)

v3.0.10 起支持。仅返回显式列出的值列,不自动包含主键;不能与 OUTPUT FILE 组合。

输出列说明

默认输出包含以下列:

列名

说明

diff target against base

显示比较的表名

flag

差异类型:INSERT、DELETE 或 UPDATE

其他列

表的所有可见列

OUTPUT AS

OUTPUT AS table_name 将差异物化为持久化普通表。它不会创建数据分支,语句提交后其他会话也可以访问该表。输出保留所选值列的顺序和类型,并增加两个由差异操作填充的元数据列:

  • __mo_diff_source:该差异行对应的来源表名。

  • __mo_diff_flagINSERTDELETEUPDATE

如果用户列使用了上述名称,MatrixOne 会自动选择带后缀的安全名称以避免冲突。COLUMNS (...) 以与结果集差异相同的方式控制可见值列,主键列只有在显式请求时才会加入。目标表名可以带数据库名,不能已存在,也不能是快照;语句会执行普通 CREATE TABLE 权限检查。如果 schema 校验或建表失败,部分创建的输出表会被原子清理。

DROP DATABASE IF EXISTS diff_output_demo;
CREATE DATABASE diff_output_demo;
USE diff_output_demo;
CREATE TABLE base (id INT PRIMARY KEY, value VARCHAR(20));
INSERT INTO base VALUES (1, 'old');
CREATE TABLE target (id INT PRIMARY KEY, value VARCHAR(20));
INSERT INTO target VALUES (1, 'new'), (2, 'added');
DATA BRANCH DIFF target AGAINST base COLUMNS (value) OUTPUT AS diff_rows;
SHOW COLUMNS FROM diff_rows;
SELECT __mo_diff_flag, __mo_diff_source, id, value FROM diff_rows ORDER BY id;
DROP TABLE diff_rows;
DROP TABLE target;
DROP TABLE base;
DROP DATABASE diff_output_demo;

使用说明

LCA(最近公共祖先)

系统会自动检测两个表之间的分支关系:

  1. 无 LCA:两个表没有共同的祖先,直接比较所有数据

  2. 有 LCA:两个表有共同的祖先,基于祖先计算增量差异

  3. 自身为 LCA:一个表是另一个表的祖先

无 LCA 时,两侧独有行以及同一键的不同镜像都标记为对应侧的 INSERT;存在 LCA 时,INSERTDELETEUPDATE 表示相对共同祖先的变更。

持久化与排序

  • OUTPUT AS 将结果物化为普通表。目标表已存在、目标带快照选项或调用者缺少目标数据库的 CREATE TABLE 权限时会失败。

  • 持久化表使用 __mo_diff_source__mo_diff_flag 保存来源与变更类型。

  • OUTPUT AS 参与显式事务:COMMIT 会同时持久化表和差异数据,ROLLBACK 后表与数据都不存在;空差异在提交后仍会创建空表。

  • 如果源列已经使用 __mo_diff_source__mo_diff_flag,生成的元数据列会采用可用的数字后缀(例如 __mo_diff_source_1),源列名保持不变。快照比较会物化所选历史端点可见的 schema;历史映射不兼容时会在创建目标表前失败。

  • OUTPUT LIMIT N 在按主键完成最终排序后截取前 N 行,不使用内部存储顺序。

  • OUTPUT SUMMARY 返回 metric<target_table><base_table> 三列,以及 INSERTEDDELETEDUPDATED 三行,不返回行镜像。

LCA 解析和变更范围计算支持任意 DAG 深度的分支,不仅限于直接的父子关系。多叉树中具有复杂分支历史的结构可以在任意节点对(兄弟节点、表兄弟节点、跨子树节点)之间产生正确的差异。

支持的表类型

  • 带主键的表(推荐)

  • 复合主键的表

  • 无主键的表(使用隐藏的 fake primary key)

示例

示例 1:基本差异比较

比较两个没有共同祖先的表:

-- Expected-Rows: 0
CREATE DATABASE test;
-- Expected-Rows: 0
USE test;

-- Expected-Rows: 0
CREATE TABLE test.t1 (a INT PRIMARY KEY, b VARCHAR(10));
-- Expected-Rows: 0
INSERT INTO test.t1 VALUES (1, '1'), (2, '2'), (3, '3');

-- Expected-Rows: 0
CREATE TABLE test.t2 (a INT PRIMARY KEY, b VARCHAR(10));
-- Expected-Rows: 0
INSERT INTO test.t2 VALUES (1, '1'), (2, '2'), (4, '4');

-- Expected-Rows: 2
DATA BRANCH DIFF test.t2 AGAINST test.t1;
+-------------------+--------+------+------+
| diff t2 against t1 | flag   | a    | b    |
+-------------------+--------+------+------+
| t2                | INSERT |    4 | 4    |
| t1                | INSERT |    3 | 3    |
+-------------------+--------+------+------+

-- Expected-Rows: 2
DATA BRANCH DIFF test.t1 AGAINST test.t2;
+-------------------+--------+------+------+
| diff t1 against t2 | flag   | a    | b    |
+-------------------+--------+------+------+
| t1                | INSERT |    3 | 3    |
| t2                | INSERT |    4 | 4    |
+-------------------+--------+------+------+

-- Expected-Rows: 0
DROP TABLE test.t1;
-- Expected-Rows: 0
DROP TABLE test.t2;

示例 2:比较分支表(有共同祖先)

-- Expected-Rows: 0
CREATE TABLE test.t0 (a INT PRIMARY KEY, b INT);
-- Expected-Rows: 0
INSERT INTO test.t0 VALUES (1, 1), (2, 2), (3, 3);

-- 从 t0 创建两个分支
-- Expected-Rows: 0
DATA BRANCH CREATE TABLE test.t1 FROM test.t0;
-- Expected-Rows: 0
INSERT INTO test.t1 VALUES (4, 4);

-- Expected-Rows: 0
DATA BRANCH CREATE TABLE test.t2 FROM test.t0;
-- Expected-Rows: 0
INSERT INTO test.t2 VALUES (5, 5);

-- Expected-Rows: 2
DATA BRANCH DIFF test.t2 AGAINST test.t1;
+-------------------+--------+------+------+
| diff t2 against t1 | flag   | a    | b    |
+-------------------+--------+------+------+
| t2                | INSERT |    5 |    5 |
| t1                | INSERT |    4 |    4 |
+-------------------+--------+------+------+

-- Expected-Rows: 0
DROP TABLE test.t0;
-- Expected-Rows: 0
DROP TABLE test.t1;
-- Expected-Rows: 0
DROP TABLE test.t2;

示例 3:使用快照比较

-- Expected-Rows: 0
CREATE TABLE test.t1 (a INT PRIMARY KEY, b INT);
-- Expected-Rows: 0
INSERT INTO test.t1 VALUES (1, 1), (2, 2);

-- Expected-Rows: 0
CREATE SNAPSHOT sp1 FOR TABLE test t1;

-- Expected-Rows: 0
INSERT INTO test.t1 VALUES (3, 3);
-- Expected-Rows: 1
UPDATE test.t1 SET b = 10 WHERE a = 1;

-- Expected-Rows: 0
CREATE SNAPSHOT sp2 FOR TABLE test t1;

-- Expected-Rows: 1
DATA BRANCH DIFF test.t1{SNAPSHOT='sp2'} AGAINST test.t1{SNAPSHOT='sp1'};
+--------------------+--------+------+------+
| diff t1 against t1 | flag   | a    | b    |
+--------------------+--------+------+------+
| t1                 | INSERT |    3 |    3 |
+--------------------+--------+------+------+

-- Expected-Rows: 0
DROP SNAPSHOT sp1;
-- Expected-Rows: 0
DROP SNAPSHOT sp2;
-- Expected-Rows: 0
DROP TABLE test.t1;

备注

当比较同一张表的两个快照时,系统会基于快照之间的增量变化进行比较。在此示例中,虽然 UPDATE 操作修改了 a=1 的行,但由于快照比较的特殊机制,只显示了新增的行。

示例 4:仅获取差异数量

-- Expected-Rows: 0
CREATE TABLE test.t1 (a INT PRIMARY KEY, b INT);
-- Expected-Rows: 0
INSERT INTO test.t1 SELECT result, result FROM generate_series(1, 1000) g;

-- Expected-Rows: 0
DATA BRANCH CREATE TABLE test.t2 FROM test.t1;
-- Expected-Rows: 0
INSERT INTO test.t2 SELECT result, result FROM generate_series(1001, 2000) g;
-- Expected-Rows: 100
DELETE FROM test.t2 WHERE a <= 100;

-- Expected-Rows: 1
DATA BRANCH DIFF test.t2 AGAINST test.t1 OUTPUT COUNT;
+----------+
| COUNT(*) |
+----------+
|     1100 |
+----------+

-- Expected-Rows: 0
DROP TABLE test.t1;
-- Expected-Rows: 0
DROP TABLE test.t2;

示例 5:限制返回行数

-- Expected-Rows: 0
CREATE TABLE test.t1 (a INT PRIMARY KEY, b INT);
-- Expected-Rows: 0
INSERT INTO test.t1 SELECT result, result FROM generate_series(1, 100) g;

-- Expected-Rows: 0
DATA BRANCH CREATE TABLE test.t2 FROM test.t1;
-- Expected-Rows: 0
INSERT INTO test.t2 SELECT result, result FROM generate_series(101, 200) g;

-- Expected-Rows: 5
DATA BRANCH DIFF test.t2 AGAINST test.t1 OUTPUT LIMIT 5;
+--------------------+--------+------+------+
| diff t2 against t1 | flag   | a    | b    |
+--------------------+--------+------+------+
| t2                 | INSERT |  101 |  101 |
| t2                 | INSERT |  102 |  102 |
| t2                 | INSERT |  103 |  103 |
| t2                 | INSERT |  104 |  104 |
| t2                 | INSERT |  105 |  105 |
+--------------------+--------+------+------+

-- Expected-Rows: 0
DROP TABLE test.t1;
-- Expected-Rows: 0
DROP TABLE test.t2;

备注

OUTPUT LIMIT 返回按主键完成最终排序后的前 N 行差异。

示例 6:导出差异为 SQL 文件

-- Expected-Rows: 0
CREATE TABLE test.t1 (a INT PRIMARY KEY, b INT);
-- Expected-Rows: 0
INSERT INTO test.t1 VALUES (1, 1), (2, 2);

-- Expected-Rows: 0
DATA BRANCH CREATE TABLE test.t2 FROM test.t1;
-- Expected-Rows: 0
INSERT INTO test.t2 VALUES (3, 3);
-- Expected-Rows: 1
UPDATE test.t2 SET b = 10 WHERE a = 1;
-- Expected-Rows: 1
DELETE FROM test.t2 WHERE a = 2;

-- Expected-Rows: 1
DATA BRANCH DIFF test.t2 AGAINST test.t1 OUTPUT FILE '/tmp/';
+------------------------------------------+------------------------------------------+
| FILE SAVED TO                            | HINT                                     |
+------------------------------------------+------------------------------------------+
| /tmp/diff_t2_t1_<UTC-YYYYMMDD_HHMMSS>_<UUID>.sql | DELETE FROM `test`.`t1`, INSERT INTO `test`.`t1` |
+------------------------------------------+------------------------------------------+

-- Expected-Rows: 0
DROP TABLE test.t1;
-- Expected-Rows: 0
DROP TABLE test.t2;

上述路径是格式示意:MatrixOne 每次调用都会生成 UTC 时间戳和 UUID。v4.2 输出 .sql 补丁,其中包含所需的 DELETE FROM ...INSERT INTO ... 语句。

回放补丁文件

回放 SQL 文件(增量同步)

mysql -h <mo_host> -P <mo_port> -u <user> -p <db_name> < diff_t2_t1_<UTC-YYYYMMDD_HHMMSS>_<UUID>.sql

示例 6b:导出差异到 Stage(对象存储)

Stage 是 MatrixOne 用于连接外部存储(如 S3、HDFS)的逻辑对象,可以将差异文件直接输出到对象存储,便于跨集群/跨环境同步数据。

-- Expected-Rows: 0
CREATE STAGE my_stage URL = 's3://my-bucket/diff-output/?region=us-east-1&access_key_id=xxx&secret_access_key=yyy';

DATA BRANCH DIFF test.t2 AGAINST test.t1 OUTPUT FILE 'stage://my_stage/';
+-------------------------------------------------+------------------------------------------+
| FILE SAVED TO                                   | HINT                                     |
+-------------------------------------------------+------------------------------------------+
| stage://my_stage/diff_t2_t1_<UTC-YYYYMMDD_HHMMSS>_<UUID>.sql | DELETE FROM `test`.`t1`, INSERT INTO `test`.`t1` |
+-------------------------------------------------+------------------------------------------+

SELECT load_file(CAST('stage://my_stage/diff_t2_t1_<UTC-YYYYMMDD_HHMMSS>_<UUID>.sql' AS DATALINK));

-- Expected-Rows: 0
DROP STAGE my_stage;

使用 Stage 的优势:

  • 安全:无需在每次 SQL 中暴露 AK/SK,管理员配置一次即可

  • 便捷:将复杂的 URL 路径封装成简单的对象名

  • 跨集群同步:源端写到对象存储,目标端直接读取执行

示例 7:检测更新操作

-- Expected-Rows: 0
CREATE TABLE test.t0 (a INT PRIMARY KEY, b INT, c INT);
-- Expected-Rows: 0
INSERT INTO test.t0 SELECT result, result, result FROM generate_series(1, 100) g;

-- Expected-Rows: 0
DATA BRANCH CREATE TABLE test.t1 FROM test.t0;
-- Expected-Rows: 3
UPDATE test.t1 SET c = c + 1 WHERE a IN (1, 50, 100);

-- Expected-Rows: 0
DATA BRANCH CREATE TABLE test.t2 FROM test.t0;
-- Expected-Rows: 3
UPDATE test.t2 SET c = c + 2 WHERE a IN (1, 25, 75);

-- 比较差异,将检测到冲突的更新
-- Expected-Rows: 6
DATA BRANCH DIFF test.t2 AGAINST test.t1;
+--------------------+--------+------+------+------+
| diff t2 against t1 | flag   | a    | b    | c    |
+--------------------+--------+------+------+------+
| t2                 | UPDATE |    1 |    1 |    3 |
| t1                 | UPDATE |    1 |    1 |    2 |
| t2                 | UPDATE |   25 |   25 |   27 |
| t1                 | UPDATE |   50 |   50 |   51 |
| t2                 | UPDATE |   75 |   75 |   77 |
| t1                 | UPDATE |  100 |  100 |  101 |
+--------------------+--------+------+------+------+

-- Expected-Rows: 0
DROP TABLE test.t0;
-- Expected-Rows: 0
DROP TABLE test.t1;
-- Expected-Rows: 0
DROP TABLE test.t2;

示例 8:复合主键表的差异比较

-- Expected-Rows: 0
CREATE TABLE test.orders (
    tenant_id INT,
    order_code VARCHAR(8),
    amount DECIMAL(12,2),
    PRIMARY KEY (tenant_id, order_code)
);

-- Expected-Rows: 0
INSERT INTO test.orders VALUES
    (100, 'A100', 120.50),
    (100, 'A101', 80.00),
    (101, 'B200', 305.75);

-- Expected-Rows: 0
DATA BRANCH CREATE TABLE test.orders_branch FROM test.orders;

-- 在分支上进行修改
-- Expected-Rows: 1
UPDATE test.orders_branch SET amount = 130.50 WHERE tenant_id = 100 AND order_code = 'A100';
-- Expected-Rows: 1
DELETE FROM test.orders_branch WHERE tenant_id = 100 AND order_code = 'A101';
-- Expected-Rows: 0
INSERT INTO test.orders_branch VALUES (102, 'C300', 512.25);

-- 比较差异
-- Expected-Rows: 3
DATA BRANCH DIFF test.orders_branch AGAINST test.orders;
+-----------------------------------+--------+-----------+------------+--------+
| diff orders_branch against orders | flag   | tenant_id | order_code | amount |
+-----------------------------------+--------+-----------+------------+--------+
| orders_branch                     | UPDATE |       100 | A100       | 130.50 |
| orders_branch                     | DELETE |       100 | A101       |  80.00 |
| orders_branch                     | INSERT |       102 | C300       | 512.25 |
+-----------------------------------+--------+-----------+------------+--------+

-- Expected-Rows: 0
DROP TABLE test.orders;
-- Expected-Rows: 0
DROP TABLE test.orders_branch;
-- Expected-Rows: 0
DROP DATABASE test;

示例 9:COLUMNS 投影(v3.0.10)

COLUMNS (col1, ..., colN) 只返回显式请求的值列。主键仍用于内部匹配和排序,但仅在显式列出时出现在结果中。

DROP DATABASE IF EXISTS data_branch_diff_columns_demo;
CREATE DATABASE data_branch_diff_columns_demo;
USE data_branch_diff_columns_demo;

CREATE TABLE c1 (
    id INT PRIMARY KEY,
    name VARCHAR(30),
    balance DECIMAL(12,2),
    created_at TIMESTAMP,
    birthday DATE
);
INSERT INTO c1 VALUES
    (1, 'alice', 1000.50, '2024-01-01 10:00:00', '1990-03-15'),
    (2, 'bob',   2000.75, '2024-01-02 11:00:00', '1985-07-20'),
    (3, 'carol', 3000.00, '2024-01-03 12:00:00', '1992-11-08');

DATA BRANCH CREATE TABLE c1_br FROM c1;
UPDATE c1_br SET balance = 1500.50, name = 'alice_v2' WHERE id = 1;
DELETE FROM c1_br WHERE id = 2;
INSERT INTO c1_br VALUES (4, 'dave', 4000.00, '2024-02-01 09:00:00', '1988-12-25');

-- 全量 diff:所有列。
DATA BRANCH DIFF c1_br AGAINST c1;

-- 仅投影 name 列:可见结果不自动包含 id。
DATA BRANCH DIFF c1_br AGAINST c1 COLUMNS (name);

-- 投影两个非主键列。
DATA BRANCH DIFF c1_br AGAINST c1 COLUMNS (name, balance);

-- 需要显示主键时显式列出主键。
DATA BRANCH DIFF c1_br AGAINST c1 COLUMNS (id, balance);

-- COLUMNS 可以和 OUTPUT LIMIT 组合。
DATA BRANCH DIFF c1_br AGAINST c1 COLUMNS (balance) OUTPUT LIMIT 5;

DROP TABLE c1_br;
DROP TABLE c1;
DROP DATABASE data_branch_diff_columns_demo;

示例 10:线性链式差异(非相邻分支)

DATA BRANCH DIFF 支持跨非相邻层级的线性分支链(如 t0 -> t1 -> t2 -> t3)。当对 t3 与 t0 进行差异比较时,LCA 会通过遍历中间的克隆边解析为 t0。链式结构比扁平分叉触发了不同的 LCA 代码路径。中间的包装节点(t1、t2)对差异不可见:仅显示相关分支点之后 t0 和 t3 上的变更。

DROP DATABASE IF EXISTS test_chain_diff;
CREATE DATABASE test_chain_diff;
USE test_chain_diff;

CREATE TABLE t0(a INT PRIMARY KEY, b INT);
INSERT INTO t0 VALUES (1, 1), (2, 2), (3, 3);

DATA BRANCH CREATE TABLE t1 FROM t0;
DATA BRANCH CREATE TABLE t2 FROM t1;

-- 仅在叶子 t2 上写入
INSERT INTO t2 VALUES (4, 4), (5, 5);
UPDATE t2 SET b = b + 100 WHERE a = 1;
DELETE FROM t2 WHERE a = 2;

-- 非相邻差异:t2 与 t0(LCA = t0)
DATA BRANCH DIFF t2 AGAINST t0 OUTPUT SUMMARY;

-- 相邻差异:t2 与 t1(LCA = t1)
DATA BRANCH DIFF t2 AGAINST t1 OUTPUT SUMMARY;

-- 反向差异
DATA BRANCH DIFF t0 AGAINST t2 OUTPUT SUMMARY;

DROP TABLE t2;
DROP TABLE t1;
DROP TABLE t0;
DROP DATABASE test_chain_diff;

示例 11:多叉树差异

DATA BRANCH DIFF 支持复杂的多叉分支树(任意 DAG 深度)。LCA 解析覆盖兄弟叶子、表兄弟、跨子树对以及不同深度的节点对。每个差异对都测试树中结构上不同的横截面。下面是缩略伪代码,不是可直接执行的 SQL 脚本:其中有意省略了 t3t18 的创建。完整可执行构造见 MatrixOne 源码中的 test/distributed/cases/git4data/branch/diff/diff_13.sql

树形结构(4 层,19 个节点):

                    t0  (根节点)
               /    |    \\
              t1    t2    t3        (第 2 层)
            /  \\    |    /  \\
          t4   t5  t6  t7   t8      (第 3 层)
         / \\  / \\  / \\ / \\  / \\
       t9 t10 t11 t12 t13 t14 t15 t16 t17 t18  (第 4 层,10 个叶子)
DROP DATABASE IF EXISTS test_tree_diff;
CREATE DATABASE test_tree_diff;
USE test_tree_diff;

CREATE TABLE t0(a INT PRIMARY KEY, b INT);
INSERT INTO t0 SELECT *, * FROM generate_series(1, 20) g;

-- 自顶向下构建树,每个节点上执行 IUD
DATA BRANCH CREATE TABLE t1 FROM t0;
INSERT INTO t1 VALUES (101, 101);
UPDATE t1 SET b = 10000 WHERE a = 1;

DATA BRANCH CREATE TABLE t2 FROM t0;
INSERT INTO t2 VALUES (102, 102);
UPDATE t2 SET b = 20000 WHERE a = 2;

-- 伪代码:按 diff_13.sql 创建 t3、t4-t8 和叶子 t9-t18。

-- 叶子 vs 根节点:跨越 3 条边
DATA BRANCH DIFF t9 AGAINST t0 OUTPUT SUMMARY;

-- 叶子 vs 兄弟叶子:共享祖父节点
DATA BRANCH DIFF t9 AGAINST t10 OUTPUT SUMMARY;

-- 叶子 vs 表兄弟叶子:共享曾祖父节点
DATA BRANCH DIFF t9 AGAINST t11 OUTPUT SUMMARY;

-- 叶子 vs 跨子树表兄弟:对向子树
DATA BRANCH DIFF t9 AGAINST t13 OUTPUT SUMMARY;

-- 反向深度跨子树对
DATA BRANCH DIFF t18 AGAINST t9 OUTPUT SUMMARY;

DROP DATABASE test_tree_diff;

备注

完整的树构建和差异对见测试套件。关键特性:

  • 任意节点对(无论深度差异)的 LCA 解析均正确。

  • 反转方向时差异为镜像关系。

  • 快照作用域的链式差异在每侧回放到指定时间点。

  • DAG 遍历在任意深度(深度 3 及以上)保持稳定。

注意事项

  1. Schema evolution 兼容性:只有在列身份和主键语义仍可证明时,DIFF 才接受 schema evolution。以下矩阵同时适用于 DIFF 和 MERGE;对于 MERGE,源表的变更会合并到目标表。

    Schema 变更

    DIFF/MERGE 行为

    列身份和属性不变、仅调整列顺序

    支持

    MatrixOne 可以证明血缘关系的列重命名

    支持

    具有显式主键且定义兼容的目标/源独有可见列

    支持;DIFF 会包含该列,MERGE 只写入目标表也存在的列

    源表/基准表中的列在目标表中缺失

    拒绝

    同名列先 DROP 再 ADD(产生新身份)

    拒绝

    类型、可空性、生成列定义或主键不兼容

    拒绝

    使用隐藏 fake primary key 的表存在目标独有列

    拒绝

    OUTPUT AS 创建的是新的普通表,不参与上述两张输入表的 schema 兼容性检查。

  2. 主键要求:虽然支持无主键的表,但建议使用带主键的表以获得更准确的差异结果。

  3. 性能考虑:对于大表,差异比较可能需要较长时间。建议使用 OUTPUT COUNT 先了解差异规模。

  4. 快照有效性:使用快照比较时,确保快照存在且有效。

  5. 输出文件:使用本地路径时,只要服务进程有权限,MatrixOne 会通过 MkdirAll 创建缺失的父目录;最终路径必须可写。生成的 SQL 文件名包含 UTC 时间戳和 UUID,可用于回放同步。

  6. LCA 检测:系统会自动检测 LCA,无需手动指定。LCA 的存在会影响差异计算的方式。