我有一个存储城市 IP 地址范围的表,该表中有数百万条记录。我敢肯定你们中许多处理 IP 地址的人都有一个与我相似的表(我在这个例子中简化了我的表):
CREATE TABLE [dbo].[IPRangeByCity]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[IPIntegerStart] [bigint] NOT NULL,
[IPIntegerEnd] [bigint] NOT NULL,
[Country] [nvarchar](150) NOT NULL,
[City] [nvarchar](150) NULL
CONSTRAINT [pk_IPRangeByCity]
PRIMARY KEY CLUSTERED([ID] ASC),
) ON [PRIMARY]
GO
现在我不保存、更新或删除该表中的任何记录。我只从这张 table 上读到。当我从这个表中读取时,我获取一个 IPv4 地址,将其转换为整数形式,并使用 IPv4 地址的整数形式,我在 IP 地址范围内查找该整数的城市。
例如,假设 IPv4 地址是“187.245.227.116”。
“187.245.227.116”转换为整数 3153453940。然后我运行以下选择语句来查找与此 IP 地址关联的城市:
select * from IPRangeByCity
where 3153453940 between IPIntegerStart and IPIntegerEnd
我的问题是,如果我只使用上面的 select 语句从这个表中读取,我应该如何构建我的索引以缩短 select 语句的查找时间?
在我的脑海中,如果我将此表的索引设置为“IPIntegerStart”列,它似乎是我的 select 语句的一个很好的索引。例如:
CONSTRAINT [pk_IPRangeByCity] PRIMARY KEY CLUSTERED([IPIntegerStart] ASC)
不过,我不太确定。根据我的选择语句,有人知道为我的表设置的最佳索引是什么吗?它应该是聚簇索引还是非聚簇索引?它应该是多列索引(即同时包含 IPIntegerStart 和 IPIntegerEnd 列的索引)吗?任何帮助,将不胜感激。谢谢。
编辑:我可以在我的表中创建任何主键。在此示例中,我将主键设置为 ID 和身份键。但是我可以将主键更改为任何列,只要我的 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 地址的完整列表并搜索它的可能性——它实际上并不像听起来那么荒谬。最终,这一切都取决于细节。
关于sql-server - SQL 服务器 : How should I structure my index to improve lookup time?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40958730/