MSSQL分组取Top-N:如何快速获取各品类Top10高价商品?
高效获取MSSQL中每个类别Top10最贵商品的方案
嘿,针对你手里1万到100万条商品数据的场景,要快速揪出每个类别下最贵的10件商品,我得给你推荐几个经过实战验证的高效方法——毕竟数据量不小,慢查询可是会拖垮系统的!
最优方案:窗口函数ROW_NUMBER() + 覆盖索引
这绝对是大数据量下的首选,MSSQL对窗口函数的优化做得非常到位,再配上合适的索引,速度快到飞起。
第一步:先建关键索引(敲黑板!这是性能提升的核心)
首先给你的商品表创建一个覆盖索引,让数据库不用回表就能拿到所有需要的数据:
CREATE NONCLUSTERED INDEX IX_Product_Category_Price ON dbo.Product (category ASC, price DESC) INCLUDE (product_id, name); -- 把需要返回的字段都包含进来,避免额外的键查找
这个索引是按category分组,同类别下按price降序排列,数据库可以直接顺着索引读取每个类别的前10条数据,完全不需要额外排序。
第二步:写查询语句
用CTE(公共表表达式)结合ROW_NUMBER()窗口函数,逻辑清晰又高效:
WITH RankedProducts AS ( SELECT product_id, name, category, price, -- 按类别分组,价格降序排名,每个类别里的商品从1开始编号 ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) AS PriceRank FROM dbo.Product ) SELECT product_id, name, category, price FROM RankedProducts WHERE PriceRank <= 10 -- 只取每个类别前10名 ORDER BY category, price DESC;
为什么这个方法快?因为索引已经帮我们把数据排好序了,查询时数据库只需要扫描一次索引,就能直接筛选出符合条件的记录,时间复杂度接近O(n),100万条数据也能秒出结果。
兼容旧版本的备选方案:关联子查询
如果你还在使用MSSQL 2008及更早的版本(不过现在应该很少了),可以用关联子查询的方式实现,但性能会比窗口函数差不少,尤其是数据量越大差距越明显:
SELECT p1.product_id, p1.name, p1.category, p1.price FROM dbo.Product p1 WHERE 10 > ( -- 统计当前类别中价格比当前商品高的数量,小于10说明是前10 SELECT COUNT(*) FROM dbo.Product p2 WHERE p2.category = p1.category AND p2.price > p1.price ) ORDER BY p1.category, p1.price DESC;
这个方法的问题在于,每条记录都要执行一次子查询,数据量到100万时,查询时间会显著增加,所以优先选窗口函数方案。
性能小贴士
- 定期更新表的统计信息:
UPDATE STATISTICS dbo.Product;,让查询优化器能生成最优的执行计划。 - 如果你的表经常有插入、更新操作,可以考虑给索引设置适当的填充因子,避免频繁的页拆分。
- 测试时可以查看执行计划,确认查询是否用到了我们创建的覆盖索引——如果看到“索引扫描”而不是“索引查找”,那就要检查索引是否正确了。
内容的提问来源于stack exchange,提问作者Mr. 13
相关产品推荐
相关产品推荐

