Ken*_*ith 7 sql-server spatial
我们有一个简单的 SQL Server 表,其中包含如下所示的地理空间数据:
CREATE TABLE [dbo].[Factors](
[Id] [int] IDENTITY(1,1) NOT NULL,
[StateCode] [nvarchar](2) NOT NULL,
[GeoLocation] [geography] NULL,
[Factor] [decimal](18, 6) NOT NULL,
CONSTRAINT [PK_dbo.Factors] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
Run Code Online (Sandbox Code Playgroud)
我们现在有大约 100k+ 行,但预计会增长到数百万。
我们对其运行查询,如下所示:
declare @state nvarchar(2) = 'AL'
declare @point geography = geography::STGeomFromText('POINT(-86.19146040 32.38225770)', 4326)
select top 3
Lat,
Lon,
Factor,
GeoLocation.STDistance(@point) as Distance
from dbo.Factors
where StateCode = @state and GeoLocation.STDistance(@point) is not null
order by Distance
Run Code Online (Sandbox Code Playgroud)
不过,这有点奇怪。该表中的数据是参差不齐的:例如,我们得到了一个州南部的数据,但没有得到整个州的数据。如果我们搜索的点在我们获得数据的点的几百米内(例如,来自该州的南部),则查询返回亚秒级。但是,如果距离最近的数据点 100 公里(例如,如果目标点来自该州的北部),则查询最多需要 3 分钟左右才能返回。在这两种情况下,查询计划都表明它们是从地理空间索引的扫描开始的,所以这不是有时会发生的问题,SQL Server 无法确定它应该使用有问题的索引。
我的假设是它与地理空间索引的布局方式有关。
CREATE SPATIAL INDEX IX_Factors_Spatial
ON [dbo].[Factors] (GeoLocation)
USING GEOGRAPHY_AUTO_GRID
WITH (
CELLS_PER_OBJECT = 16,
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON);
Run Code Online (Sandbox Code Playgroud)
但我不知道我是否能很好地理解细节以解决问题。
有关如何解决此问题的任何建议?
简短回答:比较快速和慢速变体的实际执行计划,您会看到自己。
当给定@point的点接近表中的点时,空间索引中使用的细分实际上有助于消除大多数行,并且只需要很少的索引查找。
当给定@point远离表中的任何点时,引擎实际上必须读取所有行。它寻求索引 100K 次,这很慢。
如果禁用空间索引,您将看到查询的性能对于任何给定的@point. 当索引有用时,它会比您的快速变体慢,但当索引有害时,它会比您的慢变体更快。
如果您还没有了解此类索引的内部结构的基本细节,请参阅空间索引概述。
样本测试数据
CREATE TABLE [dbo].[Factors](
[Id] [int] IDENTITY(1,1) NOT NULL,
[GeoLocation] [geography] NULL,
[Factor] [decimal](18, 6) NOT NULL,
CONSTRAINT [PK_Factors] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
在 ~20km x ~20km 区域内生成 ~100K 行。
DECLARE @MinLat float = -38.180184;
DECLARE @MaxLat float = -38.000000;
DECLARE @MinLon float = 145.000000;
DECLARE @MaxLon float = 145.227707;
DECLARE @PointCount int = 317;
WITH
x AS
(
SELECT TOP (@PointCount)
ROW_NUMBER() OVER (ORDER BY [object_id]) AS rn
FROM sys.all_objects
)
INSERT INTO [dbo].[Factors]
([GeoLocation]
,[Factor])
SELECT
geography::Point(
@MinLat + (TLat.rn-1) * (@MaxLat - @MinLat) / (@PointCount-1)
,@MinLon + (TLon.rn-1) * (@MaxLon - @MinLon) / (@PointCount-1)
,4326) AS GeoLocation
,0 AS Factor
FROM
x AS TLat CROSS JOIN x AS TLon
ORDER BY TLat.rn, TLon.rn;
Run Code Online (Sandbox Code Playgroud)
创建默认空间索引
CREATE SPATIAL INDEX [IX_GeoLocation] ON [dbo].[Factors]
(
[GeoLocation]
)USING GEOGRAPHY_AUTO_GRID
WITH (
CELLS_PER_OBJECT = 16,
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
测试查询
@point1接近表中的其他点。
@point2远离表中的其他点。
declare @point1 geography = geography::Point(-38.000000, 145.000000, 4326);
declare @point2 geography = geography::Point(+38.000000, 145.000000, 4326);
select top 3
Factor,
GeoLocation.STDistance(@point1) as Distance
from dbo.Factors
where GeoLocation.STDistance(@point1) is not null
order by Distance
option(recompile);
select top 3
Factor,
GeoLocation.STDistance(@point2) as Distance
from dbo.Factors
where GeoLocation.STDistance(@point2) is not null
order by Distance
option(recompile);
Run Code Online (Sandbox Code Playgroud)
启用索引
IO。最高结果很快(7 毫秒,171 次读取)。底部结果很慢(5,693 毫秒,234,662 次读取)。
快速地。
减缓。
索引已禁用
IO。两个查询具有相同的读取次数 (601) 和相同的持续时间 (~1700ms)。
两个查询的计划相同:
扫描 100K 行比搜索 100K 次更快。
我不知道如何解决这个问题,如果有一种方法可以两全其美,并以某种方式自动决定是否使用索引。
您可以尝试计算边界框(最小/最大纬度/经度)并根据给定点是否在边界框内更改逻辑。
最有趣的事情发生在内置的 Geodetic Tesselation 表值函数中,我不知道如何对其进行微调。
空间索引很大程度上取决于您的数据分布。
在某些情况下,如果您知道数据很密集并且可以将搜索限制在窄条带或小区域(给定点 +- 几公里),则最好使用纬度和经度上的两个单独的简单标准索引。
| 归档时间: |
|
| 查看次数: |
3076 次 |
| 最近记录: |