DM8 中 BIGINT 字段 GROUP BY 返回重复分组,CAST 为 VARCHAR 后正常,请协助分析
以下表名、部分字段名和索引名已匿名化。
【DM版本】:
DM Database Server 64 V8 8.4 企业版 DB Version: 0x7000c 03134284194-20250707-281643-20108 Msg Version: 14 Gsu level(5) cnt: 0
查询工具:disql V8。
【操作系统】:麒麟。
【问题描述】*:
BIGINT 字段执行 GROUP BY 时,同一个 ID 被拆成两组;将分组表达式改成 VARCHAR 后结果正常。希望协助分析具体原因,以及是否有对应的修复或规避方案。
一、相关表结构说明
tree_node:通过 id、parent_id 存储节点层级关系。node_record_rel:通过 node_id 关联末级节点。del_flag=0 表示未删除。USER_TAB_COLUMNS 确认,tree_node.id、tree_node.parent_id 均为 BIGINT,DATA_SCALE=0。查询通过节点表的三次关联,按上级节点 ID 统计末级节点的关联记录数。
二、异常查询及实际结果
执行以下 SQL:
SELECT
l.parent_id AS node_id,
CAST(l.parent_id AS VARCHAR(100)) AS id_text,
LENGTH(CAST(l.parent_id AS VARCHAR(100))) AS id_length,
COUNT(r.id) AS record_count
FROM tree_node l
INNER JOIN tree_node s ON l.id = s.parent_id
INNER JOIN tree_node c ON s.id = c.parent_id
INNER JOIN node_record_rel r ON c.id = r.node_id
WHERE l.parent_id IN (
1066440524199625030,
1983446954328997890,
2115984052879773697
)
AND l.del_flag = 0
AND s.del_flag = 0
AND c.del_flag = 0
GROUP BY l.parent_id
ORDER BY l.parent_id;
实际返回四行:
node_id id_text id_length record_count ------------------- ------------------- --------- ------------ 1066440524199625030 1066440524199625030 19 3008 1983446954328997890 1983446954328997890 19 1708 2115984052879773697 2115984052879773697 19 87 1983446954328997890 1983446954328997890 19 32
异常表现:
1983446954328997890 在 GROUP BY 结果中出现两次,分别统计为 1708、32。ORDER BY l.parent_id,但最后一行没有按升序排列。三、已核对问题 ID 的类型及内部字节
问题父节点下有四个未删除的直接子节点。执行:
SELECT
l.id AS child_id,
l.parent_id,
DUMP(l.parent_id, 16) AS parent_id_dump
FROM tree_node l
WHERE l.parent_id = 1983446954328997890
AND l.del_flag = 0
ORDER BY l.id;
结果:
child_id parent_id parent_id_dump ------------------- ------------------- ---------------------------------- 1983446954379329536 1983446954328997890 Typ=8 Len=8: 2,40,9c,7,7f,9e,86,1b 1983446954391912448 1983446954328997890 Typ=8 Len=8: 2,40,9c,7,7f,9e,86,1b 1983446954400301056 1983446954328997890 Typ=8 Len=8: 2,40,9c,7,7f,9e,86,1b 1983446954404495360 1983446954328997890 Typ=8 Len=8: 2,40,9c,7,7f,9e,86,1b
四个节点的父 ID 为 BIGINT,显示值及内部字节完全一致。
四、单独计数,不进行分组时返回 1740
执行:
SELECT COUNT(r.id) AS record_count
FROM tree_node l
INNER JOIN tree_node s ON l.id = s.parent_id
INNER JOIN tree_node c ON s.id = c.parent_id
INNER JOIN node_record_rel r ON c.id = r.node_id
WHERE l.parent_id = 1983446954328997890
AND l.del_flag = 0
AND s.del_flag = 0
AND c.del_flag = 0;
返回:
record_count ------------ 1740
正好等于异常分组结果中的:
1708 + 32 = 1740
五、两层聚合且不带 ORDER BY 时,同样出现重复分组
应用原查询采用“内层 COUNT 并分组、外层 SUM 再分组”的结构,不带 ORDER BY:
SELECT
parent_id AS node_id,
SUM(record_count) AS record_count
FROM (
SELECT
l.parent_id,
COUNT(r.id) AS record_count
FROM tree_node l
INNER JOIN tree_node s
ON l.id = s.parent_id AND l.del_flag = 0
INNER JOIN tree_node c
ON s.id = c.parent_id AND c.del_flag = 0
INNER JOIN node_record_rel r
ON c.id = r.node_id
WHERE l.parent_id IN (
1066440524199625030,
1983446954328997890,
2115984052879773697
)
AND l.del_flag = 0
AND s.del_flag = 0
AND c.del_flag = 0
GROUP BY l.parent_id
) a
GROUP BY parent_id;
仍返回四行:
node_id record_count ------------------- ------------ 1066440524199625030 3008 1983446954328997890 1708 2115984052879773697 87 1983446954328997890 32
六、改为字符串表达式分组后正常
保持关联及过滤条件不变,执行以下 SQL:
SELECT
CAST(l.parent_id AS VARCHAR(100)) AS node_id_text,
COUNT(r.id) AS record_count
FROM tree_node l
INNER JOIN tree_node s ON l.id = s.parent_id
INNER JOIN tree_node c ON s.id = c.parent_id
INNER JOIN node_record_rel r ON c.id = r.node_id
WHERE l.parent_id IN (
1066440524199625030,
1983446954328997890,
2115984052879773697
)
AND l.del_flag = 0
AND s.del_flag = 0
AND c.del_flag = 0
GROUP BY CAST(l.parent_id AS VARCHAR(100))
ORDER BY CAST(l.parent_id AS VARCHAR(100));
正常返回三行:
node_id_text record_count ------------------- ------------ 1066440524199625030 3008 1983446954328997890 1740 2115984052879773697 87
七、异常查询的执行计划
以下为第二部分中单层 BIGINT 分组查询的执行计划,对象名称已对应匿名化:
1 #NSET2: [15, 6, 208] 2 #PRJT2: [15, 6, 208]; exp_num(4), is_atom(FALSE) 3 #SAGR2: [14, 6, 208]; grp_num(1), sfun_num(1), distinct_flag[0]; slave_empty(0) keys(l.parent_id) 4 #SORT3: [14, 6, 208]; key_num(1), partition_key_num(1), is_distinct(FALSE), top_flag(0), is_adaptive(1) 5 #HASH2 INNER JOIN: [13, 660, 208]; KEY_NUM(1); KEY(c.id=r.node_id) KEY_NULL_EQU(0) 6 #NEST LOOP INDEX JOIN2: [13, 660, 208] 7 #ACTRL: [13, 660, 208] 8 #SLCT2: [11, 330, 192]; exp_cast(c.del_flag) = 0 9 #HASH2 INNER JOIN: [11, 330, 192]; KEY_NUM(1); KEY(s.id=c.parent_id) KEY_NULL_EQU(0) 10 #SLCT2: [11, 330, 192]; exp_cast(c.del_flag) = 0 11 #NEST LOOP INDEX JOIN2: [11, 330, 192] 12 #ACTRL: [11, 330, 192] 13 #HASH2 INNER JOIN: [10, 165, 128]; KEY_NUM(1); KEY(s.parent_id=l.id) KEY_NULL_EQU(0) 14 #SLCT2: [5, 2203, 64]; exp_cast(s.del_flag) = 0 SLCT_PUSHDOWN(TRUE) 15 #CSCN2: [5, 44062, 64]; INDEX33560418(tree_node as s) NEED_SLCT(TRUE); btr_scan(1) 16 #SLCT2: [3, 3304, 64]; exp_cast(l.del_flag) = 0 17 #NEST LOOP INDEX JOIN2: [3, 3304, 64] 18 #CONST VALUE LIST: [1, 3, 8]; row_num(3), col_num(1) 19 #BLKUP2: [3, 1101, 64]; idx_parent_id(l) 20 #SSEK2: [3, 1101, 64]; scan_type(ASC), idx_parent_id(tree_node as l), scan_range[DMTEMPVIEW_895658659.colname,DMTEMPVIEW_895658659.colname], is_global(0) 21 #BLKUP2: [1, 2, 8]; idx_parent_id(c) 22 #SSEK2: [1, 2, 8]; scan_type(ASC), idx_parent_id(tree_node as c), scan_range[s.id,s.id], is_global(0) 23 #CSCN2: [5, 44062, 64]; INDEX33560418(tree_node as c); btr_scan(1) 24 #BLKUP2: [2, 2, 8]; idx_node_id(r) 25 #SSEK2: [2, 2, 8]; scan_type(ASC), idx_node_id(node_record_rel as r), scan_range[c.id,c.id], is_global(0) 26 #CSCN2: [4, 45720, 16]; INDEX33560903(node_record_rel as r); btr_scan(1)
关键路径为:
先 SORT3,再 SAGR2 分组
八、正常字符串分组查询的执行计划
以下为第六部分查询的执行计划,对象名称已对应匿名化:
1 #NSET2: [15, 6, 208] 2 #PRJT2: [15, 6, 208]; exp_num(2), is_atom(FALSE) 3 #SORT3: [15, 6, 208]; key_num(1), partition_key_num(0), is_distinct(FALSE), top_flag(0), is_adaptive(0) 4 #HAGR2: [14, 6, 208]; grp_num(1), sfun_num(1), distinct_flag[0]; slave_empty(0) keys(DMTEMPVIEW_895659480.TMPCOL0) 5 #PRJT2: [13, 660, 208]; exp_num(2), is_atom(FALSE) 6 #HASH2 INNER JOIN: [13, 660, 208]; KEY_NUM(1); KEY(c.id=r.node_id) KEY_NULL_EQU(0) 7 #NEST LOOP INDEX JOIN2: [13, 660, 208] 8 #ACTRL: [13, 660, 208] 9 #SLCT2: [11, 330, 192]; exp_cast(c.del_flag) = 0 10 #HASH2 INNER JOIN: [11, 330, 192]; KEY_NUM(1); KEY(s.id=c.parent_id) KEY_NULL_EQU(0) 11 #SLCT2: [11, 330, 192]; exp_cast(c.del_flag) = 0 12 #NEST LOOP INDEX JOIN2: [11, 330, 192] 13 #ACTRL: [11, 330, 192] 14 #HASH2 INNER JOIN: [10, 165, 128]; KEY_NUM(1); KEY(s.parent_id=l.id) KEY_NULL_EQU(0) 15 #SLCT2: [5, 2203, 64]; exp_cast(s.del_flag) = 0 SLCT_PUSHDOWN(TRUE) 16 #CSCN2: [5, 44062, 64]; INDEX33560418(tree_node as s) NEED_SLCT(TRUE); btr_scan(1) 17 #SLCT2: [3, 3304, 64]; exp_cast(l.del_flag) = 0 18 #NEST LOOP INDEX JOIN2: [3, 3304, 64] 19 #CONST VALUE LIST: [1, 3, 8]; row_num(3), col_num(1) 20 #BLKUP2: [3, 1101, 64]; idx_parent_id(l) 21 #SSEK2: [3, 1101, 64]; scan_type(ASC), idx_parent_id(tree_node as l), scan_range[DMTEMPVIEW_895659482.colname,DMTEMPVIEW_895659482.colname], is_global(0) 22 #BLKUP2: [1, 2, 8]; idx_parent_id(c) 23 #SSEK2: [1, 2, 8]; scan_type(ASC), idx_parent_id(tree_node as c), scan_range[s.id,s.id], is_global(0) 24 #CSCN2: [5, 44062, 64]; INDEX33560418(tree_node as c); btr_scan(1) 25 #BLKUP2: [2, 2, 8]; idx_node_id(r) 26 #SSEK2: [2, 2, 8]; scan_type(ASC), idx_node_id(node_record_rel as r), scan_range[c.id,c.id], is_global(0) 27 #CSCN2: [4, 45720, 16]; INDEX33560903(node_record_rel as r); btr_scan(1)
关键路径为:
先 HAGR2 分组,再 SORT3 排序
两份计划仍使用 idx_parent_id、idx_node_id 等相同索引访问路径。
九、其他排查及尝试
查询到的相关缓存计划均为:
BINDED=N RS_CAN_CACHED=N
在异常查询中增加以下 HINT,结果仍返回四行:
/*+ SORT_ADAPTIVE_FLAG(0) */
/*+ SORT_FLAG(0) SORT_ADAPTIVE_FLAG(0) */
V$HINT_INI_INFO 显示这两个参数的 HINT_TYPE 均为 EXEC。
增加以下 HINT 后,结果仍为四行:
/*+ FULL(l) FULL(s) FULL(c) FULL(r) */
但 EXPLAIN 中仍然使用原二级索引,因此这次尝试没有形成有效的访问路径对照,尚不能据此排除索引访问路径相关问题。
排查期间未修改业务数据、重建索引或调整全局参数。
目前只确认相同 BIGINT 分组键出现重复分组,以及改变分组表达式后结果正常,尚未确定具体内部原因。谢谢。

有没有尝试过,sp_set_para_value(1,'USE_HAGR_FLAG',1);然后再执行?另外麻烦提供下select * from v$dm_ini where para_name = 'GROUP_OPT_FLAG'的结果。