web-dev-qa-db-ja.com

STIntersectsのパフォーマンスの向上

テーブルT_PINには300,000個のピンがあり、T_POLYGONには36,000個のポリゴンがあります。 T_PINには次のインデックスがあります:

CREATE SPATIAL INDEX [T_PIN_COORD] ON [dbo].[T_PIN]
(
[Coord]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = HIGH,LEVEL_2 = HIGH,LEVEL_3 = HIGH,LEVEL_4 = HIGH), 
CELLS_PER_OBJECT = 128, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY];

T_POLYGONには以下が含まれます:

CREATE SPATIAL INDEX [T_POLYGON_COORD] ON [dbo].[T_POLYGON]
(
[COORD]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = HIGH,LEVEL_2 = HIGH,LEVEL_3 = HIGH,LEVEL_4 = HIGH), 
CELLS_PER_OBJECT = 128, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
ON [PRIMARY];

T_PINT_POLYGONの共通部分を見つけるクエリは、実行に45分以上かかります。

SELECT COUNT(*)
FROM T_PIN 
INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.COORD) = 1;

結果は4,438,318行です。

このクエリを高速化するにはどうすればよいですか?

11
seb49

まず、クエリ実行プランを調べて空間インデックスが使用されているかどうかを確認し、クラスター化インデックスシーク(空間)アイテムがあるかどうかを確認します。

それが使用されていると仮定すると、最初に確認するために、単純化されたポリゴンを含むバウンディングボックスに基づいてセカンダリ/単純化されたフィルターを追加してみることができます。これらの単純化されたポリゴンとの一致は、最終的な結果を得るためにプライマリフィルターを介して実行できます。

1)[dbo]。[T_POLYGON]テーブルに新しいgeographyとgeometry列を追加します。

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeom geometry;
ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog geography;

2)バウンディングボックスポリゴンを作成します(これには、STEnvelope()を利用するためのジオメトリへの初期変換が含まれます)。

UPDATE [dbo].[T_POLYGON] SET SimplePolysGeom = geometry::STGeomFromWKB(
    COORD.STAsBinary(), COORD.STSrid).STEnvelope();

UPDATE [dbo].[T_POLYGON] SET SimplePolysGeog = geography::STGeomFromWKB(
    SimplePolysGeom.STAsBinary(), SimplePolysGeom.STSrid);

3)簡略化されたgeography列に空間インデックスを作成する

4)この単純化されたgeography列に対する交差を取得し、一致するgeographyデータ型で再度フィルター処理します。おおよそ、次のようなもの:

;WITH cte AS
(
   SELECT pinID, polygonID FROM T_PIN INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.SimplePolysGeog ) = 1
)
SELECT COUNT(*)
FROM T_PIN 
INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.COORD) = 1
    AND T_PIN.pinID IN (SELECT pinID FROM cte)
    AND T_POLYGON.polygonID IN (SELECT polygonID FROM cte)

[〜#〜] edit [〜#〜]:(1)と(2)をこの計算された永続列に置き換えることができます。提案のためのポールホワイトへの信用。

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog AS  ([geography]::STGeomFromWKB([geometry]::STGeomFromWKB([COORD].[STAsBinary](),[COORD].[STSrid]).STEnvelope().STAsBinary(),(4326))) PERSISTED
7
g2server

このようなクエリは、ポリゴンが複雑なため、時間がかかることがよくあります。 (たとえば)複雑な海岸線が境界近くのポイントをテストするのに年齢を要し、ポイントが内側か外側かを見つけるために多くのレベルをズームする必要があることを見てきました。

...それで、ポリゴンを.Reduce() 'してみて、それが役立つかどうかを確認できます。

その関数の詳細については、 http://msdn.Microsoft.com/en-us/library/cc627410.aspx を参照してください

2
Rob Farley

Microsoftのドキュメントによると、空間インデックスは、WHERE句のある比較述語の先頭にある場合、次のメソッドの地理タイプで使用されます。

  • STIntersects
  • STDistance
  • STEquals

ジオメトリタイプのメソッド(制限付きリスト)のみが_JOIN ... ON_で空間インデックスの使用をトリガーするため、WHERE geog1.STIntersects(geog2) = 1を使用するようにコードを変更すると、速度が向上します。

g2server's answer でアドバイスを受け、フィルタリングと空間インデックスを追加するために以下を追加することもお勧めします

_ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog AS
     ([geography]::STGeomFromWKB([geometry]::STGeomFromWKB([COORD].[STAsBinary](),
                                                           [COORD].[STSrid])
                 .STEnvelope().STAsBinary(),(4326))) PERSISTED
_

次に、次のようなクエリlikeを作成できます(私はこの投稿をすぐに書いて、まだテストしていません。これは、クエリと投稿された最高の回答は、空間インデックスを使用しないJOIN ON空間op = 1を使用します):

_SELECT   
     (SELECT p2.polygon_id
      FROM   T_Polygon p2
      WHERE  p2.coords.STIntersects(t.coords) = 1),
     t.pin_id
FROM     T_PIN t
WHERE    
     (SELECT t.coords.STIntersects(p.coords)
      FROM   T_POLYGON p
      WHERE  t.coords.STIntersects(p.SimplePolysGeog) = 1) = 1
_

参考:SimplePolysGeogが重複してしまうと上記は機能しません(ピンが2つの簡略化されたジオグにある可能性があるため、これをある州の境内の人々に実行しただけで、通常のポリゴンが境界を共有しているため、境界ボックスが重複しています)なので、ほとんどのユースケースでは、サブクエリが複数の結果を返すというエラーがスローされます。

MSドキュメントから 空間インデックスの概要

空間インデックスでサポートされる地理的方法

特定の条件下で、空間インデックスは次のセット指向の地理メソッドをサポートします:STIntersects()、STEquals()、およびSTDistance()。空間インデックスでサポートするには、これらのメソッドをクエリのWHERE句内で使用する必要があり、次の一般的な形式の述語内で実行する必要があります。

geography1.method_name(geography2)comparison_operatorvalid_number

Null以外の結果を返すには、geography1およびgeography2に同じ 空間参照識別子(SRID) 。それ以外の場合、メソッドはNULLを返します。

空間インデックスは、次の述語形式をサポートしています。


空間インデックスを使用するクエリ

空間インデックスは、WHERE句にインデックス付き空間演算子を含むクエリでのみサポートされます。たとえば、次のような構文:

_[spatial object].SpatialMethod([reference spatial object]) [ = | < ] [const literal or variable]
_

クエリオプティマイザーは、空間演算(その@a.STIntersects(@b) = @b.STInterestcs(@a))の交換可能性を理解します。ただし、比較の開始に空間演算子が含まれていない場合、空間インデックスは使用されません(たとえば、_WHERE 1 = spatial op_は空間インデックスを使用しません)。空間インデックスを使用するには、比較を書き直します(たとえば、_WHERE spatial op = 1_)。

...

次のクエリは、SimplePolysGeogsが重複している場合に機能します。

_;WITH cte AS
(
   SELECT T_PIN.PIN_ID, 
          T_POLYGON.POLYGON_ID, 
          T_POLYGON.COORD 
   FROM T_PIN 
   INNER JOIN T_POLYGON
   ON T_PIN.COORD.STIntersects(T_POLYGON.SimplePolysGeog) = 1
)

SELECT COUNT(*)
FROM T_PIN 
INNER JOIN cte
ON T_PIN_PIN_ID = cte.PIN_ID
where cte.[COORD].STIntersects(T_PIN.COORD) = 1
_
1
pbordeaux