如何使用Lat Longs的表类型进行批处理STDistance?

时间:2014-12-01 12:33:29

标签: sql-server tsql stored-procedures geospatial spatial

我正在尝试将表类型传递给存储过程,并希望sproc查找lat / longs的每一行并返回该行的最近点。

类型:

CREATE TYPE dbo.LatLongRoadLinkType AS TABLE
( 
    Id INT NOT NULL,
    Latitude FLOAT NOT NULL,
    Longitude FLOAT NOT NULL
);

存储过程:

ALTER PROCEDURE [dbo].[BatchNearestRoadNodes]
    @Input dbo.LatLongRoadLinkType READONLY
AS
BEGIN
    -- do stuff here
    -- return a table of id from input, nodeid and distance 
END

对于整个表格,这里需要做的是单个纬度/经度:

DECLARE @g geography = 'POINT(13.5333414077759 54.549524307251)';
DECLARE @region geography = @g.STBuffer(5000)

SELECT TOP 1 NodeID, Point.STDistance(@g) as 'Distance'
FROM Location 
WHERE Point.Filter(@region) = 1
ORDER BY Point.STDistance(@g)

Location表有重要的Point类型Geography,它是空间索引的,是比较完成的。我将lat / longs表从代码发送到sproc中,代码期待返回of:

Id (original point passed in)
NodeID (of nearest point in location table)
Distance

我该如何处理?为了让它更容易一点,我可以简单地将SqlGeography从我的代码传递到sproc而不是Lat / Long,但是这会导致性能下降,因为它转换成非常昂贵的。

修改: 这有效,但不知道它是否是最佳解决方案。

ALTER PROCEDURE [dbo].[BatchNearestRoadNodes]
    @Input dbo.LatLongRoadLinkType READONLY
AS
BEGIN

SELECT x.Id, x.LocationName, x.NodeID, x.Distance
FROM (SELECT I.Id,
        L.LocationName,
        L.NodeId,
        L.Point.STDistance(geography::Point(I.Latitude, I.Longitude, 4326)) AS Distance,
        ROW_NUMBER ()  OVER (PARTITION BY I.Id ORDER BY L.Point.STDistance(geography::Point(I.Latitude, I.Longitude, 4326)) ASC) AS Ranking
        FROM @Input AS I
        JOIN Location AS L 
        ON L.Point.STIntersects(geography::Point(I.Latitude, I.Longitude, 4326).STBuffer(5000)) = 1
    ) AS x WHERE Ranking = 1
END

效果 - V1与Jon的编辑

V1
============
original:643 found:627 in:1361 ms
original:1018 found:999 in:1700 ms
original:1801 found:1758 in:2628 ms
original:4098 found:3973 in:5271 ms
original:16388 found:15948 in:19624 ms

Jon's Edit
==========
original:643 found:627 in:1333 ms
original:1018 found:999 in:1689 ms
original:1801 found:1758 in:2559 ms
original:4098 found:3973 in:5114 ms
original:16388 found:15948 in:19054 ms

差异很小。需要得到最后一个数字。

1 个答案:

答案 0 :(得分:0)

尝试这样的方法来获得部分结果:

WITH PreQuery AS
(
I.Id,
GEOGRAPHY::STPointFromText(I.PointAsWKT).STBuffer(5000) AS Geog,
L.NodeId,
L.Point
FROM
@Input AS I
JOIN
Location AS L ON L.Point.STIntersects(I.Geog) = 1
)
SELECT
P.Id,
P.NodeId,
P.Geog.STDistance(P.Point) AS Distance
FROM
PreQuery P

我是从头上写下来的,没有任何测试数据,所以可能会有小错误,但主要是它会给你每个节点和它距离每个点的距离(在5000米内) 。你仍然需要过滤它们以获得每个id最小距离的那个 - 不应该太难; - )

希望它有所帮助,即使不完整。

编辑(12月2日)

我已经看到了我的第一个解决方案的问题,你无法获得距离,因为它是预先缓冲的(注意主要的事情)。但是,这种合并应该是两种尝试的最有效组合。

WITH PreQuery AS
(
SELECT
I.Id,
geography::Point(I.Latitude, I.Longitude, 4326) AS InputGeography
FROM
@input AS I
)
SELECT x.Id, x.LocationName, x.NodeId, x.Distance
FROM
(
SELECT
PQ.Id,
L.LocationName,
L.NodeId,
L.Point.STDistance(PQ.InputGeography) AS Distance,
ROWNUMBER() OVER (PARTITION BY I.Id ORDER BY L.Point.Distance(PQ.InputGeography) ASC) AS Ranking
FROM
Prequery AS PQ
JOIN
Location AS L
-- ON L.Point.STIntersects(PQ.InputGeography.STBuffer(5000)) = 1 -- Slower
ON L.Point.STDistance(PQ.InputGeography) <= 5000 -- Faster
) AS X WHERE Ranking = 1

这样,您只需预先创建输入地理位置一次,而不是根据您的尝试创建三次。这是未经测试的,但应该证明是最有效的。