【问题标题】:Insert buffered GEOGRAPHY Points into target table without duplicates将缓冲的 GEOGRAPHY 点插入目标表而不重复
【发布时间】:2016-01-23 10:53:05
【问题描述】:

我想从一个源表创建一个目标表,它只包含唯一/不同的点,因为在 1000 米半径范围内不应该有任何点。

这是一个起点(使用临时表的简化模型):

IF OBJECT_ID('tempdb..#Source') IS NOT NULL DROP TABLE #Source
IF OBJECT_ID('tempdb..#TargetSeeded') IS NOT NULL DROP TABLE #TargetSeeded
IF OBJECT_ID('tempdb..#TargetEmpty') IS NOT NULL DROP TABLE #TargetEmpty

CREATE TABLE #Source
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    Point GEOGRAPHY
)

CREATE TABLE #TargetSeeded
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    Point GEOGRAPHY
)

CREATE TABLE #TargetEmpty
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    Point GEOGRAPHY
)

DECLARE @Point1 GEOGRAPHY;
DECLARE @Point2 GEOGRAPHY;
DECLARE @Point3 GEOGRAPHY;
DECLARE @Point4 GEOGRAPHY;
DECLARE @PointBufferDistanceInMeters INT;

SET @Point1 = GEOGRAPHY::STPointFromText('POINT(1 52.50)', 4326);
SET @Point2 = GEOGRAPHY::STPointFromText('POINT(1 52.51)', 4326);
SET @Point3 = GEOGRAPHY::STPointFromText('POINT(1 52.52)', 4326);
SET @Point4 = GEOGRAPHY::STPointFromText('POINT(1 52.52)', 4326);
SET @PointBufferDistanceInMeters = 1000;

--SELECT @Point1.STDistance(@Point2);
--SELECT @Point1.STDistance(@Point3);
--SELECT @Point1.STDistance(@Point4);
--SELECT @Point2.STDistance(@Point3);

INSERT INTO #Source
    SELECT @Point1
        UNION ALL 
    SELECT @Point2
        UNION ALL 
    SELECT @Point3
        UNION ALL 
    SELECT @Point4

INSERT INTO #TargetSeeded
    SELECT @Point1
        UNION ALL 
    SELECT @Point2

CREATE SPATIAL INDEX SpatialIndex ON #Source([Point]);
CREATE SPATIAL INDEX SpatialIndex ON #TargetEmpty([Point]);
CREATE SPATIAL INDEX SpatialIndex ON #TargetSeeded([Point]);

-- Identify Ids to be inserted
SELECT 
    Id,
    Point
FROM #Source WHERE Id NOT IN
(
    SELECT 
        So.Id
    FROM #Source AS So
    INNER JOIN #TargetSeeded AS Ta
    ON So.Point.STDistance(Ta.Point) < @PointBufferDistanceInMeters
) 

我可以以(恕我直言)有效(?)的方式识别要从#Source 作为基于集合的方法插入的候选人。我只是不知道如何删除#Source 表中的重复项(上述意义上的重复项 - 缓冲区为 1000 米)。所以最终,我希望将 id 3 或 4 插入到#TargetSeed 中(哪个无关紧要)。有什么想法吗?

PS:

这是一个相关的子查询尝试,可能是一个解决方案:

IF OBJECT_ID('tempdb..#Source') IS NOT NULL DROP TABLE #Source
IF OBJECT_ID('tempdb..#TargetSeeded') IS NOT NULL DROP TABLE #TargetSeeded
IF OBJECT_ID('tempdb..#TargetEmpty') IS NOT NULL DROP TABLE #TargetEmpty

CREATE TABLE #Source
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    Point GEOGRAPHY
)

CREATE TABLE #TargetSeeded
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    Point GEOGRAPHY
)

CREATE TABLE #TargetEmpty
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    Point GEOGRAPHY
)

DECLARE @Point1 GEOGRAPHY;
DECLARE @Point2 GEOGRAPHY;
DECLARE @Point3 GEOGRAPHY;
DECLARE @Point4 GEOGRAPHY;
DECLARE @PointBufferDistanceInMeters INT;

