11

我精通 SQL,但不熟悉使用 SQL 几何功能。我有一个可能是一个非常基本的问题要解决,但我还没有在网上找到任何好的资源来解释如何使用几何对象。(Technet 是一种糟糕的学习新事物的方式......)

我在笛卡尔平面上有一个二维点的集合,我试图找到半径集合内的所有点。

我使用如下语法创建并填充了一个表:

更新 [事物] 设置 [位置] = geometry::Point(@X, @Y, 0)

(@X,@Y 只是 x 和 y 值,0 是所有对象共享的任意数字,如果我理解正确,允许设置过滤)

这是我偏离轨道的地方......我是否尝试使用它构建某种多边形集合和查询,或者是否有一些简单的方法来检查多个半径的交集而不构建一堆圆形多边形?

附录:如果没有人知道多半径问题的答案,那么单半径解决方案是什么?

更新

以下是我处理的一些示例,使用虚构的星数据库,其中星作为点存储在 xy 网格上:

选择框中的所有点:

DECLARE @polygon geometry = geometry::STGeomFromText('POLYGON((' 
+ CAST(@MinX AS VARCHAR(10)) + ' '  + CAST(@MinY AS VARCHAR(10)) + ',' 
+ CAST(@MaxX AS VARCHAR(10)) + ' '  + CAST(@MinY AS VARCHAR(10)) + ', ' 
+ CAST(@MaxX AS VARCHAR(10)) + ' '  + CAST(@MaxY AS VARCHAR(10)) + ',' 
+ CAST(@MinX AS VARCHAR(10)) + ' '  + CAST(@MaxY AS VARCHAR(10)) + ',' 
+ CAST(@MinX AS VARCHAR(10)) + ' '  + CAST(@MinY AS VARCHAR(10)) + '))', 0);

SELECT  [Star].[Name]           AS [StarName],
        [Star].[StarTypeId]     AS [StarTypeId],        
FROM    [Star]
WHERE   @polygon.STContains([Star].[Location]) = 1

使用它作为模式,您可以做各种有趣的事情,例如定义多个多边形:

WHERE   @polygon1.STContains([Star].[Location]) = 1
OR @polygon2.STContains([Star].[Location]) = 1
OR @polygon3.STContains([Star].[Location]) = 1

或检查距离:

WHERE [Star].[Location].STDistance(@polygon1) < @SomeDistance 

示例插入语句

INSERT [Star]
(
    [Name],
    [StarTypeId],
    [Location],
)
VALUES
(
    @Name,
    @StarTypeId,
    GEOMETRY::Point(@LocationX, @LocationY, 0),
)
4

2 回答 2

18

This is an incredibly late answer, but perhaps I can shed some light on a solution. The "set" number you refer to is a Spatial Reference Indentifier or SRID. For lat/long calculations you should consider setting this to 4326, which will ensure metres are used as a unit of measurement. You should also consider switching to SqlGeography rather than SqlGeometry, but we'll continue with SqlGeometry for now. To bulk set the SRID, you can update your table as follows:

UPDATE [YourTable] SET [SpatialColumn] = GEOMETRY.STPointFromText([SpatialColumn].STAsText(), 4326);

For a single radius, you need to create a radii as a spatial object. For example:

DECLARE @radiusInMeters FLOAT = 1000; -- Set to a number in meters
DECLARE @radius GEOMETRY = GEOMETRY::Point(@x, @y, 4326).STBuffer(@radiusInMeters);

STBuffer() takes the spatial point and creates a circle (now a Polygon type) from it. You can then query your data set as follows:

SELECT * FROM [YourTable] WHERE [SpatialColumn].STIntersects(@radius);

The above will now use any Spatial Index you have created on the [SpatialColumn] in its query plan.

There is also a simpler option which will work (and still use a spatial index). The STDistance method allows you to do the following:

DECLARE @radius GEOMETRY = GEOMETRY::Point(@x, @y, 4326);
DECLARE @distance FLOAT = 1000; -- A distance in metres   
SELECT * FROM [YourTable] WHERE [SpatialColumn].STDistance(@radius) <= @distance;

Lastly, working with a collection of radii. You have a few options. The first is to run the above for each radii in turn, but I would consider the following to do it as one:

DECLARE #radiiCollection TABLE
(
    [RadiusInMetres] FLOAT,
    [Radius] GEOMETRY
)

INSERT INTO #radiiCollection ([RadiusInMetres], [Radius]) VALUES (1000, GEOMETRY::Point(@xValue, @yValue, 4326).STBuffer(1000));
-- Repeat for other radii

SELECT
    X.[Id],
    MIN(R.[RadiusInMetres]) AS [WithinRadiusDistance]
FROM
    [YourTable] X
    JOIN
    #radiiCollection RC ON RC.[Radius].STIntersects(X.[SpatialColumn])
GROUP BY
    X.[IdColumn],
    R.[RadiusInMetres]

DROP TABLE @radiiCollection;

The final above has not been tested, but I'm 99% sure it's just about there with a small amount of tweaking being a possibility. The ideal of taking the min radius distance in the select is that if the multiple radii stem from a single location, if a point is within the first radius, it will naturally be within all of the others. You'll therefore duplicate the record, but by grouping and then selecting the min, you get only one (and the closest).

Hope it helps, albeit 4 weeks after you asked the question. Sorry I didn't see it sooner, if only there was only one spatial tag for questions!!!!

于 2014-03-09T22:31:56.257 回答
-1

当然,这是可能的。单独的 where 子句应该类似于:

DIM @Center AS Location
-- Initialize the location here, you probably know better how to do that than I.
Dim @Radius AS Decimal(10, 2)
SELECT * from pointTable WHERE sqrt(square(@Center.STX-Location.STX)+square(@Center.STX-Location.STX)) > @Radius 

然后,您可以将一堆半径和 xy 点堆积到一个表格变量中,如下所示:

Dim @MyCircleTable AS Table(Geometry Circle) 
INSERT INTO @MyCircleTable (.........)

注意:我没有通过编译器完成这个,但这是一个工作解决方案的基本框架。

其他选项看起来在这里: http ://technet.microsoft.com/en-us/library/bb933904.aspx

这里有一个看似有效的语法演示:http: //social.msdn.microsoft.com/Forums/sqlserver/en-US/6e1d7af4-ecc2-4d82-b069-f2517c3276c2/slow-spatial-predicates-stcontains-stintersects- stwithin-?forum=sqlspatial

第二篇文章暗示了语法:

SELECT Distinct pointTable.* from pointTable pt, circletable crcs
WHERE crcs.geom.STContains(b.Location) = 1
于 2014-02-11T17:20:01.780 回答