如何在聚合时遇到SQL Server以返回第一个值(任何一个,我不在乎,它只需要快速)?
例如,假设我有:
ID Group
1 A
2 A
3 A
4 B
5 B
我需要为每个组获取任何一个ID。我可以这样做:
Select
max(id)
,group
from Table
group by group
返回
ID Group
3 A
5 B
这就完成了这项工作,但是当我要求SQL Server计算最高ID时,我真的需要做的就是选择它遇到的第一个ID,这似乎很愚蠢。
由于
PS - 字段被编入索引,所以它可能没有什么区别吗?
答案 0 :(得分:5)
有一个名为ANY
的{{3}},它不是有效的语法,但可以出现在执行计划中。但是,这并没有提供任何性能优势。
假设有以下表格和索引结构
CREATE TABLE T
(
id int identity primary key,
[group] char(1)
)
CREATE NONCLUSTERED INDEX ix ON T([group])
INSERT INTO T
SELECT TOP 1000000 CHAR( 65 + ROW_NUMBER() OVER (ORDER BY @@SPID) % 3)
FROM sys.all_objects o1, sys.all_objects o2, sys.all_objects o3
我还填充了样本数据,每组有很多行。
您的原始查询
SELECT MAX(id),
[group]
FROM T
GROUP BY [group]
提供Table 'T'. Scan count 1, logical reads 1367
和计划
|--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([Expr1003]=MAX([[T].[id])))
|--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)
重写以获得ANY
聚合...
;WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [group] ORDER BY [group] ) AS RN
FROM T)
SELECT id,
[group]
FROM cte
WHERE RN=1
提供Table 'T'. Scan count 1, logical reads 1367
和计划
|--Stream Aggregate(GROUP BY:([[T].[group]) DEFINE:([[T].[id]=ANY([[T].[id])))
|--Index Scan(OBJECT:([[T].[ix]), ORDERED FORWARD)
即使潜在的SQL Server可能会在找到第一个值后立即停止处理该组,但跳到下一个值却不会。它仍然处理所有行,逻辑读取是相同的。
对于此组中包含许多行的特定示例,更高效的版本将是递归CTE。
WITH RecursiveCTE
AS (
SELECT TOP 1 id, [group]
FROM T
ORDER BY [group]
UNION ALL
SELECT R.id, R.[group]
FROM (
SELECT T.*,
rn = ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM T
JOIN RecursiveCTE R
ON R.[group] < T.[group]
) R
WHERE R.rn = 1
)
SELECT *
FROM RecursiveCTE
OPTION (MAXRECURSION 0);
哪个给出了
Table 'Worktable'. Scan count 2, logical reads 19
Table 'T'. Scan count 4, logical reads 12
逻辑读取要少得多,因为它会检索每个组的第一行,然后搜索到下一个组,而不是读取对最终结果没有贡献的大量记录。