Sql server SQL Server:我应该如何构造索引以缩短查找时间?

Sql server SQL Server:我应该如何构造索引以缩短查找时间?,sql-server,database,indexing,database-design,database-optimization,Sql Server,Database,Indexing,Database Design,Database Optimization,我有一个存储城市IP地址范围的表,该表中有数百万条记录。我确信,你们中的许多人处理IP地址时都有一个与我类似的表(在本例中,我简化了我的表): 现在,我不保存、更新或删除此表中的任何记录。我只在这张桌子上看书。当我从这个表中读取时,我获取一个IPv4地址,将其转换为整数形式,并使用IPv4地址的整数形式,在这个整数的IP地址范围之间查找城市 例如,假设IPv4地址为“187.245.227.116” “187.245.227.116”转换为整数3153453940。然后运行以下select语句以

我有一个存储城市IP地址范围的表,该表中有数百万条记录。我确信,你们中的许多人处理IP地址时都有一个与我类似的表(在本例中,我简化了我的表):

现在,我不保存、更新或删除此表中的任何记录。我只在这张桌子上看书。当我从这个表中读取时,我获取一个IPv4地址,将其转换为整数形式,并使用IPv4地址的整数形式,在这个整数的IP地址范围之间查找城市

例如,假设IPv4地址为“187.245.227.116”

“187.245.227.116”转换为整数3153453940。然后运行以下select语句以查找与此IP地址关联的城市:

select * from IPRangeByCity 
where 3153453940 between IPIntegerStart and IPIntegerEnd
我的问题是,如果我只使用上面的select语句读取此表,我应该如何构造索引以缩短select语句的查找时间

不经意间,如果我将此表的索引设置为“IPIntegerStart”列,那么它可能是select语句的良好索引。例如:

CONSTRAINT [pk_IPRangeByCity] PRIMARY KEY CLUSTERED([IPIntegerStart] ASC)
然而,我不是很确定。有人知道在给定select语句的情况下,为表设置索引的最佳索引是什么吗?它应该是聚集索引还是非聚集索引?是否应该是多列索引(即同时包含IPIntegerStart和IPINTEGERND列的索引)?任何帮助都将不胜感激。谢谢


编辑:我可以使主键成为表中的任何内容。在本例中,我将主键设置为ID,标识键设置为identity。但我可以将主键更改为任何列,只要我的select语句运行得快,这就是我所关心的。

编辑:在了解了一些细节之后,前面的答案就一文不值了。您最初的想法似乎是正确的:

alter table dbo.IPRangeByCity add constraint [PK_IPRangeByCity]
primary key (IPIntegerStart);
但是,在
IPIntegerEnd
上还需要一个非聚集索引。下面是一些解释

首先,由于范围之间没有重叠,并且
Id
列是伪造的,因此可以用指定的主键替换主键。PK在默认情况下是集群的,因此它将加快搜索和扫描速度

其次,正如Martin Smith在评论中正确指出的那样,使用
between
谓词或类似逻辑的直接查询将进行大规模索引扫描。但是,我认为这不是问题,因为没有范围重叠,这意味着任何IP地址最多只能属于1个范围。因此,可以按如下方式重写搜索查询:

select top (1) r.*
from dbo.IPRangeByCity r
where 3153453940 between r.IPIntegerStart and r.IPIntegerEnd
order by r.IPIntegerStart desc;
这样,它应该总是足够快,因为聚集索引扫描要么在找到第一个合适的行后停止,要么将被
IPIntegerEnd
切断。注意,这纯粹是猜测,您应该对照与您的生产相当的数据量进行检查


不过,我不会放弃类似于杰克·道格拉斯(Jack Douglas)的非规范化方法(尽管不能说我理解它)。我甚至不会放弃创建一个所有可能的IPv4地址的完整列表并在其中搜索的可能性——这实际上并不像听起来那么荒谬。最终,这一切都取决于细节。

我的测试结果更新,可能还有最终答案。我特别希望这对@MartinSmith和@RogerWolf有好处,他们给了我很多帮助。非常感谢马丁·史密斯和罗杰·沃尔夫

请注意,我的数据中没有IP范围重叠。而且,我的数据中有几百万行

我使用Roger Wolf的方法测试了聚集索引、非聚集索引等的一系列变体。对于这种方法,将IPIntegerStart列设置为聚集索引对性能的影响最大

我还测试了其他变化,例如:

1) 使IPIntegerStart和IPIntegerEnd都成为聚集索引。其性能相当于仅使iIntegerStart成为聚集索引

2) 使IPIntegerStart和IPIntegerEnd成为非聚集索引,但将ID(identity)列保留为聚集主键。性能大约比Roger Wolf重写的解决方案慢8倍,很可能是因为IPIntegerStart不是聚集索引

3) 使IPINTEGER启动聚集索引并使IPINTEGER结束非聚集索引。将IPINTEGREND设置为非聚集索引对性能没有显著影响。同样,重要的是IPIntegerStart被设置为聚集索引

这个故事的寓意是,当我修改设置聚集索引和非聚集索引的不同变体时,最重要的是最终将IPIntegerStart设置为聚集索引对加快速度的影响最大

下面是马丁·史密斯的建议,具体内容见(最初由杰克·道格拉斯撰写):

4) Jack Douglas方法的总体思路是将表格划分为不同的部分(Jack Douglas在其文章中称之为“颗粒”)。我把桌子分成4个部分,而不是3个部分。如果要加快查找速度,表存储的数据越多,可以添加的段就越多。请注意,添加的段越多,在select语句中执行的联合就越多—如果段太多,我不确定附加的联合最终是否会增加更多开销。Jack Douglas的方法产生了最快的查找,大约是将IPIntegerStart设置为聚集索引的基本方法的3到4倍

以下是三种不同方法的总结:

方法1)使用ID(标识键)作为聚集索引:最慢的方法

方法2)Roger Wolf将聚集索引设置为IPIntegerStart,并将IPIntegerEnd设置为非聚集索引的方法:大约比方法1快6到7倍)

方法3)Marti
select top (1) r.*
from dbo.IPRangeByCity r
where 3153453940 between r.IPIntegerStart and r.IPIntegerEnd
order by r.IPIntegerStart desc;