เหตุใดจึงใช้ชนิดข้อมูลภูมิศาสตร์ของ SQL Server 2008


105

ฉันกำลังออกแบบฐานข้อมูลลูกค้าใหม่และหนึ่งในข้อมูลใหม่ที่ฉันต้องการจัดเก็บพร้อมกับช่องที่อยู่มาตรฐาน (ถนนเมือง ฯลฯ ) คือที่ตั้งทางภูมิศาสตร์ของที่อยู่ กรณีการใช้งานเดียวที่ฉันนึกถึงคืออนุญาตให้ผู้ใช้ทำแผนที่พิกัดบนแผนที่ 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

แม้ว่าจะไม่ว่ามากความซับซ้อนมากขึ้นว่าทำไมความซับซ้อนเพิ่มถ้าฉันจะได้ไม่ต้อง?

ก่อนที่ฉันจะละทิ้งแนวคิดในการใช้geographyมีอะไรที่ฉันควรพิจารณาหรือไม่? จะเร็วกว่าไหมหากค้นหาตำแหน่งโดยใช้ดัชนีเชิงพื้นที่เทียบกับการจัดทำดัชนีเขตข้อมูลละติจูดและลองจิจูด มีข้อดีในการใช้งานgeographyที่ฉันไม่ทราบหรือไม่? หรือในทางกลับกันมีข้อแม้ที่ฉันควรรู้เกี่ยวกับสิ่งที่จะทำให้ฉันgeographyไม่สามารถใช้งานได้หรือไม่?


อัปเดต

@Erik Philips นำเสนอความสามารถในการค้นหาความใกล้เคียงgeographyซึ่งยอดเยี่ยมมาก

ในทางกลับกันการทดสอบอย่างรวดเร็วแสดงให้เห็นว่าวิธีง่ายๆselectในการรับละติจูดและลองจิจูดนั้นช้ากว่ามากเมื่อใช้geography(รายละเอียดด้านล่าง) และความคิดเห็นเกี่ยวกับคำตอบที่ได้รับการยอมรับสำหรับคำถาม SO อื่น ๆ ที่geographyฉันมีอยู่:

@SaphuA ยินดีต้อนรับครับ ในฐานะที่เป็นด้านข้างควรระมัดระวังอย่างยิ่งในการใช้ดัชนีเชิงพื้นที่บนคอลัมน์ประเภทข้อมูล GEOGRAPHY ที่เป็นโมฆะ มีปัญหาด้านประสิทธิภาพที่ร้ายแรงดังนั้นทำให้คอลัมน์ GEOGRAPHY นั้นไม่เป็นโมฆะแม้ว่าคุณจะต้องสร้างสคีมาใหม่ก็ตาม - Tomas 18 มิ.ย. เวลา 11:18 น

สรุปแล้วการชั่งน้ำหนักความเป็นไปได้ที่จะทำการค้นหาระยะใกล้เทียบกับการลดประสิทธิภาพและความซับซ้อนฉันตัดสินใจที่จะละทิ้งการใช้งานgeographyในกรณีนี้


รายละเอียดของการทดสอบที่ฉันใช้:

ฉันสร้างตารางสองตารางโดยหนึ่งใช้geographyและอีกตารางหนึ่งใช้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 ในคอลัมน์ของตัวเองและมีอีกคอลัมน์หนึ่งสำหรับวัตถุทางภูมิศาสตร์ดังนั้นหากฉันต้องการ Lat / Lon ฉันจะนำมันออกจากคอลัมน์และหากฉันต้องการการค้นหาแบบใกล้เคียงฉัน จะใช้ภูมิศาสตร์ ฉลาดขนาดนี้เลยหรอ มีข้อเสีย (นอกจากใช้พื้นที่มากกว่า ... ) หรือไม่?
Yuval A.

@ ยุววัล. นั่นฟังดูสมเหตุสมผลและอาจเป็นการประนีประนอมที่ดี สิ่งเดียวที่ฉันกังวลอยู่เหนือหัวของฉันคือการมีคอลัมน์ภูมิศาสตร์ในตารางจะมีผลกระทบต่อการค้นหากับตารางหรือไม่ฉันไม่มีประสบการณ์กับสิ่งนั้นดังนั้นคุณจะต้องทดสอบเพื่อยืนยัน
Jeff Ogata

1
เหตุใดคุณจึงอัปเดตคำถามของคุณด้วยคำถามใหม่ ๆ แทนที่จะถามคำถามใหม่
ชาด

@ ชาดไม่แน่ใจว่าคุณหมายถึงอะไร ฉันอัปเดตเนื้อหาของคำถามหนึ่งครั้งและไม่ต้องถามคำถามเพิ่มเติม
Jeff Ogata

6
เป็นที่น่าสังเกตสำหรับผู้ที่พบคำถามนี้ว่า SQL Server 2012 มีประสิทธิภาพที่เพิ่มขึ้นอย่างมากด้วยการจัดทำดัชนีเชิงพื้นที่ นอกจากนี้โปรดทราบว่าตราบใดที่คุณจัดเก็บข้อมูลตำแหน่งคุณสามารถเพิ่มข้อมูลเชิงพื้นที่ได้ในภายหลังโดยใช้บริการค้นหาเพื่อระบุพิกัดภูมิศาสตร์ที่อยู่ที่คุณจัดเก็บไว้แล้ว
Volvox

คำตอบ:


66

หากคุณวางแผนที่จะทำการคำนวณเชิงพื้นที่ EF 5.0 จะอนุญาตให้ LINQ Expressions เช่น:

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 :

