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


105

私は顧客データベースを再設計しています。標準の住所フィールド(Street、Cityなど)とともに保存したい新しい情報の1つは、住所の地理的な場所です。私が考えている唯一の使用例は、住所が見つからない場合にユーザーがGoogleマップに座標をマッピングできるようにすることです。これは、エリアが新しく開発された場合や、遠隔地や農村部にある場合によく発生します。

私の最初の傾向は、緯度と経度を10進値として格納することでしたが、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

そうではありませんが、その私は持っていない場合は、はるか、なぜ追加の複雑さを複雑?

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


更新

@Erik Philipsは、近接検索を行う機能をもたらしました geography、で。これは非常に優れています。

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

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

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


私が実行したテストの詳細:

私は2つのテーブルを作成しました。1つは緯度と経度geographyを使用し、もう1つはを使用していdecimal(9,6)ます。

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)

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

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

4
LatとLonをそれぞれの列に保存し、Geographyオブジェクト用に別の列を作成するため、両方を実行することを考えているので、Lat / Lonだけが必要な場合は列から取得し、近接検索が必要な場合は地理を使用します。これは賢いですか?マイナス面はありますか?
Yuval A.

@YuvalA。それは確かに合理的に聞こえ、良い妥協かもしれません。私が頭から離れている唯一の懸念は、テーブルにGeography列があることがテーブルに対するクエリに影響を与えるかどうかです-私はその経験がないので、テストして確認する必要があります。
ジェフ緒方

1
新しい質問をするのではなく、なぜ新しい質問で質問を更新し続けたのですか?
チャド

@チャドはあなたが何を意味するのかわからない。質問の本文を1回更新しましたが、それ以上質問することはありませんでした。
Jeff Ogata

6
SQL Server 2012には、空間インデックスによる大幅なパフォーマンスの向上が含まれています。また、位置情報を保存している限り、後で検索サービスを使用して空間情報を追加し、既に保存されている住所をジオコーディングできることにも注意してください。
Volvox 2013年

回答:


66

空間計算を計画している場合、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();
}

次に、Geographyを使用する非常に十分な理由があります。

Entity Framework内の空間の説明

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

Noel Abrahams Answerで述べたように:

スペースに関する注意:各座標は、長さが64ビット(8バイト)の倍精度浮動小数点数として格納され、8バイトの2進値は10進精度の15桁にほぼ等しいため、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

geographyデータ型は、30%以上の領域を占有します。

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

SQL Serverに緯度と経度を10進数として保存するのは賢明ではありません。

アップデート2

距離や面積などの計算を計画している場合、地球の表面上でこれらを適切に計算することは困難です。SQL Serverに保存される各地理タイプは、空間参照IDとともに保存されます。これらのIDは、異なる球にすることができます(地球は4326です)。これは、SQL Serverでの計算が実際に地球の表面全体で正しく計算されることを意味します(地表面を通過する可能性のあるハエの代わり)。

ここに画像の説明を入力してください


1
この情報に追加するために、Geographyを使用すると、SQL検索の機能が他の緯度/経度(通常は長方形)の間の緯度/経度(地理情報のみ)から拡張されます。地理データ型では、ほぼすべてのサイズと形状の複数の領域を作成できます。
エリックフィリップス

1
再度、感謝します。私は使用を検討する理由を尋ねましたgeography、そしてあなたはいくつかの良いものを提供しました。最終的にはdecimal、この場合はフィールドのみを使用することを決定しました(長々とした更新を参照してください)が、geography単に座標をマッピングするだけではなく、もっと特別なことが必要な場合に使用できることを知っておくのは良いことです。
ジェフ尾形、

6

考慮すべきもう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

geographyデータ型は、ほぼ2倍のスペースを占めます。


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

9
@ErikPhilipsポイントは、必要なのがdecimal(9、6)だけであるのに、decimal(15、12)を使用するのはなぜですか?上記の比較は実用的なものであり、学術的な演習ではありません。
Noel Abrahams 2013年

-1
    CREATE FUNCTION [dbo].[fn_GreatCircleDistance]
(@Latitude1 As Decimal(38, 19), @Longitude1 As Decimal(38, 19), 
            @Latitude2 As Decimal(38, 19), @Longitude2 As Decimal(38, 19), 
            @ValuesAsDecimalDegrees As bit = 1, 
            @ResultAsMiles As bit = 0)
RETURNS decimal(38,19)
AS
BEGIN
    -- Declare the return variable here
    DECLARE @ResultVar  decimal(38,19)

    -- Add the T-SQL statements to compute the return value here
/*
Credit for conversion algorithm to Chip Pearson
Web Page: www.cpearson.com/excel/latlong.aspx
Email: chip@cpearson.com
Phone: (816) 214-6957 USA Central Time (-6:00 UTC)
Between 9:00 AM and 7:00 PM

Ported to Transact SQL by Paul Burrows BCIS
*/
DECLARE  @C_RADIUS_EARTH_KM As Decimal(38, 19)
SET @C_RADIUS_EARTH_KM = 6370.97327862
DECLARE  @C_RADIUS_EARTH_MI As Decimal(38, 19)
SET @C_RADIUS_EARTH_MI = 3958.73926185
DECLARE  @C_PI As Decimal(38, 19)
SET @C_PI =  pi()

DECLARE @Lat1 As Decimal(38, 19)
DECLARE @Lat2 As Decimal(38, 19)
DECLARE @Long1 As Decimal(38, 19)
DECLARE @Long2 As Decimal(38, 19)
DECLARE @X As bigint
DECLARE @Delta As Decimal(38, 19)

If @ValuesAsDecimalDegrees = 1 
Begin
    set @X = 1
END
Else
Begin
    set @X = 24
End 

-- convert to decimal degrees
set @Lat1 = @Latitude1 * @X
set @Long1 = @Longitude1 * @X
set @Lat2 = @Latitude2 * @X
set @Long2 = @Longitude2 * @X

-- convert to radians: radians = (degrees/180) * PI
set @Lat1 = (@Lat1 / 180) * @C_PI
set @Lat2 = (@Lat2 / 180) * @C_PI
set @Long1 = (@Long1 / 180) * @C_PI
set @Long2 = (@Long2 / 180) * @C_PI

-- get the central spherical angle
set @Delta = ((2 * ASin(Sqrt((power(Sin((@Lat1 - @Lat2) / 2) ,2)) + 
    Cos(@Lat1) * Cos(@Lat2) * (power(Sin((@Long1 - @Long2) / 2) ,2))))))

If @ResultAsMiles = 1 
Begin
    set @ResultVar = @Delta * @C_RADIUS_EARTH_MI
End
Else
Begin
    set @ResultVar = @Delta * @C_RADIUS_EARTH_KM
End

    -- Return the result of the function
    RETURN @ResultVar

END

2
新しい答えはいつでも歓迎ですが、コンテキストを追加してください。上記が問題をどのように解決するかを簡単に説明すると、答えが他の人にとってより有用になります。
リー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.