MSSQL 2008R2中UNION存在与否导致同查询返回不同值问题咨询
问题根因
核心原因是对UNION的去重逻辑认知有误,加上第一个CTE子句选的列太少,导致大量有效数据被错误去重,最终汇总值偏小:
UNION的实际执行逻辑是:将两个子查询的结果集拼接后,对全量数据做去重——只要两行数据的SELECT列表所有列值完全相同,不管这两行是来自同一个子查询还是两个不同子查询,都会被判定为重复,仅保留1行。这和UNION ALL直接保留所有行、不做去重的逻辑有本质区别。- 第一个查询里查CED数据的分支,只选了
src和Costo两个列:其中src是固定常量'CED',相当于所有行的第一列值完全一样,去重判定就只看Costo的值是否相等。
而DM_VRECostPerCid_CED视图本身是按CodAzi、CID、CdC、CoGe等多个业务维度分组算出来的结果,不同维度分组下的成本值Costo重复是非常常见的情况(比如多个部门的小额办公成本金额相同、大量0值调整项等)。这些业务上完全独立的有效数据,就因为只选了两个列,被UNION判定为重复行大量删除,最终sum出来的结果比真实值小近40%完全符合这个现象。 - 第二个查询结果正确也很好解释:
- 这个查询里
UNION关联ADJ表的代码全被注释了,本质就是单查视图,根本不会触发UNION的去重逻辑; - 同时这个查询的SELECT列表带了
CodAzi、CID、CdC、CoGe等所有分组维度列,这些列的组合在视图输出里是全局唯一的,就算Costo值一样,整行内容也不会重复,自然不会丢数据。
- 这个查询里
验证方法
把第一个查询里的UNION改成UNION ALL再执行,CED对应的汇总值会和第二个查询的结果完全一致:
declare @datCed DATE = '2022-01-31' ;with t as ( SELECT 'CED' AS src, Costo FROM DM_VRECostPerCid_CED WHERE DatCed = @datCed UNION ALL SELECT 'ADJ' AS src, Importo FROM dbo.DM_PY_Adjusted WHERE DatCed = @datCed ) select src, sum(costo) from t group by src
注意:执行计划里看到的"Concatenation(串联)"只是结果集拼接的步骤,不管是
UNION还是UNION ALL都会有这个操作。UNION在串联之后还会额外执行排序去重(Distinct Sort)的步骤,不要把串联当成UNION的全部执行逻辑。
注意事项
- 除非明确需要对合并后的结果去重,否则多结果集拼接一律优先用
UNION ALL,不仅性能更好(省去了排序去重的开销),也不会出现这种误删有效数据的问题; - 如果确实要使用
UNION,一定要保证SELECT列表里包含足够的唯一标识字段(比如业务主键、维度组合字段),避免不同业务数据因为查询列值重复被误去重。
内容的提问来源于stack exchange,提问作者Paolo Ursini
相关产品推荐
相关产品推荐

