SQL中如何按分组取TOP1?替代多次TOP与WHERE查询操作
按分组各取1条记录的高效SQL实现方法
针对你遇到的问题——原查询因A组符合条件数据过多,导致TOP26结果全为A组数据,现需为A-Z每个group各取1条code='11'的记录,以下是两种高效实现方案:
方案一:使用ROW_NUMBER()窗口函数(推荐)
这是最通用且性能最优的方案,只需一次表扫描即可完成分组取数,还能灵活指定每组取哪条记录。
SELECT [date], [group], [code], [pol], [relation] FROM ( SELECT [date], [group], [code], [pol], [relation], -- 按group分组,每组内按date降序取最新记录(可改为ASC取最早) ROW_NUMBER() OVER (PARTITION BY [group] ORDER BY [date] DESC) AS rn FROM [database].[table1] WHERE [group] BETWEEN 'A' AND 'Z' -- 替代IN列表,代码更简洁 AND code = '11' ) AS ranked_data WHERE rn = 1;
优势:
- 仅需一次表扫描,性能高效,避免多次查询的开销
- 通过
ORDER BY可精准控制每组返回的记录(如最新、最早或其他规则) - 用
BETWEEN 'A' AND 'Z'替代冗长的IN列表,维护更方便
方案二:使用CROSS APPLY(适用于SQL Server)
若你更偏好直观的分组关联逻辑,且group数量较少(这里仅26个),可采用此方案:
SELECT t.[date], t.[group], t.[code], t.[pol], t.[relation] FROM ( -- 先获取所有符合条件的group SELECT DISTINCT [group] FROM [database].[table1] WHERE [group] BETWEEN 'A' AND 'Z' AND code = '11' ) AS target_groups CROSS APPLY ( -- 对每个group取TOP1记录 SELECT TOP 1 [date], [code], [pol], [relation] FROM [database].[table1] WHERE [group] = target_groups.[group] AND code = '11' ORDER BY [date] DESC -- 同样可自定义排序规则 ) AS t;
性能优化建议:
- 为
[group]和code创建联合索引(如CREATE INDEX IX_table1_group_code_date ON [database].[table1] ([group], code, [date]);),可避免全表扫描,大幅提升查询速度 - 若无需指定排序规则,可将
ORDER BY改为ORDER BY (SELECT NULL),但此方式返回的记录是随机的,不建议用于需要稳定结果的场景
内容的提问来源于stack exchange,提问作者Jonathan Budez
相关产品推荐
相关产品推荐

