如何使用 SQL Server 空间数据类型设置多维距离搜索的解决方案

wil*_*ten 7 sql-server spatial nearest-neighbor geometry

我有一个性能不佳的现有数据库解决方案。我正在使用浮点数等使用传统表结构搜索多维数据。数据库包含约 1-2M 行。

经过一番搜索,我发现了 SQL Server 空间数据类型,您可以在其中定义基于 Point 或 MultiPoint 的几何图形,您可以在其中进行距离搜索。我认为这可能是一个解决方案,但我需要一些帮助才能朝着正确的方向开始。

我当前的表结构如下,其中每行包含 2 个(最终更多)XYZ 值:

    | ID | X1  | Y1  | Z1  | X2  | Y2  | Z2  |
    | 1  | 1.1 | 2.2 | 5.1 | 1.2 | 2.1 | 4.1 |
    | 2  | 3.2 | 5.1 | 4.1 | 3.2 | 3.1 | 3.1 |
    | 3  | 4.1 | 2.3 | 6.3 | 4.2 | 4.1 | 2.1 |
    | 4  | 2.4 | 3.5 | 2.1 | 3.2 | 2.1 | 4.1 |
Run Code Online (Sandbox Code Playgroud)

我在对每个 X、Y 和 Z 值进行容差搜索。

例如:

    (pseudocode)
    declare targetXYZ1 = (4.1, 2.2, 3.1);
    declare targetXYZ2 = (2.8, 2.2, 4.2);

    declare toleranceXYZ = (2,2,2)

    Select ID from MyXYZTable tb
    Where 
           Math.Abs(tb.X1 - targetXYZ1.X1) < toleranceXYZ.X 
       AND Math.Abs(tb.Y1 - targetXYZ1.Y1) < toleranceXYZ.Y
       AND Math.Abs(tb.Z1 - targetXYZ1.Z1) < toleranceXYZ.Z

       AND Math.Abs(tb.X2 - targetXYZ1.X2) < toleranceXYZ.X 
       AND Math.Abs(tb.Y2 - targetXYZ1.Y2) < toleranceXYZ.Y
       AND Math.Abs(tb.Z2 - targetXYZ1.Z2) < toleranceXYZ.Z
Run Code Online (Sandbox Code Playgroud)

经过一些分析后,我可能需要为 xyz1 和 xyz2 制作一个包含 1 或 2 个几何列的表格,其中 geometry::Point(0, 0, 0)

我的问题:

  1. SQL Server 空间类型是否有可能的解决方案?
  2. 我需要使用最近邻搜索还是可以使用 STDistance
  3. 我需要为表设置空间索引吗?

非常欢迎任何建议或提示和技巧!

Bra*_*adC 5

可能有一些空间方法在某种程度上有用,但您最大的问题将是 Z 值

Z 坐标不用于库进行的任何计算,也不通过任何库计算进行。

这是设计使然。不要将 SQL 空间对象视为“真正的” 3D 几何对象,而是将它们视为带有“海拔标记”的地图坐标,不会被考虑到任何计算中。因此距离函数STDistance()非常适用于“地图距离”,但不适用于真正的 3D 距离。

您可以定义一个点的 Z 值(和“M”值),但它不用于任何计算。

展示:

DECLARE @a geometry = geometry::Parse('POINT(0 0 0 2)');
DECLARE @b geometry = geometry::Parse('POINT(1 0 5 1)');
SELECT @a.Z, @b.Z, @a.STDistance(@b)
Run Code Online (Sandbox Code Playgroud)

将返回值

0   5   1
Run Code Online (Sandbox Code Playgroud)

显然,如果正确使用 Z,这两个点之间的距离应该大于 1。因此,它们可能不会像您希望的那样工作。

但是,如果您可以忽略 Z 值,那么执行这些计算将非常简单。您可以从现有表中读取这些值,如下所示:

SELECT geometry::Point(X1,Y1,0).STDistance(geometry::Point(X2,Y2,0))
From myTable;  
Run Code Online (Sandbox Code Playgroud)

0在这种用法中,它不是Z 值,而是SRID。)

或者,如果您花时间更改它们的实际存储方式:

CREATE TABLE myPoints (ID INT, P1 geometry, P2 geometry)

INSERT myPoints 
SELECT 1, geometry::Point(X1, Y1, 0), geometry::Point(X2, Y2, 0)
FROM oldTable

SELECT ID, P1.STDistance(P2)
FROM myPoints
Run Code Online (Sandbox Code Playgroud)

在我这里的一个较旧的答案中有一些相关的讨论。

编辑:一些进一步的想法:

  1. 关于性能,我完全不知道geometry对象将如何在性能方面与您手动进行数学比较。我想即时转换为 Geometry 对象会有一定程度的开销,但也许转换原始数据的存储方式会更好。测试将是这里的关键。
  2. 在Z参数可能没有被使用STDistance()功能,但很明显,你可以手动查询 Z,并用它做的是数学自己的一部分,只是使用毕达哥拉斯:True3D_Distance ^ 2 = STDistance ^ 2 +(AZ-BZ)^ 2
  3. 但请注意,如果您的目标是简单地按接近度对点进行排名,并且您不需要计算实际距离,则可以简化您的计算。我在想类似WHERE PointA.STDistance(PointB) + ABS(PointA.Z-PointB.Z) < tolerance或者也许WHERE PointA.STDistance(PointB) < tolerance AND ABS(PointA.Z-PointB.Z) < tolerance