web-dev-qa-db-ja.com

SQL Server 2008の地理データ型を使用する理由

顧客データベースを再設計していますが、標準の住所フィールド(通り、市など)とともに保存したい新しい情報の1つは、住所の地理的な場所です。私が念頭に置いている唯一のユースケースは、住所が見つからない場合に、ユーザーがGoogleマップの座標をマップできるようにすることです。これは、エリアが新しく開発されたり、リモート/田舎の場所にあるときによく起こります.

最初の傾向は、緯度と経度を小数値として保存することでしたが、SQL Server 2008 R2にはgeographyデータ型があることを思い出しました。 geographyを使用した経験はまったくありません。最初の調査から、このシナリオではやり過ぎに見えます。

たとえば、decimal(7,4)として保存されている緯度と経度を使用するには、次のようにします。

insert into Geotest(Latitude, Longitude) values (47.6475, -122.1393)
select Latitude, Longitude from Geotest

geographyを使用すると、次のようになります。

insert into Geotest(Geolocation) values (geography::Point(47.6475, -122.1393, 4326))
select Geolocation.Lat, Geolocation.Long from Geotest

thatほど複雑ではありませんが、必要がないのになぜ複雑になるのですか?

geographyを使用するという考えを捨てる前に、考慮すべきことはありますか?空間インデックスを使用して場所を検索する方が、緯度と経度のフィールドをインデックスするよりも速いでしょうか?知らないgeographyを使用する利点はありますか?または、反対に、どちらがgeographyを使用することを思いとどまらせるかについて知っておくべき警告がありますか?


更新

@Erik Philipsは、geographyを使用して近接検索を実行する機能を導入しました。これは非常に便利です。

一方、簡単なテストでは、selectを使用すると、緯度と経度を取得する単純なgeographyが大幅に遅くなることが示されています(詳細は以下)。 、および 受け入れられた回答 に対するコメントgeographyに関する別のSO質問に対する不満があります:

@SaphuAどういたしまして。補足として、null許容のGEOGRAPHYデータ型列で空間インデックスを使用する場合は、十分注意してください。重大なパフォーマンスの問題があるため、スキーマを再構築する必要がある場合でも、GEOGRAPHY列をNULL不可にします。 –トマス6月18日11時18分

全体として、近接検索を行う可能性とパフォーマンスと複雑さのトレードオフを比較検討し、この場合はgeographyの使用を控えることにしました。


実行したテストの詳細:

緯度と経度にgeographyを使用するテーブルとdecimal(9,6)を使用するテーブルの2つのテーブルを作成しました。

CREATE TABLE [dbo].[GeographyTest]
(
    [RowId] [int] IDENTITY(1,1) NOT NULL,
    [Location] [geography] NOT NULL,
    CONSTRAINT [PK_GeographyTest] PRIMARY KEY CLUSTERED ( [RowId] ASC )
) 

CREATE TABLE [dbo].[LatLongTest]
(
    [RowId] [int] IDENTITY(1,1) NOT NULL,
    [Latitude] [decimal](9, 6) NULL,
    [Longitude] [decimal](9, 6) NULL,
    CONSTRAINT [PK_LatLongTest] PRIMARY KEY CLUSTERED ([RowId] ASC)
) 

同じ緯度と経度の値を使用して各テーブルに単一の行を挿入しました。

insert into GeographyTest(Location) values (geography::Point(47.6475, -122.1393, 4326))
insert into LatLongTest(Latitude, Longitude) values (47.6475, -122.1393)

最後に、次のコードを実行すると、私のマシンでは、geographyを使用すると緯度と経度の選択が約5倍遅くなります。

declare @lat float, @long float,
        @d datetime2, @repCount int, @trialCount int, 
        @geographyDuration int, @latlongDuration int,
        @trials int = 3, @reps int = 100000

create table #results 
(
    GeographyDuration int,
    LatLongDuration int
)

set @trialCount = 0

while @trialCount < @trials
begin

    set @repCount = 0
    set @d = sysdatetime()

    while @repCount < @reps
    begin
        select @lat = Location.Lat,  @long = Location.Long from GeographyTest where RowId = 1
        set @repCount = @repCount + 1
    end

    set @geographyDuration = datediff(ms, @d, sysdatetime())

    set @repCount = 0
    set @d = sysdatetime()

    while @repCount < @reps
    begin
        select @lat = Latitude,  @long = Longitude from LatLongTest where RowId = 1
        set @repCount = @repCount + 1
    end

    set @latlongDuration = datediff(ms, @d, sysdatetime())

    insert into #results values(@geographyDuration, @latlongDuration)

    set @trialCount = @trialCount + 1

end

select * 
from #results

