SQL Server:每组加速max()

时间:2017-12-28 13:57:19

标签: sql sql-server performance

我尝试使用索引搜索表的max()查询中获取group by值。< / p>

我尝试了很多组合,但似乎无法说服SQL Server不进行索引扫描:

create table DataTable
(
    [key] int, --non-unique
    [value] int
)

大约有1亿行。 key只有大约20个左右的不同值。

我有一个想法,如果我能够以某种方式强制查询优化器生成一个嵌套循环,为max key解决问题...但我可以&#39;似乎这样做。

我已经尝试了所有这些索引:

CREATE NONCLUSTERED INDEX MyIndex 
ON DataTable ([key] ASC, [value] ASC)

CREATE NONCLUSTERED INDEX MyIndex 
ON DataTable ([key] ASC, [value] DESC)

CREATE NONCLUSTERED INDEX MyIndex 
ON DataTable ([key] ASC)
INCLUDE ([value])

尝试选择语句:

select key, max(value)
from DataTable with (index(MyIndex))
group by key

select distinct
    key,
    first_value(value) over (partition by key order by value desc)
from DataTable with (index(MyIndex))

select T.key, (select max(value) from DataTable where key = T.key)
from DataTable T
group by T.key

select distinct T.key, M.Maxvalue
from DataTable T
inner loop join (
    select key, Maxvalue = max(value)
    from DataTable 
    group by key
) M on M.key = T.key 


select *
from (
select 
    key,
    Maxvalue = first_value(value) over (partition by key order by value desc),
    rn  = row_number() over (partition by key order by value desc)
from DataTable with (index(MyIndex))
) x
where rn = 1

2 个答案:

答案 0 :(得分:0)

TL; DR:对于提供的确切查询,您无法做到。

原因是,为了找到特定value的最大key,SQL Server必须使用此密钥检查所有行。并且因为您的查询没有where部分并且查找所有键,所以必须触摸表中的所有行。为此,扫描比搜索更有效。

比较它们非常容易:您可以使用FORCESEEK表提示并比较生成的执行计划和时间/ io统计信息。

select key, max(value)
from DataTable with (index(MyIndex))
group by key;
go
select key, max(value)
from DataTable with (forceseek, index(MyIndex))
group by key;
go

如果/当您的查询实际上会收到where子句以便只需要搜索表的一部分时,查询优化器可能会在某个时候决定支持搜索过扫描。对于按键过滤,您的第二个索引似乎是理想的:

CREATE NONCLUSTERED INDEX MyIndex ON DataTable
(
    [key] ASC,
    [value] DESC
)

但请记住,它可能发生的阈值在很大程度上取决于您的数据基数,索引碎片,最新的统计信息,数据库引擎版本/版本,跟踪标记以及其他一些太深奥的东西并且奥术在这里提到它。我的意思是,不要指望它是某种常数。

答案 1 :(得分:0)

我同意@Roger Wolf及其解释。

从学习的角度来看,你的问题很好。

我尝试创建示例数据并获取Index Seek

create table DataTable
(
    [key] int, --non-unique
    [value] int
)

insert into DataTable WITH (TABLOCK)
([key],value )
select rn/500000 ,rn/100
from
(
select top (10000000) ROW_NUMBER()over(order by a.number)rn 
from 
master..spt_values a
,master..spt_values b
,master..spt_values c
)t4

这里有22个不同的密钥,每个密钥值约为5000

如果我的样本数据逻辑错误,那么你可以纠正并告诉我。我在我的机器上试试。

我使用这个索引

CREATE nonCLUSTERED INDEX MyIndex
ON DataTable ([key] ASC)
INCLUDE ([value])

现在我使用旧查询,

select a.[key], max(a.value) value
from DataTable a
group by [key]

只需不到1秒,但它有Index Scan。 为什么?解释与@ Roger相同。查询计划很简单。

现在就像你说的那样,如果你循环抛出那22行,那么你就得到索引搜索。 我在这里使用Recursive方法

;with RecursiveCTE as
(
select [key],cast(0 as bigint)rn,max(value)value
from DataTable
where [key]=0  
group by [key]

union ALL

select [key],rn,t4.value FROM
(
select c.[key]+1 [key],max(a.value)over(order by a.[key]) value
,ROW_NUMBER()OVER(order by a.[key]) rn

from DataTable a
inner join RecursiveCTE c
on a.[key]=c.[key]
)t4 where rn=1
)

select [key],value FROM
RecursiveCTE
OPTION (MAXRECURSION 0);

这里我27 second返回22 rows。我在recursive的两个部分都得到Index Seek。查询计划很复杂。我不知道如何附加它。

现在决定?我没有试过循环,你可以比较和更新我们。