我尝试使用索引搜索在大表的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
答案 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
。查询计划很复杂。我不知道如何附加它。
现在决定?我没有试过循环,你可以比较和更新我们。