为提高效率,提问时请提供以下信息,问题描述清晰可优先响应。
【DM版本】:
DM Database Server 64 V8
DB Version: 0x7000d
03134284552-20260414-322369-20221
Msg Version: 1
Gsu level(5) cnt: 102
DM 管理工具
【操作系统】:
Windows 11 64 位
【CPU】:
Intel x86_64
【问题描述】*:
在 DM8 中,视图或 CTE 的查询结果包含 DENSE_RANK 窗口函数时,对其执行 GROUP BY ROLLUP,两个完全相同的空字符串会被错误拆分为两个不同分组。
将相同的查询结果通过 CTAS 物化到 ON COMMIT PRESERVE ROWS 全局临时表后,再执行相同的 GROUP BY ROLLUP,两个空字符串可以正确合并为一个分组。
结果对比如下:
视图: 4行,相同空字符串被拆分为两个分组
CTE: 4行,相同空字符串被拆分为两个分组
CTAS临时表:3行,相同空字符串正确合并
复现脚本
CREATE SCHEMA database0;
SET SCHEMA database0;
drop table if EXISTS T_DM_14;
drop view if EXISTS V_DM_14;
drop table if EXISTS TEMP_DM_14;
drop table if EXISTS TEMPOCPR_DM_14;
CREATE TABLE T_DM_14 (C0 VARCHAR2(16), C1 DATETIME, C2 TIME);
INSERT INTO T_DM_14 (C0, C1, C2) VALUES ('', TIMESTAMP '2000-02-29 23:59:59', NULL);
INSERT INTO T_DM_14 (C0, C1, C2) VALUES (' ', TIMESTAMP '1986-05-23 09:06:38', TIME '08:27:25');
INSERT INTO T_DM_14 (C0, C1, C2) VALUES ('', TIMESTAMP '1975-12-24 16:31:56', TIME '11:12:55');
CREATE VIEW V_DM_14 (VC_0, VC_1) AS SELECT C0 AS VC_0, DENSE_RANK() OVER (ORDER BY C0 ASC NULLS LAST, C1 ASC NULLS LAST, C2 ASC NULLS LAST) AS VC_1 FROM T_DM_14;
SELECT VC_0, COUNT(*), SUM(CAST((VC_1) AS DECIMAL(38,12))), AVG(CAST((VC_1) AS DECIMAL(38,12))) FROM V_DM_14 GROUP BY ROLLUP(VC_0);
-- row = 4
WITH CTE AS (SELECT C0 AS VC_0, DENSE_RANK() OVER (ORDER BY C0 ASC NULLS LAST, C1 ASC NULLS LAST, C2 ASC NULLS LAST) AS VC_1 FROM T_DM_14) SELECT VC_0, COUNT(*), SUM(CAST((VC_1) AS DECIMAL(38,12))), AVG(CAST((VC_1) AS DECIMAL(38,12))) FROM CTE GROUP BY ROLLUP(VC_0);
-- row = 4
CREATE GLOBAL TEMPORARY TABLE TEMP_DM_14 ON COMMIT PRESERVE ROWS AS SELECT C0 AS VC_0, DENSE_RANK() OVER (ORDER BY C0 ASC NULLS LAST, C1 ASC NULLS LAST, C2 ASC NULLS LAST) AS VC_1 FROM T_DM_14;
SELECT VC_0, COUNT(*), SUM(CAST((VC_1) AS DECIMAL(38,12))), AVG(CAST((VC_1) AS DECIMAL(38,12))) FROM TEMP_DM_14 GROUP BY ROLLUP(VC_0);
-- row = 3
当 blank_pad_mode=0 且 space_compare_mode=0 时,排序比较会忽略字符串末尾空格,因此将空字符串 '' 和单空格 ' ' 视为相同;但分组比较默认考虑末尾空格,会将二者视为不同值。
原始数据为:
'' ' ' ''
执行 DENSE_RANK() 时,由于排序认为三个 C0 值相同,会继续按照 C1、C2 排序,结果仍为:
'' ' ' ''
如果后续 GROUP BY 复用了该排序结果,分组操作会按照自身规则将其切分为:
第1组:'' 第2组:' ' 第3组:''
分组操作在复用前面的排序结果时,通常认为相同分组值应当连续出现。虽然第1组和第3组的值都是空字符串,但由于它们被单空格记录隔开,排序分组操作不会再返回前面查找已有分组,而是将其识别为两个独立的分组片段。于是会依次将“空字符串、单空格、空字符串”切分,因此形成 3个明细分组;ROLLUP 再增加 1条总计记录,最终返回 4行。
使用临时表时,窗口函数的查询结果会先写入临时表,之后再执行独立的 GROUP BY 查询。临时表物化切断了窗口排序与分组之间的直接执行关系,后续分组不会继续复用前面的排序顺序,而是重新进行分组处理。
此时两条空字符串记录能够被合并为同一个分组:
空字符串组:2条 单空格组:1条 ROLLUP总计:1条
因此临时表查询返回 3行。
优先统一排序和分组的空格比较策略:
blank_pad_mode=1 或 space_compare_mode=1,使排序也考虑末尾空格。group_opt_flag=4096,使分组也忽略末尾空格。返回4行的根本原因是排序和分组对末尾空格采用了不同的比较规则,导致相同的空字符串未连续排列,并被分组操作拆成了两个分组片段。

感谢您的反馈,我们会跟进分析。