หมายเหตุเกี่ยวกับช่องว่างแต่ละพิกัดจะถูกจัดเก็บเป็นตัวเลขทศนิยมที่มีความแม่นยำสองเท่าซึ่งมีความยาว 64 บิต (8 ไบต์) และค่าไบนารี 8 ไบต์นั้นเทียบเท่ากับความแม่นยำของทศนิยม 15 หลักดังนั้นการเปรียบเทียบทศนิยม (9 , 6) ซึ่งมีขนาดเพียง 5 ไบต์ไม่ใช่การเปรียบเทียบที่ยุติธรรม ทศนิยมจะต้องมีค่าต่ำสุดเป็นทศนิยม (15,12) (9 ไบต์) สำหรับแต่ละ LatLong (รวม 18 ไบต์) สำหรับการเปรียบเทียบจริง

ดังนั้นการเปรียบเทียบประเภทการจัดเก็บ:

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%

นอกจากนี้ประเภทข้อมูลภูมิศาสตร์ไม่ได้ จำกัด อยู่เพียงการจัดเก็บ Point, คุณยังสามารถจัดเก็บ LineString, CircularString, CompoundCurve รูปหลายเหลี่ยม CurvePolygon, GeometryCollection, MultiPoint, MultiLineString และ MultiPolygon และอื่น ๆ ความพยายามใด ๆ ที่จะจัดเก็บแม้แต่ประเภทภูมิศาสตร์ที่ง่ายที่สุด (เป็น Lat / Long) นอกเหนือจากจุด (เช่นอินสแตนซ์ LINESTRING (1 1, 2 2)) จะทำให้เกิดแถวเพิ่มเติมสำหรับแต่ละจุดคอลัมน์สำหรับการจัดลำดับสำหรับลำดับของแต่ละจุด และอีกคอลัมน์สำหรับการจัดกลุ่มบรรทัด SQL Server นอกจากนี้ยังมีวิธีการสำหรับชนิดข้อมูลภูมิศาสตร์ซึ่งรวมถึงการคำนวณพื้นที่เขตแดนความยาวระยะทางและอื่น ๆ

ดูเหมือนว่าจะไม่ฉลาดในการจัดเก็บ Latitude และ Longitude เป็นทศนิยมใน Sql Server

อัปเดต 2

หากคุณวางแผนที่จะทำการคำนวณใด ๆ เช่นระยะทางพื้นที่ ฯลฯ การคำนวณสิ่งเหล่านี้อย่างเหมาะสมบนพื้นผิวโลกนั้นเป็นเรื่องยาก แต่ละประเภทภูมิศาสตร์ที่เก็บไว้ใน SQL Server ยังถูกเก็บไว้กับเชิงพื้นที่รหัสทรัพย์ รหัสเหล่านี้อาจเป็นทรงกลมที่แตกต่างกัน (โลกคือ 4326) ซึ่งหมายความว่าการคำนวณใน SQL Server จะคำนวณได้อย่างถูกต้องบนพื้นผิวโลก (แทนที่จะเป็นแมลงวันซึ่งอาจผ่านพื้นผิวโลก)

ป้อนคำอธิบายภาพที่นี่


1
ในการเพิ่มข้อมูลนี้การใช้ Geography จะขยายความสามารถของการค้นหา sql อย่างแท้จริงจากคือ lat / long ระหว่าง lat / longs อื่น ๆ (โดยปกติจะเป็นเพียงแค่สี่เหลี่ยมผืนผ้า) เนื่องจากประเภทข้อมูล Geography ช่วยให้คุณสามารถสร้างพื้นที่ได้หลายขนาดและเกือบทุกขนาด
Erik Philips

1
ขอบคุณอีกครั้ง. ฉันได้ขอเหตุผลในการพิจารณาใช้geographyและคุณให้สิ่งดีๆ ในที่สุดฉันตัดสินใจใช้decimalช่องในกรณีนี้ (ดูการอัปเดตที่ยืดยาวของฉัน) แต่เป็นการดีที่จะรู้ว่าฉันสามารถใช้ได้geographyถ้าฉันต้องการทำอะไรที่น่าสนใจมากกว่าแค่การทำแผนที่พิกัด
Jeff Ogata

6

สิ่งที่ควรพิจารณาก็คือพื้นที่จัดเก็บที่ใช้ในแต่ละวิธี ประเภทภูมิศาสตร์ถูกจัดเก็บเป็นไฟล์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
หมายเหตุเกี่ยวกับช่องว่างแต่ละพิกัดจะถูกจัดเก็บเป็นตัวเลขทศนิยมที่มีความแม่นยำสองเท่าซึ่งมีความยาว 64 บิต (8 ไบต์) และค่าไบนารี 8 ไบต์นั้นเทียบเท่ากับความแม่นยำของทศนิยม 15 หลักดังนั้นการเปรียบเทียบทศนิยม (9 , 6) ซึ่งมีขนาดเพียง 5 ไบต์ไม่ใช่การเปรียบเทียบที่ยุติธรรม ทศนิยมจะต้องมีค่าทศนิยมขั้นต่ำ (15,12) (9 ไบต์) สำหรับแต่ละ LatLong (รวม 18 ไบต์) สำหรับการเปรียบเทียบจริง
Erik Philips

9
@ErikPhilips จุดคือเหตุใดจึงใช้ทศนิยม (15, 12) ในเมื่อสิ่งที่คุณต้องการคือทศนิยม (9, 6)? การเปรียบเทียบข้างต้นเป็นข้อปฏิบัติ - ไม่ใช่แบบฝึกหัดทางวิชาการ
Noel Abrahams

-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
ยินดีต้อนรับคำตอบใหม่เสมอ แต่โปรดเพิ่มบริบท การอธิบายสั้น ๆ ว่าวิธีแก้ปัญหาข้างต้นทำให้คำตอบมีประโยชน์ต่อผู้อื่นมากขึ้นอย่างไร
Leigh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.