在 SQL Server 中按 ID 分组并保留数据最多的一行
实现方法
使用两层公用表达式(CTE)配合ROW_NUMBER()窗口函数实现,逻辑清晰,且天然支持全NULL分组返回一行的要求。
完整代码
假设表名为dbo.YourTable,按实际列名补全中间省略的27个数据列即可:
WITH RowWithNonNullCount AS ( SELECT *, -- 统计当前行30个数据列的非NULL数量 ( CASE WHEN [Data A] IS NOT NULL THEN 1 ELSE 0 END + CASE WHEN [Data B] IS NOT NULL THEN 1 ELSE 0 END + -- 此处依次补全 Data C ~ Data29 的判断 CASE WHEN [Data30] IS NOT NULL THEN 1 ELSE 0 END ) AS NonNullCount FROM dbo.YourTable ), RankedRows AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY NonNullCount DESC -- 若表有主键/自增列,取消下一行注释,保证并列时结果固定 -- , TablePrimaryKey ASC ) AS RowRank FROM RowWithNonNullCount ) -- 显式选择业务列,排除计算用的辅助列 SELECT ID, City, Country, [Data A], [Data B], -- 此处依次补全 Data C ~ Data29 [Data30] FROM RankedRows WHERE RowRank = 1;
逻辑说明
- 第一层CTE逐行计算30个数据列的非NULL总数,判断逻辑只需编写一次
ROW_NUMBER()通过PARTITION BY ID将同ID的行划为一组,组内按非NULL数量降序编号,非NULL列最多的行编号为1- 若同ID所有数据列全为NULL,所有行计数值均为0,窗口函数依然会生成从1开始的连续编号,不会丢失分组
- 最终过滤
RowRank = 1即可保证每个ID只返回一行
注意事项
- 列名含空格、特殊字符时,必须用SQL Server的方括号
[]包裹,比如示例中的[Data A] - 并列场景下如果需要固定返回规则(比如示例要求取第一行),建议追加表的主键、自增列作为第二排序条件;不要依赖默认物理顺序,索引重建、数据更新都可能导致默认顺序变化
- 如果业务上把空字符串
''也视为空值,把判断条件改为WHEN 列名 IS NOT NULL AND 列名 <> '' THEN 1即可 - 30个列必须显式写出,表结构固定的场景下这是性能最稳定、可读性最好的实现,不要为固定结构的表使用动态SQL
- 不推荐“先分组求最大非空数再回表关联”的写法:该方案遇到并列时会返回多行,且会产生额外表扫描,性能更差
内容的提问来源于stack exchange,提问作者SQLROOKIE
相关产品推荐
相关产品推荐

