sql server中distinct与top不能直接共用,需先去重再取前10,且必须显式order by以保证结果确定性;推荐用cte或子查询实现,并依据业务逻辑明确“重复”定义及排序列索引。

用 DISTINCT + TOP 10 会出错,别这么写
直接写 SELECT TOP 10 DISTINCT col1, col2 FROM t 是语法错误。SQL Server 不允许 TOP 和 DISTINCT 在同一级 SELECT 中混用——解析器会报错“Incorrect syntax near the keyword 'DISTINCT'”。这不是版本问题,是语法规则限制。
正确做法:先去重,再取前10(用 CTE 或子查询)
核心思路是把去重逻辑放在内层,外层再套 TOP 10。必须显式指定排序依据,否则 TOP 行为不可控(尤其在有并行计划时可能每次结果不同)。
SELECT TOP 10 * FROM (SELECT DISTINCT col1, col2 FROM t) AS dedup ORDER BY col1;- 更推荐用 CTE,可读性好、便于后续扩展:
WITH dedup AS ( SELECT DISTINCT col1, col2 FROM t ) SELECT TOP 10 * FROM dedup ORDER BY col1;
- 如果去重依据是多个字段,但只按其中一列排序,没问题;但若想按“首次出现顺序”排,SQL Server 没有隐式行序概念,必须依赖某个确定列(如
id或created_at)
按业务含义去重(如保留最新一条),不能只靠 DISTINCT
当“重复”指多行语义相同但时间戳不同(例如同产品多次下单),DISTINCT 只能剔除完全相同的行,无法保证留的是最新那条。这时得用窗口函数:
-
ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY order_date DESC)给每组最新记录标 1 - 再外层筛选
WHERE rn = 1,最后TOP 10——注意ORDER BY必须写在最终 SELECT,否则TOP无意义 - 示例:
WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY order_date DESC) AS rn FROM sales ) SELECT TOP 10 product_id, order_date FROM ranked WHERE rn = 1 ORDER BY order_date DESC;
性能提醒:DISTINCT 本身不走索引,大表慎用
DISTINCT 底层等价于 GROUP BY,会触发 Sort 或 Hash Match 运算符,内存和 CPU 开销明显。如果字段上有现成的非聚集索引(比如 CREATE INDEX IX_t_col1_col2 ON t(col1, col2)),优化器可能直接索引扫描+流聚合,避免排序;否则容易成为瓶颈。
真正难的不是写出语法,而是想清楚“重复”的定义是否和业务一致,以及 ORDER BY 列有没有索引支撑——这两点漏掉,TOP 10 很可能查得慢还返回错数据。