SET @Point1 = GEOGRAPHY::STPointFromText('POINT(1 52.50)', 4326);
SET @Point2 = GEOGRAPHY::STPointFromText('POINT(1 52.51)', 4326);
SET @Point3 = GEOGRAPHY::STPointFromText('POINT(1 52.52)', 4326);
SET @Point4 = GEOGRAPHY::STPointFromText('POINT(1 52.52)', 4326);
SET @PointBufferDistanceInMeters = 1000;

--SELECT @Point1.STDistance(@Point2);
--SELECT @Point1.STDistance(@Point3);
--SELECT @Point1.STDistance(@Point4);
--SELECT @Point2.STDistance(@Point3);

INSERT INTO #Source
    SELECT @Point1
        UNION ALL 
    SELECT @Point2
        UNION ALL 
    SELECT @Point3
        UNION ALL 
    SELECT @Point4

INSERT INTO #TargetSeeded
    SELECT @Point1
        UNION ALL 
    SELECT @Point2

CREATE SPATIAL INDEX SpatialIndex ON #Source([Point]);
CREATE SPATIAL INDEX SpatialIndex ON #TargetEmpty([Point]);
CREATE SPATIAL INDEX SpatialIndex ON #TargetSeeded([Point]);

-- Identify Ids to be inserted
DELETE FROM #Source WHERE Id NOT IN 
(
    SELECT 
        Id
    FROM #Source WHERE Id NOT IN
    (
        SELECT 
            So.Id
        FROM #Source AS So
        INNER JOIN #TargetSeeded AS Ta
        ON So.Point.STDistance(Ta.Point) < @PointBufferDistanceInMeters
    ) 
)

SELECT 
    *
FROM #Source o
WHERE o.Id IN
(
    SELECT MAX(i.Id)
    FROM #Source i
    WHERE 
        i.Point.STDistance(o.Point) < @PointBufferDistanceInMeters
)

【问题讨论】:

  • 也许一个可视化的例子会更好。您是否有一个大的点列表并且想要选择点并且那些需要间隔 1000 米的点,比如尝试创建仓库以优化配送过程?或者您已经有了这些点并且想要删除每个点缓冲区内的所有点?第一个需要非常大的优化过程,第二个很容易使用contain函数
  • 您似乎正在检查 #Source#TargetSeeded 行之间的距离。您是否保证 within #Source 没有任何违反您的 1000m 规则的行对?
  • @Habo - 请参阅 PS - 相关子查询尝试

标签: sql-server tsql sql-server-2014 sqlgeography


【解决方案1】:

这有帮助吗?

-- Sample data.
declare @Source as Table ( Id Int Identity Primary Key, Point Geography );
insert into @Source ( Point ) values
  ( Geography::STPointFromText( 'Point( 1 52.50 )', 4326 ) ),
  ( Geography::STPointFromText( 'Point( 1 52.51 )', 4326 ) ),
  ( Geography::STPointFromText( 'Point( 1 52.52 )', 4326 ) ),
  ( Geography::STPointFromText( 'Point( 1 52.52 )', 4326 ) );
select *, Point.ToString() as DecodedPoint from @Source;

declare @Target as Table ( Id Int Identity Primary Key, Point Geography );
insert into @Target ( Point ) values
  ( Geography::STPointFromText( 'Point( 1 52.50 )', 4326 ) ),
  ( Geography::STPointFromText( 'Point( 1 52.51 )', 4326 ) );
select *, Point.ToString() as DecodedPoint from @Target;

declare @PointBufferDistanceInMeters as Int = 1000;

-- Merge the data.    
insert into @Target
  select Point
    from @Source as S
    where
      -- Remove rows that conflict with another   Source   row.
      not exists ( select 42 from @Source where S.Point.STDistance( Point ) < @PointBufferDistanceInMeters and S.Id < Id ) and
      -- Remove rows that conflict with an existing   Target   row.
      not exists ( select 42 from @Target where S.Point.STDistance( Point ) < @PointBufferDistanceInMeters )
select *, Point.ToString() as DecodedPoint from @Target;

【讨论】:

  • 谢谢,它看起来很棒,而且可能不像我最初的尝试那么笨拙。你能告诉我'select 42'是什么意思吗?
  • @csetzkorn 42 是the answer!这是一个方便的占位符,语法需要一个值或表达式,但它永远不会被使用,例如在exists 查询中。
  • 哈哈 - 我怎么能错过这个(-:
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-20
  • 2021-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多