【问题标题】:Group by value and create geography polyline from points (latitude and longitude) for each group in T-SQL按值分组并从 T-SQL 中的每个组的点(纬度和经度)创建地理折线
【发布时间】:2018-01-13 15:50:55
【问题描述】:

这里有人问过类似的问题:

Create geography polyline from points in T-SQL

进一步考虑这个问题,我有一个如下所示的表架构:

CREATE TABLE [dbo].[LongAndLats](
[Longitude] [float] NULL,
[Latitude] [float] NULL,
[SortOrder] [bigint] NULL,
[SensorID] [bigint] NULL,
)

示例数据如下所示:

如何使用 TSQL 将这些点转换为每个 SensorID 的地理折线(以便每个 SensorID 都有一个 SensorID/折线记录)?

我尝试过使用 db_cursor,但我得到了每个组的单独结果集(我认为地理位置可能相同)。这段代码:

DECLARE @SensorID VARCHAR(2000)
DECLARE @LineFromPoints geography
DECLARE @BuildString NVARCHAR(MAX)

DECLARE db_cursor CURSOR FOR  
SELECT Distinct([SensorId]) 
FROM [dbo].[LongAndLats]

OPEN db_cursor   
FETCH NEXT FROM db_cursor INTO LongAndLats 

WHILE @@FETCH_STATUS = 0   
BEGIN   
       SELECT @BuildString = COALESCE(@BuildString + ',', '') + CAST([Longitude] AS NVARCHAR(50)) + ' ' + CAST([Latitude] AS NVARCHAR(50))
       FROM [LongAndLats]
       WHERE SensorID = @SensorID
       ORDER BY SortOrder            

       SET @BuildString = 'LINESTRING(' + @BuildString + ')';   
       SET @LineFromPoints = geography::STLineFromText(@BuildString, 4326);
       SELECT @LineFromPoints As 'Geomerty', @name As 'SensorID' 

       FETCH NEXT FROM db_cursor INTO @name   
END   

CLOSE db_cursor   
DEALLOCATE db_cursor

结果如下:

最后,我想要一个返回所有 SensorID/Polyline 对的视图。我不知道我目前的方法是否有效。我将不胜感激任何建议或示例。

【问题讨论】:

    标签: sql-server tsql geospatial sqlgeography sqlgeometry


    【解决方案1】:

    来自SQL Server 2017+,您可以使用:

    SELECT geography::STLineFromText('LINESTRING(' + 
             STRING_AGG(CONCAT(Longitude, ' ' ,Latitude), ',') 
             WITHIN GROUP(ORDER BY SortOrder) + ')' , 4326) AS geometry
          ,SensorId
    FROM dbo.LongAndLats
    GROUP BY SensorId
    HAVING COUNT(*) > 1;
    

    DBFiddle Demo


    我尝试过使用 db_cursor,但我得到了每个组的单独结果集

    请避免使用光标,每行以分号结束并停止使用:

    SELECT @BuildString = COALESCE(@BuildString + ',', '') 
           + CAST([Longitude] AS NVARCHAR(50)) + ' ' + CAST([Latitude] 
            AS NVARCHAR(50))
    FROM [LongAndLats]
    WHERE SensorID = @SensorID
    ORDER BY SortOrder;  
    

    上面的构造可能看起来不错,但它可能导致未定义的行为。更多信息:nvarchar concatenation / index / nvarchar(max) inexplicable behavior

    编辑:

    SQL Server 2012 版本:

    SELECT geography::STLineFromText('LINESTRING(' 
          + STUFF(
                 (SELECT ',' + CONCAT(Longitude, ' ' ,Latitude) 
                  FROM dbo.LongAndLats t2
                  WHERE t1.SensorId = t2.SensorId 
                  ORDER BY SortOrder
                  FOR XML PATH (''))
                 , 1, 1, '')
           + ')' 
           , 4326) AS geometry, SensorId
    FROM dbo.LongAndLats t1
    GROUP BY SensorId
    HAVING COUNT(*) > 1;
    

    DBFiddle Demo2

    EDIT2:

    避免:

    在执行用户定义的例程或聚合“地理”期间发生 .NET Framework 错误:

    System.FormatException: 24117: LineString 输入无效,因为它没有足够的点。 LineString 必须至少有两个点。

    你可以加HAVING COUNT(*) > 1

    最终编辑:

    如果您有“垃圾数据”,只需将其过滤掉(或在该列上添加CHECK 约束):

    “纬度值必须在 -90 到 90 度之间”

    SELECT geography::STLineFromText('LINESTRING(' 
          + STUFF(
                 (SELECT ',' + CONCAT(Longitude, ' ' ,Latitude) 
                  FROM dbo.LongAndLats t2
                  WHERE t1.SensorId = t2.SensorId 
                    AND Latitude BETWEEN -90 and 90
                    AND Longitude BETWEEN -180 AND 180
                  ORDER BY SortOrder
                  FOR XML PATH (''))
                 , 1, 1, '')
           + ')' 
           , 4326) AS geometry, SensorId
    FROM dbo.LongAndLats t1
    WHERE Latitude BETWEEN -90 and 90
      AND Longitude BETWEEN -180 AND 180
    GROUP BY SensorId
    HAVING COUNT(*) > 1;
    

    DBFiddle Demo3

    【讨论】:

    • 谢谢。我应该补充说我正在使用 SQL Server 2012。您如何看待这个示例 (gooroo.io/GoorooTHINK/Article/10001/…) 作为 STRING_AGG 等效项?
    • @user3799279 请检查 STUFF + XML 版本
    • 再次感谢。我得到了一个 FormatException “纬度值必须在 -90 到 90 度之间”,并且可以在 where 子句中过滤掉它们,然后得到另一个 FormatException “LineString 输入无效,因为它没有足够的点。LineString 必须有至少两分。”我如何过滤 Count([SensorId]) > 2?
    • @user3799279 HAVING COUNT(*) > 1;
    • 这很漂亮,干得漂亮。感谢您的时间和示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-02
    • 1970-01-01
    相关资源
    最近更新 更多