如何优化 STDistance 执行?

got*_*tqn 7 sql-server sql-server-2012 spatial

我在存储过程执行期间使用以下结构创建临时表:

[ID] BIGINT
[Point] GEOGRAPHY
Run Code Online (Sandbox Code Playgroud)

ID不是唯一的-大约有200每个记录ID

我需要找到一个不同的列表,IDs其中至少有一个PointPoint常量值(例如200米)大的距离。

所以,我正在使用这样的东西:

SELECT DISTINCT DS1.[ID]
FROM DataSource DS1
INNER JOIN DataSource DS2
    ON DS1.[ID] = DS2.[ID]
WHERE DS1.Point.STDistance(DS2.Point) > 200
Run Code Online (Sandbox Code Playgroud)

对于 23 000 个点,查询执行4-5几秒钟。因为我期待有更多的价值,所以我需要找到更好的解决方案。

我想如果有更快的方法,我总是可以创建一个物化表并实现额外的逻辑来计算它ID

我创建了一个空间索引,但查询优化器没有使用它。如果我使用hint这样的WITH (INDEX(SPATIAL_idx_test))我会收到以下错误:

消息 8635,级别 16,状态 4,第 78 行
查询处理器无法为带有空间索引提示的查询生成查询计划。原因:空间索引不支持谓词中提供的比较器。尝试删除索引提示或删除SET FORCEPLAN. `

Pau*_*ite 7

无论您进行任何其他改进,请务必测试启用跟踪标志 6532、6533 和 6534(仅限启动)对空间执行时间的影响。这些打开本机代码空间实现。需要 SQL Server 2012 Service Pack 3 或 SQL Server 2014 Service Pack 2(Microsoft 支持文章)。默认情况下,本机编译从 SQL Server 2016 开始。

对于STDistance重要的跟踪标志是6533。在一个简单的测试中,这将执行时间从 2100 毫秒提高到 150 毫秒而无需在我笔记本电脑的 SQL Server 2012 实例上使用空间索引。

改编自SQL 2016 的示例——它运行得更快: Bob Ward 的原生空间实现):

测试数据

CREATE TABLE dbo.SpatialTest
(
    ID      integer NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    Points  geography NOT NULL
);
GO
-- Insert random sample points
SET NOCOUNT ON;
GO
DECLARE @Point float = 1.1;

INSERT dbo.SpatialTest (Points) 
VALUES ('POINT(' + CAST(@Point AS varchar(20)) + ' ' + CAST(@Point AS varchar(20)) + ')' );

WHILE(SCOPE_IDENTITY() < 100000)
BEGIN
    SET @Point = @Point + RAND(SCOPE_IDENTITY());
    IF (@Point > 90.0)
    BEGIN
        SET @Point = -89.0 + RAND(SCOPE_IDENTITY());
    END;

    INSERT dbo.SpatialTest (Points) 
    VALUES ( 'POINT(' + CAST(@Point AS varchar(20)) + ' ' + CAST(@Point AS varchar(20)) + ')' );
END;
Run Code Online (Sandbox Code Playgroud)

测试查询

DBCC TRACEON (6533);
DBCC TRACESTATUS;
GO
DECLARE 
    @s datetime2 = SYSUTCDATETIME(),
    @g geography = 'POINT(1.0 80.5)';

SELECT [Matches] = COUNT_BIG(*) 
FROM dbo.SpatialTest AS ST
WHERE ST.Points.STDistance(@g) > 10000000.5
OPTION (MAXDOP 1, RECOMPILE);

SELECT [Elapsed STDistance Query (ms)] = DATEDIFF(MILLISECOND, @s, SYSUTCDATETIME());
Run Code Online (Sandbox Code Playgroud)

平均执行时间:2100ms(trace flag off);150ms(跟踪标志打开)。