9

2 次元データ (マップ上のポイント) でいっぱいのデータベースがあります。各レコードには、ジオメトリ タイプのフィールドがあります。私ができる必要があるのは、k 個の最も近いポイントを返すストアド プロシージャにポイントを渡すことです (k は sproc にも渡されますが、それは簡単です)。http://blogs.msdn.com/isaac/archive/2008/10/23/nearest-neighbors.aspxで単一の最近傍を取得するクエリを見つけましたが、それを拡張する方法がわかりませんk最近隣人を見つけます。

これは現在のクエリTです。 はテーブル、gはジオメトリ フィールド、@xは検索対象のポイント、Numbersは整数 1 ~nのテーブルです。

DECLARE @start FLOAT = 1000; 
WITH NearestPoints AS
(
     SELECT TOP(1) WITH TIES *,  T.g.STDistance(@x) AS dist
     FROM Numbers JOIN T WITH(INDEX(spatial_index)) 
     ON T.g.STDistance(@x) < @start*POWER(2,Numbers.n)
     ORDER BY n
)
SELECT TOP(1) * FROM NearestPoints
ORDER BY n, dist

内側のクエリは最も近い空でない領域を選択し、外側のクエリはその領域から上位の結果を選択します。外側のクエリは (eg) に簡単に変更できますがSELECT TOP(20)、最も近いリージョンに結果が 1 つしか含まれていない場合は、それで行き詰まります。

おそらく、 k 個のレコードを含む最初の領域を再帰的に検索する必要があると思いますが、テーブル変数を使用しないでください (テーブル構造を作成する必要があり、変更されやすいため、メンテナンスの問題が発生します。多くのフィールドがあります)。方法がわかりません。

4

2 に答える 2

2

内部クエリから削除し、上位kTOP (1) WITH TIESを返すように外部クエリを設定するとどうなりますか?

私はまた、この修正がまったく役立つかどうかを知りたいと思います。使用するよりも効率的である必要がありますTOP

DECLARE @start FLOAT = 1000
        ,@k INT = 20
        ,@p FLOAT = 2;

WITH NearestPoints AS
(
     SELECT *
            ,T.g.STDistance(@x) AS dist
            ,ROW_NUMBER() OVER (ORDER BY T.g.STDistance(@x)) AS rn
     FROM Numbers 
     JOIN T WITH(INDEX(spatial_index)) 
     ON   T.g.STDistance(@x) <  @start*POWER(@p,Numbers.n)
     AND (Numbers.n - 1 = 0 
          OR T.g.STDistance(@x) >= @start*POWER(@p,Numbers.n - 1)
         )
)
SELECT * 
FROM NearestPoints
WHERE rn <= @k;

注意-テストされていません-ここではSQL2008にアクセスできません。

于 2010-03-26T12:27:11.543 に答える
2

Inside Microsoft® SQL Server® 2008: T-SQL プログラミングから引用。セクション 14.8.4。

次のクエリは、@input に最も近い 10 の関心のあるポイントを返します。

DECLARE @input GEOGRAPHY = 'POINT (-147 61)';
DECLARE @start FLOAT = 1000;
WITH NearestNeighbor AS(
  SELECT TOP 10 WITH TIES
    *, b.GEOG.STDistance(@input) AS dist
  FROM Nums n JOIN GeoNames b WITH(INDEX(geog_hhhh_16_sidx)) -- index hint
  ON b.GEOG.STDistance(@input) < @start*POWER(CAST(2 AS FLOAT),n.n)
  AND b.GEOG.STDistance(@input) >=
    CASE WHEN n = 1 THEN 0 ELSE @start*POWER(CAST(2 AS FLOAT),n.n-1) END
  WHERE n <= 20
  ORDER BY n
)
  SELECT TOP 10 geonameid, name, feature_code, admin1_code, dist
  FROM NearestNeighbor
  ORDER BY n, dist;

注: このクエリの WHERE 句の一部のみが、空間インデックスでサポートされています。ただし、クエリ オプティマイザーは、インデックスを使用して、サポートされている部分 ("<" 比較) を正しく評価します。これにより、">=" 部分をテストする必要がある行数が制限され、クエリのパフォーマンスが向上します。@start の値を変更すると、クエリが必要以上に遅い場合に、クエリが高速化されることがあります。

リスト2-1。補助数表の作成と移入

SET NOCOUNT ON;
USE InsideTSQL2008;

IF OBJECT_ID('dbo.Nums', 'U') IS NOT NULL DROP TABLE dbo.Nums;

CREATE TABLE dbo.Nums(n INT NOT NULL PRIMARY KEY);
DECLARE @max AS INT, @rc AS INT;
SET @max = 1000000;
SET @rc = 1;

INSERT INTO Nums VALUES(1);
WHILE @rc * 2 <= @max
BEGIN
  INSERT INTO dbo.Nums SELECT n + @rc FROM dbo.Nums;
  SET @rc = @rc * 2;
END

INSERT INTO dbo.Nums
  SELECT n + @rc FROM dbo.Nums WHERE n + @rc <= @max;
于 2010-08-24T21:38:45.060 に答える