select avg(GeographyDuration) as AvgGeographyDuration, avg(LatLongDuration) as AvgLatLongDuration
from #results

drop table #results

結果:

GeographyDuration LatLongDuration
----------------- ---------------
5146              1020
5143              1016
5169              1030

AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
5152                 1022

さらに驚くべきことは、行が選択されていない場合でも、たとえばRowId = 2(存在しない)を選択した場合でも、geographyは依然として遅いということです。

GeographyDuration LatLongDuration
----------------- ---------------
1607              948
1610              946
1607              947

AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
1608                 947
104
Jeff Ogata

空間計算を行う予定がある場合、EF 5.0では次のようなLINQ式を使用できます。

private Facility GetNearestFacilityToJobsite(DbGeography jobsite)
{   
    var q1 = from f in context.Facilities            
             let distance = f.Geocode.Distance(jobsite)
             where distance < 500 * 1609.344     
             orderby distance 
             select f;   
    return q1.FirstOrDefault();
}

次に、地理を使用する非常に良い理由があります。

Entity Framework内の空間の説明

高性能空間データベースの作成 で更新

Noel Abrahams Answer :で述べたように

スペースに関する注意。各座標は64ビット(8バイト)の長さの倍精度浮動小数点数として格納され、8バイトのバイナリ値は15桁の10進精度にほぼ等しいため、decimal(9 、6)はわずか5バイトで、正確な比較ではありません。 Decimalは、実際の比較のために、各LatLong(合計18バイト)に対して少なくともDecimal(15,12)(9バイト)でなければなりません。

ストレージタイプの比較:

CREATE TABLE dbo.Geo
(    
geo geography
)
GO

CREATE TABLE dbo.LatLng
(    
    lat decimal(15, 12),   
    lng decimal(15, 12)
)
GO

INSERT dbo.Geo
SELECT geography::Point(12.3456789012345, 12.3456789012345, 4326) 
UNION ALL
SELECT geography::Point(87.6543210987654, 87.6543210987654, 4326) 

GO 10000

INSERT dbo.LatLng
SELECT  12.3456789012345, 12.3456789012345 
UNION
SELECT 87.6543210987654, 87.6543210987654

GO 10000

EXEC sp_spaceused 'dbo.Geo'

EXEC sp_spaceused 'dbo.LatLng'

結果:

name    rows    data     
Geo     20000   728 KB   
LatLon  20000   560 KB

地理データ型は、30%以上のスペースを占有します。

さらに、geographyデータ型はPointの格納だけに限定されず、 LineString、CircularString、CompoundCurve、Polygon、CurvePolygon、GeometryCollection、MultiPoint、MultiLineString、MultiPolygonなど も格納できます。ポイント(たとえば、LINESTRING(1 1、2 2)インスタンス)を超えて最も単純なGeographyタイプ(緯度/経度など)でさえ保存しようとすると、各ポイントに追加の行、各ポイントの順序付けのための列が発生します行をグループ化するための別の列。 SQL Serverには、 Area、Boundary、Length、Distancesなど の計算を含むGeographyデータ型のメソッドもあります。

LatitudeとLongitudeをSql ServerにDecimalとして保存するのは賢明ではないようです。

更新2

距離や面積などの計算を行う予定がある場合、地球の表面でこれらを適切に計算することは困難です。 SQL Serverに保存される各Geographyタイプは、 空間参照ID とともに保存されます。これらのIDは異なる球体にすることができます(地球は4326です)。これは、SQL Serverでの計算が実際に地球の表面上で正しく計算されることを意味します( as-the-crow-flies の代わりに、地球の表面を通過できます)。

enter image description here

65
Erik Philips

考慮すべきもう1つのことは、各方法で使用されるストレージスペースです。地理タイプはVARBINARY(MAX)として保存されます。このスクリプトを実行してみてください。

CREATE TABLE dbo.Geo
(
    geo geography

)

GO

CREATE TABLE dbo.LatLon
(
    lat decimal(9, 6)
,   lon decimal(9, 6)

)

GO

INSERT dbo.Geo
SELECT geography::Point(36.204824, 138.252924, 4326) UNION ALL
SELECT geography::Point(51.5220066, -0.0717512, 4326) 

GO 10000

INSERT dbo.LatLon
SELECT  36.204824, 138.252924 UNION
SELECT 51.5220066, -0.0717512

GO 10000

EXEC sp_spaceused 'dbo.Geo'
EXEC sp_spaceused 'dbo.LatLon'

結果:

name    rows    data     
Geo     20000   728 KB   
LatLon  20000   400 KB

地理データ型は、ほぼ2倍のスペースを占有します。

6
Noel Abrahams