web-dev-qa-db-ja.com

DbGeographyクエリの改善

私はまだデータベース管理に不慣れで、検索クエリを最適化しようとしています。

次のようなクエリがあり、場合によっては実行に5〜15秒かかり、CPU使用率が100%になっていました。

DECLARE @point geography;
SET @point = geography::STPointFromText('POINT(3.3109015 6.648294)', 4326); 

SELECT TOP (1)
     [Result].[PointId] AS [PointId], 
     [Result].[PointName] AS [PointName], 
     [Result].[LegendTypeId] AS [LegendTypeId], 
     [Result].[GeoPoint] AS [GeoPoint]
FROM ( 
    SELECT 
        [Extent1].[GeoPoint].STDistance(@point) AS distance, 
        [Extent1].[PointId] AS [PointId], 
        [Extent1].[PointName] AS [PointName], 
        [Extent1].[LegendTypeId] AS [LegendTypeId], 
        [Extent1].[GeoPoint] AS [GeoPoint]
    FROM [dbo].[GeographyPoint] AS [Extent1]
    WHERE 18 = [Extent1].[LegendTypeId] 
)  AS [Result]
ORDER By [Result].distance ASC

このテーブルには、PKに1つのクラスター化インデックスがあり、geographyタイプの列に1つの空間インデックスがあります。

enter image description here

したがって、上記のクエリを実行すると、スキャン操作が実行されていました。

enter image description here

そのため、LegendTypeId列に非クラスター化インデックスを作成しました。

CREATE NONCLUSTERED INDEX [GeographyPoint_LegendType_NonClustered] ON [dbo].[GeographyPoint]
(
    [LegendTypeId] ASC
)
INCLUDE (   [PointId],
    [PointName],
    [GeoPoint]) 
    WITH (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]
GO

クエリを次のように変更しました:

DECLARE @point geography;
SET @point = geography::STPointFromText('POINT({0} {1})', 4326); 

 SELECT TOP (1) 
     [GeoPoint].STDistance(@point) AS distance, 
     [PointId], 
     [PointName],
     [LegendTypeId], 
     [GeoPoint]
     FROM [GeographyPoint]
 WHERE 18 = [LegendTypeId]
 ORDER By distance ASC

そして、SQL Serverはスキャンの代わりにシークを実行します。

enter image description here

私の見解では、クエリの効率が向上していますが、これを運用環境に展開しても、同じ結果が得られます(CPU使用率が高く、クエリの実行に平均10秒かかります)。

注:このテーブルでは、データの挿入、更新、削除は行われません。検索/読み取りのみです。

  1. それは私が間違っていることですか?

  2. どうすれば修正できますか?

[〜#〜]編集[〜#〜]

インデックスシークの詳細

enter image description here

編集2:

クエリを変更して、メソッド「Nearest Neighbor」をリンクから使用しました: https://msdn.Microsoft.com/en-us/library/ff929109.aspx 、これが結果です、このクエリも検索に3〜5秒かかります-2番目のクエリと同様です(ただし、本番環境ではテストされていません)。

enter image description here

空間インデックス設定:

CREATE SPATIAL INDEX [SPATIAL_Point] ON [dbo].[GeographyPoint]
(
[GeoPoint]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = MEDIUM,LEVEL_2 = MEDIUM,LEVEL_3 = MEDIUM,LEVEL_4 = MEDIUM), 
CELLS_PER_OBJECT = 16, 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]
GO

EDIT 3@MickyTの指示に従い、[LegendTypeId]のインデックスを削除し、次のクエリを実行しました:

DECLARE @point geography;
SET @point = geography::STPointFromText('POINT(3.3109 6.6482)', 4326); 

SELECT TOP (1) 

    [PointId],
    [PointName],
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint] WITH(INDEX(SPATIAL_Point))
WHERE 
   [GeoPoint].STDistance(@point) IS NOT NULL AND
    18 = [LegendTypeId]
ORDER By [GeoPoint].STDistance(@point) ASC
OPTION(MAXDOP 1)

このクエリの統計は

enter image description here

次に、このクエリを再度実行しました。

DECLARE @point geography;
SET @point = geography::STPointFromText('POINT(3.3109 6.6482)', 4326); 

 SELECT TOP (1) 
     [GeoPoint].STDistance(@point) AS distance, 
     [PointId], 
     [PointName],
     [LegendTypeId], 
     [GeoPoint]
     FROM [GeographyPoint] --WITH(INDEX(SPATIAL_Point))
 WHERE 18 = [LegendTypeId]
 ORDER By distance ASC

このクエリの統計は

enter image description here

7
Dawood Awan

次の設定を使用して、いくつかのテストを実行しました。

CREATE TABLE GeographyPoint (
    ID INTEGER IDENTITY(1,1) NOT NULL PRIMARY KEY,
    GeoPoint GEOGRAPHY NOT NULL,
    LegendTypeID INTEGER NOT NULL
    );

INSERT INTO GeographyPoint (GeoPoint, LegendTypeID)
SELECT TOP 1000000 
    Geography::Point(Rand(CAST(NEWID() AS VARBINARY(MAX))) * 2,Rand(CAST(NEWID() AS VARBINARY(MAX))) * 2,4326),
    CAST(Rand(CAST(NEWID() AS VARBINARY(MAX))) * 25 AS INTEGER)
FROM Tally;

CREATE INDEX GP_IDX1 ON GeographyPoint(LegendTypeID) INCLUDE (ID, GeoPoint);
CREATE SPATIAL INDEX GP_SIDX ON GeographyPoint(GeoPoint) USING GEOGRAPHY_AUTO_GRID;

これにより、2 x 2度の広がりを持つ1,000,000個のランダムポイントのテーブルが得られます。
いくつかの異なるオプションを試した後、私が得ることができる最高のパフォーマンスは、空間インデックスを使用することを強制することでした。これを達成する方法はいくつかありました。 LegendTypeIDのインデックスを削除するか、ヒントを使用します。
どちらが自分の状況に最適かを判断する必要があります。個人的には、インデックスヒントを使用したくないので、他のクエリで必要でない場合は、他のインデックスを削除します。

クエリは互いに積み重ねられます

DECLARE @point geography;
SET @point = geography::Point(1,1,4326); 
/*
Clustered index scan (PK)
 SQL Server Execution Times:
   CPU time = 641 ms,  elapsed time = 809 ms
*/
SELECT TOP (1) 
    [GeoPoint].STDistance(@point) AS distance, 
    [ID], 
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint]
WHERE 18 = [LegendTypeId]
ORDER By distance ASC
OPTION(MAXDOP 1)
/*
Index Seek NonClustered (GP_IDX1)
 SQL Server Execution Times:
   CPU time = 2250 ms,  elapsed time = 2806 ms
*/
SELECT TOP (1) 
    [GeoPoint].STDistance(@point) AS distance, 
    [ID], 
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint]
WHERE [GeoPoint].STDistance(@point) IS NOT NULL AND
    18 = [LegendTypeId]
ORDER By [GeoPoint].STDistance(@point) ASC
OPTION(MAXDOP 1)

/*
For the next 2 queries
Clustered Index Seek (Spatial)
 SQL Server Execution Times:
   CPU time = 15 ms,  elapsed time = 11 ms
*/
SELECT TOP (1) 
    [GeoPoint].STDistance(@point) AS distance, 
    [ID], 
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint] WITH(INDEX(GP_SIDX))
WHERE [GeoPoint].STDistance(@point) IS NOT NULL AND
    18 = [LegendTypeId]
ORDER By [GeoPoint].STDistance(@point) ASC
OPTION(MAXDOP 1)

DROP INDEX GP_IDX1 ON [GeographyPoint]

SELECT TOP (1) 
    [GeoPoint].STDistance(@point) AS distance, 
    [ID], 
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint]
WHERE [GeoPoint].STDistance(@point) IS NOT NULL AND
    18 = [LegendTypeId]
ORDER By [GeoPoint].STDistance(@point) ASC
OPTION(MAXDOP 1)
2
MickyT