ชนิดข้อมูลที่เหมาะสมที่สุดที่จะใช้เมื่อเก็บละติจูด / ลองจิจูดในฐานข้อมูล MySQL คืออะไร


431

จำไว้ว่าฉันจะทำการคำนวณกับคู่ lat / long ประเภทข้อมูลที่เหมาะสมที่สุดสำหรับใช้กับฐานข้อมูล MySQL?


1
ฉันพบลิงค์นี้มีประโยชน์มาก: howto-use-mysql-spatial-ext.blogspot.com/2007/11/ ......อาจเก่ากว่าเล็กน้อย แต่มีคำอธิบายที่สมบูรณ์รวมถึงตัวอย่าง
madc

Imho คนส่วนใหญ่ที่นี่ไม่เข้าใจว่าเกิดอะไรขึ้น เร็วที่สุดเท่าที่รหัสแอปสัมผัสตัวเลขให้ใครใช้คู่ (ซึ่งส่วนใหญ่ทำ) จำนวนกลายเป็นที่แม่นยำสองมากที่สุด การจัดเก็บแล้วด้วยแม้แต่หนึ่งล้านทศนิยมจะไม่ทำอะไรดี การจัดเก็บด้วยทศนิยมจำนวนจำกัด (เช่น 6) ทำลายส่วนหนึ่งของความแม่นยำนั้นและเพิ่มข้อผิดพลาดสะสมทุกครั้งที่มีการเขียนลงในฐานข้อมูลอีกครั้ง Double มีจำนวนนัยสำคัญถึง 16 หลักซึ่งอาจเป็นทศนิยมทั้งหมด การแยกออกเป็น 10 ข้อจะทำให้เกิดข้อผิดพลาดสะสมเมื่อเวลาผ่านไป มันคือ "จุดลอย" ด้วยเหตุผล ต่อเนื่อง
Stormwind

Cont: 6 ทศนิยมอาจตกลงเมื่อจัดเก็บรูปที่ได้มาจากแหล่งภายนอกไม่เปลี่ยนแปลงและเป็นครั้งแรก - เป็นแหล่งข้อมูล แต่ถ้าทำการคำนวณเพียงครั้งเดียวและเก็บอีกครั้งมันจะเป็นใบ้ที่จะลบส่วนหนึ่งของความแม่นยำโดยการบังคับใช้รูปแบบทศนิยมเฉพาะ การดำเนินการคำนวณภายในเซิร์ฟเวอร์เท่านั้นอาจแตกต่างกัน (เซิร์ฟเวอร์อาจหรืออาจไม่ใช้อย่างอื่นมากกว่าภายในเป็นสองเท่า) และการใช้ตัวเลขที่แย่กว่าในการคำนวณแอปสองเท่าของการคำนวณแอปลดความต้องการความแม่นยำในการจัดเก็บข้อมูลอย่างเท่าเทียมกัน
Stormwind

ต่อ: หากเซิร์ฟเวอร์จัดเก็บหมายเลขด้วยความแม่นยำที่สูงขึ้นแม้จะอ้างว่า "9.6" (ซึ่งฉันไม่ทราบว่าเป็นเช่นนั้น) ก็ไม่มีเรื่องใด ๆ ทั้งหมดนี้และรูปแบบนั้นเป็นเรื่องที่สะดวกสบาย - มีน้อยถึง ทำอย่างไรกับปัญหาความแม่นยำ แต่ฉันจะไม่แปลกใจถ้าเซิร์ฟเวอร์ปัดเศษตัวเลขใด ๆ เป็นความแม่นยำทศนิยม 6 รูปแบบด้วยรูปแบบนั้น
Stormwind

ต่อเนื่อง: สุดท้าย: สำหรับลาดพร้าว, lon ของทศนิยมที่ 6 เป็นเรื่องของการหักเข้าไปในแคลิฟอร์เนีย กริด 11 ซม. ทุกครั้งที่มีคนอ่าน (สัมผัส) คำนวณและจัดเก็บอีกครั้งด้วย 6 ทศนิยมจะมีการหักบัญชีใหม่ (= ข้อผิดพลาดสะสม) หากข้อผิดพลาดทั้งหมดเกิดขึ้นในทิศทางเดียวกันจะมีข้อผิดพลาดใหญ่ หากทำการคูณแบบชั่วคราวกับมัน (เช่นเพิ่มขนาดแล้วลบและลดขนาด) ก็อาจใหญ่ขึ้น อย่าทิ้งความแม่นยำโดยไม่มีช่างก่ออิฐที่ดี!
Stormwind

คำตอบ:


161

ใช้ส่วนขยายเชิงพื้นที่ของ MySQL กับ GIS


25
คุณมีลิงค์อื่น ๆ สำหรับตัวอย่างหรือข้อมูลอื่น ๆ เกี่ยวกับวิธีการเริ่มต้นใช้งานที่ดีที่สุด?
Codebeef

6
MYSQL Spatial เป็นตัวเลือกที่ดี แต่ก็ยังมีข้อ จำกัด และข้อควรจำที่สำคัญ (ณ 6) โปรดดูคำตอบของฉันด้านล่าง ...
เจมส์ Schek

1
@James Schek ถูกต้อง นอกจากนี้ MySQL ทำการคำนวณโดยใช้รูปเรขาคณิตแบบยูคลิดดังนั้นจึงไม่ได้เป็นตัวแทนกรณีการใช้งานจริงสำหรับ lat / lng
mkuech

FYI; Mysql สนับสนุนดัชนีเชิงพื้นที่เฉพาะกับตาราง * .myisam เช่นเครื่องยนต์ ISAM ลิงก์: dev.mysql.com/doc/refman/5.0/en/creating-spatial-indexes.html
PodTech.io

ดูที่บทความนี้ในตอนท้ายส่วนการปรับปรุง: mysqlserverteam.com/mysql-5-7-and-gis-an-example
Jaspal Singh เมื่อ

149

Google ให้บริการเริ่มต้นจนจบโซลูชัน PHP / MySQL สำหรับแอปพลิเคชัน "Store Locator" ด้วย Google Maps ในตัวอย่างนี้พวกเขาเก็บค่า lat / lng เป็น "ลอย" ที่มีความยาว "10,6"

http://code.google.com/apis/maps/articles/phpsqlsearch.html


11
Google ไม่เข้าใจอย่างชัดเจนว่าข้อมูลจำเพาะของ FLOAT ทำงานอย่างไร: FLOAT(10,6)ทิ้ง 4 หลักสำหรับส่วนจำนวนเต็มของพิกัด และไม่สัญญาณไม่นับซึ่งมาจากแอตทริบิวต์ (un) ที่ลงชื่อ
Alix Axel

2
แต่ถ้าคุณต้องการเก็บเป็นค่าส่วนประกอบหนึ่งจาก [0, 180] ควรมากกว่านั้นใช่ไหม?
Hrvoje Golcic

37
@AlixAxel ฉันคิดว่า Google รู้ดีว่ากำลังทำอะไรอยู่ เพราะมันระบุว่า: " ด้วยความสามารถในการซูมในปัจจุบันของ Google Maps คุณควรต้องการความแม่นยำ 6 หลักหลังจุดทศนิยมซึ่งจะทำให้ฟิลด์เก็บ 6 หลักหลังจุดทศนิยมรวมถึง 4 หลักก่อนทศนิยมเช่น- 123.456789องศา " หากได้รับการรับรองการตรวจสอบรูปแบบจะ1234,567890 ดังนั้นไม่มีปัญหา
1.44mb

16
@AlixAxel เขานับจำนวนตามลำดับ; ไม่ได้ใช้พิกัดที่แท้จริง ...
Andrew Ellis

8
การใช้ประเภทข้อมูลDoubleสำหรับ Laravel
FooBar

133

โดยทั่วไปจะขึ้นอยู่กับความแม่นยำที่คุณต้องการสำหรับตำแหน่งของคุณ เมื่อใช้ DOUBLE คุณจะมีความแม่นยำ 3.5nm DECIMAL (8,6) / (9,6) ลงไปที่ 16 ซม. FLOAT คือ 1.7 เมตร ...

ตารางที่น่าสนใจมากนี้มีรายการที่สมบูรณ์มากขึ้น: http://mysql.rjweb.org/doc.php/latlng :

Datatype               Bytes            Resolution

Deg*100 (SMALLINT)     4      1570 m    1.0 mi  Cities
DECIMAL(4,2)/(5,2)     5      1570 m    1.0 mi  Cities
SMALLINT scaled        4       682 m    0.4 mi  Cities
Deg*10000 (MEDIUMINT)  6        16 m     52 ft  Houses/Businesses
DECIMAL(6,4)/(7,4)     7        16 m     52 ft  Houses/Businesses
MEDIUMINT scaled       6       2.7 m    8.8 ft
FLOAT                  8       1.7 m    5.6 ft
DECIMAL(8,6)/(9,6)     9        16cm    1/2 ft  Friends in a mall
Deg*10000000 (INT)     8        16mm    5/8 in  Marbles
DOUBLE                16       3.5nm     ...    Fleas on a dog

หวังว่านี่จะช่วยได้


2
ฉันจำเป็นต้องเขียนคำวิจารณ์ที่สร้างสรรค์และละเอียดโดยเน้นเนื้อหาของโพสต์ดังนั้นฉันจะพูดได้ว่าในขณะที่สังเกตตารางความแม่นยำที่จัดทำจากเว็บไซต์ของ Rick James ฉันรู้สึกขบขันเล็กน้อยที่คำอธิบายความละเอียด "หมัดสุนัข" และ รู้สึกว่ามันคู่ควรกับความรุ่งโรจน์ เทคนิคการพูดนี่เป็นภาพที่เป็นประโยชน์ที่ช่วยฉันในการตัดสินใจว่าจะใช้ประเภทข้อมูลใดเมื่อจัดเก็บพิกัดเพื่อวัดระยะทางระหว่างที่อยู่สองแห่งซึ่ง @Simon ฉันอยากจะขอบคุณสำหรับการแบ่งปัน
Sam_Butler

FWIW การใช้ลิงก์ "ปรับขนาดเล็ก" ของลิงก์นั้นไม่มีประสิทธิภาพอย่างน่ากลัว คำตอบของ Oguzhanเป็นวิธีที่ยอดเยี่ยมในการเก็บยาว / lat ด้วยตัวเลข7หลักหลังจุดทศนิยมใน int 4-byte Sign ความแม่นยำสูง (~ 1 ซม.) ในขนาดเล็ก (4B)
ToolmakerSteve

74

MySQL Spatial Extensions เป็นตัวเลือกที่ดีที่สุดเพราะคุณมีรายชื่อผู้ดำเนินการเชิงพื้นที่และดัชนีทั้งหมด ดัชนีเชิงพื้นที่จะทำให้คุณสามารถคำนวณตามระยะทางได้อย่างรวดเร็ว โปรดทราบว่าตั้งแต่ 6.0 ส่วนขยายเชิงพื้นที่ยังคงไม่สมบูรณ์ ฉันไม่ได้วาง MySQL Spatial เพียงบอกให้คุณรู้ถึงข้อผิดพลาดก่อนที่คุณจะทำสิ่งนี้มากเกินไป

หากคุณจัดการกับคะแนนอย่างเคร่งครัดและมีเพียงฟังก์ชัน DISTANCE นี่เป็นเรื่องปกติ หากคุณจำเป็นต้องทำการคำนวณใด ๆ กับรูปหลายเหลี่ยมเส้นหรือบัฟเฟอร์บัฟเฟอร์คะแนนตัวดำเนินการเชิงพื้นที่จะไม่ให้ผลลัพธ์ที่แน่นอนยกเว้นว่าคุณใช้ตัวดำเนินการ "สัมพันธ์" ดูคำเตือนที่ด้านบนของ21.5.6 ความสัมพันธ์เช่นประกอบด้วยภายในหรือจุดตัดกำลังใช้ MBR ไม่ใช่รูปทรงเรขาคณิตที่แน่นอน (เช่นรูปวงรีจะถือว่าเป็นรูปสี่เหลี่ยมผืนผ้า)

นอกจากนี้ระยะทางใน MySQL Spatial ยังอยู่ในหน่วยเดียวกับเรขาคณิตแรกของคุณ ซึ่งหมายความว่าหากคุณกำลังใช้องศาทศนิยมการวัดระยะทางของคุณจะเป็นองศาทศนิยม สิ่งนี้จะทำให้ยากมากที่จะได้รับผลลัพธ์ที่แน่นอนเมื่อคุณได้รับจากเส้นศูนย์สูตร


26
การคืนค่า: ส่วนขยายเชิงพื้นที่ MySQL ไม่เหมาะสำหรับการคำนวณระยะทางวงกลมขนาดใหญ่ระหว่างจุดบนพื้นผิวโลกโดยแสดงเป็นละติจูด / ลองจิจูด ฟังก์ชันระยะทางของพวกเขา ฯลฯ มีประโยชน์เฉพาะกับคาร์ทีเซียนพลานาร์พิกัด
O. Jones

71

เมื่อฉันทำสิ่งนี้สำหรับฐานข้อมูลการนำทางที่สร้างขึ้นจาก ARINC424 ฉันทำการทดสอบและดูรหัสฉันใช้ DecIMAL (18,12) (จริง ๆ แล้วเป็นตัวเลข (18,12) เพราะเป็น firebird)

การลอยและการดับเบิลไม่แม่นยำและอาจส่งผลให้เกิดข้อผิดพลาดในการปัดเศษซึ่งอาจเป็นสิ่งที่เลวร้ายมาก ฉันจำไม่ได้ว่าฉันพบข้อมูลจริงที่มีปัญหาหรือไม่ แต่ฉันค่อนข้างมั่นใจว่าการไม่สามารถจัดเก็บอย่างถูกต้องในแบบลอยหรือแบบสองครั้งอาจทำให้เกิดปัญหา

ประเด็นคือเมื่อใช้องศาหรือเรเดียนเรารู้ช่วงของค่า - และส่วนที่เป็นเศษส่วนต้องการตัวเลขมากที่สุด

MySQL เชิงพื้นที่ส่วนขยายเป็นทางเลือกที่ดีเพราะพวกเขาทำตามopengis เรขาคณิตรุ่น ฉันไม่ได้ใช้เพราะฉันต้องการให้ฐานข้อมูลของฉันพกพาได้


3
ขอบคุณสิ่งนี้มีประโยชน์ รู้สึกแปลก ๆ ที่ได้อ่านคำถามและคำตอบทั้งหมดจากปี 2008 เมื่อ 8 ปีที่แล้ว
aexl

1
@ TheSexiestManinJamaica - ก่อน IEEE 754-1985 ฮาร์ดแวร์จุดลอยตัวคอมพิวเตอร์วุ่นวาย มีแม้กระทั่งบนเครื่องที่a*bไม่เท่ากันb*a(สำหรับบางค่า) มีตัวอย่างมากมายที่ต้องการ: 2+2 = 3.9999. มาตรฐานทำความสะอาดเป็นจำนวนมากและมีการใช้อย่างรวดเร็วโดยฮาร์ดแวร์และซอฟต์แวร์ทุกชิ้น การสนทนานี้มีผลใช้ได้ไม่เพียง แต่ตั้งแต่ปี 2008 แต่สำหรับหนึ่งในสามของศตวรรษ
Rick James

42

ขึ้นอยู่กับความแม่นยำที่คุณต้องการ

Datatype           Bytes       resolution
------------------ -----  --------------------------------
Deg*100 (SMALLINT)     4  1570 m    1.0 mi  Cities
DECIMAL(4,2)/(5,2)     5  1570 m    1.0 mi  Cities
SMALLINT scaled        4   682 m    0.4 mi  Cities
Deg*10000 (MEDIUMINT)  6    16 m     52 ft  Houses/Businesses
DECIMAL(6,4)/(7,4)     7    16 m     52 ft  Houses/Businesses
MEDIUMINT scaled       6   2.7 m    8.8 ft
FLOAT                  8   1.7 m    5.6 ft
DECIMAL(8,6)/(9,6)     9    16cm    1/2 ft  Friends in a mall
Deg*10000000 (INT)     8    16mm    5/8 in  Marbles
DOUBLE                16   3.5nm     ...    Fleas on a dog

จาก: http://mysql.rjweb.org/doc.php/latlng

เพื่อสรุป:

  • ตัวเลือกที่แม่นยำที่สุดคือ DOUBLEแม่นยำมากที่สุดคือตัวเลือกที่ใช้ได้
  • ประเภทที่พบมากที่สุดที่ใช้คือ DECIMAL(8,6)/(9,6)ส่วนใหญ่ที่พบบ่อยชนิดเห็นใช้

ในฐานะของMySQL 5.7พิจารณาใช้Spatial Data Types (SDT) โดยเฉพาะPOINTสำหรับการจัดเก็บพิกัดเดียว ก่อนหน้า 5.7, SDT ไม่รองรับดัชนี (ยกเว้น 5.6 เมื่อประเภทตารางคือ MyISAM)

บันทึก:

  • เมื่อใช้งาน POINTPOINT(latitude, longitude)ระดับคำสั่งของข้อโต้แย้งพิกัดการจัดเก็บจะต้อง
  • มีไวยากรณ์พิเศษสำหรับ การสร้างดัชนีเชิงพื้นที่
  • ประโยชน์ที่ใหญ่ที่สุดของการใช้ SDT คือคุณสามารถเข้าถึงฟังก์ชั่นการวิเคราะห์เชิงพื้นที่เช่นการคำนวณระยะทางระหว่างจุดสองจุด ( ST_Distance) และการพิจารณาว่ามีจุดหนึ่งอยู่ภายในพื้นที่อื่นหรือไม่ ( ST_Contains)

2
คุณคัดลอกส่วนที่วางไว้ของคำตอบก่อนหน้าและ "สรุป" กับสิ่งที่ผู้ชายที่สร้างตารางนั้นไม่แนะนำ : « How to PARTITION? MySQL เป็นคนพิถีพิถัน ดังนั้น FLOAT / DOUBLE จึงออกมา DECIMAL ออกแล้ว ดังนั้นเราติดอยู่กับกระบองบางตัว โดยพื้นฐานแล้วเราจำเป็นต้องแปลง Lat / Lng เป็นขนาด INT และใช้ PARTITION BY RANGE » และ«ลอยมี 24 บิตที่สำคัญ; DOUBLE มี 53. (ไม่ทำงานกับการแบ่งพาร์ติชัน แต่รวมไว้เพื่อความสมบูรณ์) บ่อยครั้งที่ผู้คนใช้ DOUBLE โดยไม่ทราบว่ามันเกินราคามากแค่ไหน
Armfoot

1
@Armfoot ถ้าคุณดูเวลาของการแก้ไขมันเป็นคำตอบอื่น ๆ ที่คัดลอกมาจากฉัน ไม่สำคัญ: ฉันเห็น Stack Overflow มากกว่า "โน้ตสำหรับอนาคตฉัน"
Gajus

1
ไม่เขาไม่ได้คัดลอกมาจากคุณเขาเพิ่งวางตารางอย่างที่คุณทำจากลิงค์ที่เขาอ้างถึงในปี 2014 (โพสต์ของคุณมาจากปี 2015) Btw ฉันคิดว่าคุณสะกดคำว่า "พิเศษ" เมื่อคุณเชื่อมโยงประเภทข้อมูลเชิงพื้นที่ ส่วนที่คุณเขียนนี้มีประโยชน์จริง ๆ สำหรับผู้ที่ต้องการเริ่มใช้พวกเขาหากคุณเพิ่มตัวอย่างเพิ่มเติมเช่นCREATE TABLE geom (g GEOMETRY NOT NULL, SPATIAL INDEX(g)) ENGINE=MyISAM;และคำเตือนเกี่ยวกับข้อ จำกัด ของ SDT ดังที่เจมส์พูดถึงบางทีคำตอบของคุณอาจจะกระชับและแม่นยำมากขึ้นในการช่วยเหลือผู้อื่นเช่นกัน ..
Armfoot

@Gajus - ฉันรู้สึกเป็นเกียรติที่คุณสองคนพบเอกสารของฉัน! (ไม่ฉันไม่ทราบว่าหมัดมีขนาดใหญ่เพียงใด แต่ฉันรู้สึกว่ามันจะได้รับความสนใจจากใครบางคน)
ริคเจมส์

เมื่อใช้คลาส POINT ลำดับของอาร์กิวเมนต์สำหรับการจัดเก็บพิกัดต้องเป็น POINT (ลองจิจูด / X, ละติจูด / Y)
AndreyP


19

ใช้DECIMAL(8,6)สำหรับละติจูด (90 ถึง -90 องศา) และDECIMAL(9,6)ลองจิจูด (180 ถึง -180 องศา) ทศนิยม 6 ตำแหน่งถือว่าใช้ได้สำหรับแอปพลิเคชันส่วนใหญ่ ทั้งสองควรเป็น "เซ็นชื่อ" เพื่ออนุญาตให้มีค่าลบ


DECIMALtype มีไว้สำหรับการคำนวณทางการเงินที่ไม่floor/ceilยอมรับ ธรรมดาอย่างมีนัยสำคัญมีประสิทธิภาพเหนือกว่าFLOAT DECIMAL
Kondybas

1
@Kondybas - เนื่องจากต้นทุนหลักในฐานข้อมูลกำลังดึงแถวความแตกต่างของประสิทธิภาพระหว่างทศนิยมและทศนิยมจึงไม่น่าเป็นห่วง
Rick James

14

ไม่จำเป็นต้องไปไกลเท่าไหร่ตาม Google Maps ที่ดีที่สุดคือ FLOAT (10,6) สำหรับ lat และ lng


คุณได้รับข้อมูลนี้ที่ไหนฉันหามันไม่เจอ? ในกรณีที่มีการเปลี่ยนแปลงบางอย่าง
webfacer

1
@webfacer อยู่ในส่วน "การสร้างตารางใน MySQL" ที่นี่: developers.google.com/maps/documentation/javascript/…เช่น lat FLOAT( 10, 6 ) NOT NULL, lng FLOAT( 10, 6 ) NOT NULL
turrican_34

1
@webfacer, ดูเหมือนว่าไวยากรณ์จะเลิกเป็นของFLOAT mysql 8.0.17ตอนนี้ Mysql แนะนำให้ใช้FLOATโดยไม่ต้องมีพารามิเตอร์ความแม่นยำใด ๆdev.mysql.com/doc/refman/8.0/th/numeric-type-overview.htmlและdev.mysql.com/doc/refman/5.5/th/floating-point- types.html
turrican_34

7

เราเก็บละติจูด / ลองจิจูด X 1,000,000 ไว้ในฐานข้อมูล oracle ของเราเป็นตัวเลขเพื่อหลีกเลี่ยงข้อผิดพลาดในการปัดเศษเป็นสองเท่า

เนื่องจากละติจูด / ลองจิจูดนั้นไปยังตำแหน่งทศนิยมที่ 6 นั้นมีความแม่นยำ 10 ซม. ซึ่งเป็นสิ่งที่เราต้องการ ฐานข้อมูลอื่น ๆ จำนวนมากยังเก็บ lat / long เป็นทศนิยมลำดับที่ 6


2
การคูณด้วยจำนวนมาก (เช่นล้าน) นั้นยอดเยี่ยมถ้าคุณมีข้อมูลจำนวนมากเนื่องจากการดำเนินการจำนวนเต็ม (เช่นการดึงดัชนี) จะเร็วกว่าการลอย
Kaitlin Duck Sherwood

@KaitlinDuckSherwood - บิตเป็นบิต - ฉันไม่ทราบว่าด้วยเหตุผลใด ๆ การลอยตัวแบบ 32 บิตจะช้าลงสำหรับการดึง (ทำดัชนีหรืออย่างอื่น) กว่าจำนวนเต็ม 32 บิต แม้แต่คณิตศาสตร์ลอยตัวในทุกวันนี้ก็ยังเร็วพอที่จะไม่ใช่ประเด็น อย่างไรก็ตามฉันเห็นด้วยกับความคิดเห็นที่จะใช้ตัวคูณโดยนัยกับจำนวนเต็ม: มันเพิ่มความแม่นยำที่คุณได้รับจาก 32 บิต การพิสูจน์ตัวจริงในอนาคตเมื่อเทคโนโลยีดีขึ้น
ToolmakerSteve

6

ในมุมมองที่แตกต่างและเรียบง่ายโดยสิ้นเชิง:

  • หากคุณใช้ Google เพื่อแสดงแผนที่เครื่องหมายรูปหลายเหลี่ยมไม่ว่าอะไรก็ตามให้ Google ทำการคำนวณให้เสร็จ!
  • คุณประหยัดทรัพยากรบนเซิร์ฟเวอร์ของคุณและคุณสามารถเก็บละติจูดและลองจิจูดไว้ด้วยกันเป็นสตริงเดียว ( VARCHAR) เช่น: " -0000.0000001, -0000.000000000000001 " (ความยาว 35 และถ้าตัวเลขมีทศนิยมมากกว่า 7 หลักมันจะถูกปัดเศษ);
  • หาก Google ส่งกลับตัวเลขทศนิยมมากกว่า 7 หลักต่อหนึ่งหมายเลขคุณสามารถรับข้อมูลนั้นเก็บไว้ในสตริงของคุณได้ในกรณีที่คุณต้องการตรวจหาflees หรือจุลินทรีย์ในอนาคต ;
  • คุณสามารถใช้เมทริกซ์ระยะทางได้หรือไลบรารีรูปทรงเรขาคณิตของพวกเขาสำหรับการคำนวณระยะทางหรือตรวจจับจุดในบางพื้นที่ด้วยการโทรง่ายๆเช่นนี้:google.maps.geometry.poly.containsLocation(latLng, bermudaTrianglePolygon))
  • มี API "ฝั่งเซิร์ฟเวอร์" มากมายที่คุณสามารถใช้ได้ (ในPython , Ruby on Rails , PHP , CodeIgniter , Laravel , Yii , Zend Frameworkและอื่น ๆ ) ที่ใช้ Google Maps API

วิธีนี้คุณไม่จำเป็นต้องกังวลเกี่ยวกับการจัดทำดัชนีตัวเลขและปัญหาอื่น ๆ ที่เกี่ยวข้องกับชนิดข้อมูลที่อาจทำให้ค่าพิกัดของคุณแย่ลง


ไม่ดี. OP กล่าวว่าเขาจะทำการคำนวณกับคู่ lat / lng - คำตอบของคุณตัดออกไป
Yarin

4
@Yarin นี่เป็นคำถามยอดนิยมที่ผู้คนเพียงไม่กี่คน (หรือมาก) ต้องการคำตอบเกี่ยวกับวิธีการจัดเก็บพิกัดตามความต้องการของตัวเอง (จำนวนมากอาจใช้ Google Maps) downvote ของคุณแนะนำว่าคำตอบนี้อาจไม่ช่วยพวกเขา ... โดยการเก็บพิกัดในสตริงพวกเขาจะรู้ว่าค่าดั้งเดิมที่ให้ไว้กับพวกเขา (เช่น: โดย Google) ซึ่งจะช่วยพวกเขาในภายหลังหากพวกเขาตัดสินใจที่จะพัฒนา แอพของตัวเองและทำการคำนวณกับมัน ในเวลานั้นพวกเขาจะยังคงมีข้อมูลดิบดั้งเดิมเพียงเพราะพวกเขาไม่ได้สับสนกับการแปลง
เท้า

4

ฉันแนะนำให้ใช้ FLOAT (9,6)

แป้นเชิงพื้นที่จะให้คุณสมบัติเพิ่มเติมแก่คุณ แต่ด้วยมาตรฐานการผลิตทำให้การลอยนั้นเร็วกว่าแป้นเชิงพื้นที่มาก (0,01 VS 0,001 ใน AVG)


1
คุณช่วยให้รายละเอียดผลการทดสอบกับที่นี่ได้ไหม?
NameNotFoundException

4

MySQL ใช้ double สำหรับ float ทั้งหมดดังนั้นใช้ type double การใช้โฟลตจะทำให้ค่าที่ปัดเศษไม่สามารถคาดการณ์ได้ในสถานการณ์ส่วนใหญ่


1
MySQL การดำเนินงานในการดำเนินการ DOUBLEMySQL ช่วยให้คุณสามารถจัดเก็บข้อมูลที่เป็นทั้ง 4 ไบต์FLOATหรือ DOUBLE8 ดังนั้นจึงมีความเป็นไปได้ที่จะสูญเสียความแม่นยำเมื่อจัดเก็บนิพจน์ลงในFLOATคอลัมน์
Rick James

4

แม้ว่ามันจะไม่เหมาะสำหรับการใช้งานทั้งหมด แต่หากคุณกำลังทำแผ่นแผนที่หรือทำงานกับเครื่องหมายจำนวนมาก (จุด) ด้วยการฉายเพียงครั้งเดียว (เช่น Mercator เช่น Google แผนที่และกรอบแผนที่ลื่น ๆ อื่น ๆ ) ฉันได้พบสิ่งที่ ฉันเรียกว่า "Vast Coordinate System" จะสะดวกและมีประโยชน์จริงๆ โดยทั่วไปคุณเก็บพิกัด x และ y pixel ในบางวิธีการซูมเข้า - ฉันใช้ระดับการซูม 23 สิ่งนี้มีประโยชน์หลายประการ:

  • คุณใช้ lat / lng แพงในการแปลงพิกเซล Mercator หนึ่งครั้งแทนที่จะเป็นทุกครั้งที่คุณจัดการ
  • การรับไทล์ประสานงานจากบันทึกที่ระดับการซูมใช้เวลาเลื่อนไปทางขวาหนึ่งครั้ง
  • การได้รับพิกัดพิกเซลจากระเบียนใช้เวลาหนึ่งกะขวาและหนึ่งบิตและ
  • การเปลี่ยนแปลงนั้นมีน้ำหนักเบามากซึ่งเป็นจริงที่จะทำได้ใน SQL ซึ่งหมายความว่าคุณสามารถทำ DISTINCT เพื่อส่งกลับได้เพียงหนึ่งระเบียนต่อตำแหน่งพิกเซลซึ่งจะลดจำนวนระเบียนที่ส่งคืนโดยแบ็กเอนด์ซึ่งหมายถึงการประมวลผลที่น้อยลง ส่วนหน้า

ฉันพูดถึงสิ่งนี้ทั้งหมดในโพสต์บล็อกล่าสุด: http://blog.webfoot.com/2013/03/12/optimizing-map-tile-generation/


4

ฉันประหลาดใจอย่างมากกับคำตอบ / ความคิดเห็น

ทำไมบนโลกนี้ใคร ๆ ก็เต็มใจที่จะ "ลดความแม่นยำ" ล่วงหน้าอย่างสมัครใจและจากนั้นทำการคำนวณจำนวนที่แย่กว่านั้นในภายหลัง ฟังดูโง่ที่สุด

หากแหล่งกำเนิดมีความแม่นยำ 64 บิตแน่นอนว่ามันจะเป็นใบ้ที่จะทำการปรับสเกลโดยสมัครใจเช่น 6 ทศนิยมและจำกัดความแม่นยำไว้ที่ 9 Digts ที่สำคัญสูงสุด (ซึ่งเกิดขึ้นกับรูปแบบทศนิยม 9.6 ที่นำเสนอโดยทั่วไป)

ตามธรรมชาติแล้วหนึ่งเก็บข้อมูลด้วยความแม่นยำที่วัสดุแหล่งที่มามี เหตุผลเดียวในการลดความแม่นยำก็คือพื้นที่จัดเก็บที่ จำกัด

  • จัดเก็บตัวเลขต้นฉบับด้วยความแม่นยำดั้งเดิม
  • ตัวเลขร้านค้าที่คำนวณจากแหล่งที่มาในความแม่นยำการคำนวณที่เกิดขึ้น (เช่นหากรหัส aplication ใช้คู่ผสมเก็บผลลัพธ์เป็นคู่)

รูปแบบ 9.6- ทศนิยมทำให้เกิดปรากฏการณ์ snap-to-grid นั่นควรจะเป็นขั้นตอนสุดท้ายถ้ามันเกิดขึ้น

ฉันจะไม่เชิญข้อผิดพลาดสะสมในรังของฉัน


2
เพราะเครื่องมือและแอพพลิเคชั่น GPS ส่วนใหญ่นั้นมีความแม่นยำถึงทศนิยมเพียง 6 ตำแหน่งเท่านั้น ไม่มีจุดหมายในการจัดเก็บข้อมูลให้มีความแม่นยำมากกว่าเครื่องมือที่สามารถวัดgis.stackexchange.com/questions/8650/…
Yarin

1
@Yarin ใช่แน่นอน แต่คุณพูดถึงการวัดและ GPS ซึ่งไม่ได้กล่าวถึงในคำถาม แน่นอนที่สุดมีตัวเลขที่ถูกต้องมากขึ้น แต่ให้พิจารณา GPS; พูดชุดข้อมูลต้นฉบับของ 64- บิตลอยที่มีอยู่แล้วไม่ถูกต้อง 6 ทศนิยมหมายถึงการหักล้างละติจูดไปยังแคลิฟอร์เนียที่ใกล้ที่สุด 11 เซนติเมตร ดังนั้นด้วยการจัดเก็บข้อมูลเท่านั้น (ด้วย 6 ทศนิยม) ตอนนี้คุณเปิดขึ้นสำหรับความไม่ถูกต้อง22 ซม. ที่อาจเกิดขึ้น(ถ้าเดิม 11 ซม. เกินไป) โดยสมัครใจอาจจะทำการคำนวณแบบ 64 บิตก่อนที่จะเก็บเป็นครั้งที่ 3 - ตอนนี้หน้าต่างที่ไม่ถูกต้อง 33 ซม. + -16 ซม. ฟังดูเป็นคนโง่
Stormwind

@ Rick James ฉันน่าจะเก็บเป็น 64- บิตเช่น 0.3333333333333333 เราพูดถึง geodata ใช่ไหม "1/3" ไม่ค่อยปรากฏในธรรมชาติที่มีการวัดสิ่งต่าง ๆ ด้วยความแม่นยำที่สมเหตุสมผล
Stormwind

4

TL; DR

ใช้ FLOAT (8,5) หากคุณไม่ได้ทำงานในองค์การนาซ่า / กองทัพและไม่ได้สร้างระบบนำทางด้วยเครื่องบิน


ในการตอบคำถามของคุณอย่างเต็มที่คุณต้องพิจารณาหลายสิ่ง:

รูปแบบ

  • องศานาทีวินาที : 40 ° 26 ′46″ N 79 ° 58′ 56″ W
  • องศาทศนิยมนาที : 40 ° 26.767 ′N 79 ° 58.933′ W
  • ทศนิยม 1 องศา : 40.446 ° N 79.982 ° W
  • ทศนิยมองศา 2 : -32.60875, 21.27812
  • รูปแบบโฮมเมดอื่น ๆ ไม่มีใครห้ามคุณจากการสร้างระบบพิกัดที่บ้านเป็นศูนย์กลางของคุณเองและเก็บไว้เป็นส่วนหัวและระยะทางจากบ้านของคุณ นี่อาจสมเหตุสมผลสำหรับปัญหาเฉพาะบางประการที่คุณกำลังทำอยู่

ดังนั้นส่วนแรกของคำตอบคือ - คุณสามารถเก็บพิกัดในรูปแบบที่แอปพลิเคชันของคุณใช้เพื่อหลีกเลี่ยงการแปลงค่ากลับไปกลับมาและทำการสืบค้น SQL ที่ง่ายขึ้น

ส่วนใหญ่คุณอาจใช้ Google Maps หรือ OSM เพื่อแสดงข้อมูลของคุณและ GMaps ใช้รูปแบบ "ทศนิยมองศา 2" ดังนั้นจะง่ายกว่าในการจัดเก็บพิกัดในรูปแบบเดียวกัน

ความแม่นยำ

จากนั้นคุณต้องการกำหนดความแม่นยำที่คุณต้องการ แน่นอนคุณสามารถเก็บพิกัดเช่น "-32.608697550570334,21.278081997935146" แต่คุณเคยสนใจเรื่องมิลลิเมตรในขณะนำทางไปยังจุดใดบ้าง หากคุณไม่ได้ทำงานใน NASA และไม่ทำดาวเทียมหรือจรวดหรือวิถีการเคลื่อนที่คุณควรใช้ความแม่นยำในระดับหลายเมตร

รูปแบบที่ใช้กันทั่วไปคือ 5 หลักหลังจากจุดที่ให้ความแม่นยำ 50 ซม.

ตัวอย่าง : มีระยะห่างระหว่าง 1cm x, 21.278081 8และ X, 21.278081 9 ดังนั้น 7 หลักหลังจากจุดให้คุณความแม่นยำ 1/2 ซม. และ 5 หลักหลังจากจุดจะให้ความแม่นยำ 1/2 เมตร (เพราะระยะทางที่น้อยที่สุดระหว่างจุดที่แตกต่างคือ 1m ดังนั้นข้อผิดพลาดในการปัดเศษไม่เกินครึ่งหนึ่ง) เพื่อจุดประสงค์ทางแพ่งส่วนใหญ่ควรจะเพียงพอ

รูปแบบทศนิยมทศนิยมองศา (40 ° 26.767 ′N 79 ° 58.933′ W) ให้ความแม่นยำเหมือนกับจุด 5 หลักหลังจุด

พื้นที่เก็บข้อมูลที่มีประสิทธิภาพ

หากคุณเลือกรูปแบบทศนิยมแล้วพิกัดของคุณคือคู่ (-32.60875, 21.27812) เห็นได้ชัดว่า 2 x (1 บิตสำหรับการลงชื่อ 2 หลักสำหรับองศาและ 5 หลักสำหรับเลขชี้กำลัง) จะเพียงพอ

ดังนั้นที่นี่ฉันต้องการสนับสนุน Alix Axelจากความคิดเห็นที่บอกว่าคำแนะนำของ Google เพื่อเก็บไว้ใน FLOAT (10,6) นั้นพิเศษจริงๆเพราะคุณไม่ต้องการตัวเลข 4 หลักสำหรับส่วนหลัก (เนื่องจากเครื่องหมายถูกแยกและละติจูดมี จำกัด ถึง 90 และลองจิจูด จำกัด ที่ 180) คุณสามารถใช้ FLOAT (8,5) สำหรับความแม่นยำ 1 / 2m หรือ FLOAT (9,6) สำหรับความแม่นยำ 50/2 ซม. หรือคุณสามารถจัดเก็บ lat และ long แยกประเภทได้เนื่องจาก FLOAT (7,5) เพียงพอสำหรับ lat ดู MySQL ประเภทลอยอ้างอิง ใด ๆ ของพวกเขาจะเป็นเหมือนปกติลอยและเท่ากับ 4 ไบต์ต่อไป

โดยปกติพื้นที่ไม่ได้เป็นปัญหาในปัจจุบัน แต่ถ้าคุณต้องการเพิ่มประสิทธิภาพการจัดเก็บจริง ๆ ด้วยเหตุผลบางอย่าง (ข้อจำกัดความรับผิดชอบ: อย่าทำการเพิ่มประสิทธิภาพล่วงหน้า) คุณอาจบีบอัด lat (ไม่เกิน 91,000 ค่า + เครื่องหมาย) + ยาว (ไม่มากกว่า 181,000 ค่า + เครื่องหมาย) ถึง 21 บิตซึ่งน้อยกว่า 2xFLOAT อย่างมีนัยสำคัญ (8 ไบต์ == 64 บิต)


3

ฟังก์ชั่นเชิงพื้นที่ใน PostGIS นั้นมีฟังก์ชั่นมากกว่า (เช่นไม่ จำกัด การทำงานของ BBOX) มากกว่าฟังก์ชั่นเชิงพื้นที่ MySQL ลองดูสิ: ลิงค์ข้อความ


1
  1. ละติจูดอยู่ในช่วง -90 ถึง +90 (องศา) ดังนั้น DECIMAL (10, 8) จึงใช้ได้

  2. ลองจิจูดที่มีช่วงตั้งแต่ -180 ถึง +180 (องศา) ดังนั้นคุณต้องมี DECIMAL (11, 8)

หมายเหตุ: หมายเลขแรกคือจำนวนหลักทั้งหมดที่จัดเก็บและที่สองคือหมายเลขหลังจุดทศนิยม

ในระยะสั้น: lat DECIMAL(10, 8) NOT NULL, lng DECIMAL(11, 8) NOT NULL



-2

การคำนวณ Lat Long ต้องการความแม่นยำดังนั้นใช้ทศนิยมบางประเภทและทำให้ความแม่นยำสูงกว่าตัวเลขที่คุณจะเก็บไว้อย่างน้อย 2 เพื่อทำการคำนวณทางคณิตศาสตร์ ฉันไม่ทราบเกี่ยวกับประเภทข้อมูล sql ของฉัน แต่ใน SQL Server คนมักจะใช้ float หรือ real แทนที่จะเป็นทศนิยมและมีปัญหาเพราะสิ่งเหล่านี้เป็นตัวเลขโดยประมาณไม่ใช่ของจริง ดังนั้นตรวจสอบให้แน่ใจว่าชนิดข้อมูลที่คุณใช้เป็นทศนิยมจริงไม่ใช่ประเภททศนิยมแบบลอยตัวและคุณควรจะปรับ


1
ทั้งประเภททศนิยมและทศนิยมมีสถานที่ของพวกเขา ตามกฎของหัวแม่มือ, ลอยหมายถึงตัวแปรทางกายภาพและทศนิยมสำหรับหน่วยงานนับได้ (ส่วนใหญ่เงิน) ฉันไม่เห็นสาเหตุที่คุณต้องการทศนิยมสำหรับละติจูด / ลองจิจูด
Javier

1
ฉันยังคิดว่าโฟลตนั้นใช้ได้สำหรับ lat / long อย่างน้อยใน SQL Server (4bytes, 7 หลัก)
Dragoljub Ćurčić

โฟลทไม่แน่นอนมันเป็นที่คาดกันทะเลสาบแห่งความถูกต้องใน lat ยาวเป็นอันตรายถึงชีวิต! มันอาจนำคุณไปสู่จุดที่แตกต่างอย่างสิ้นเชิงบนโลกใบนี้
HLGEM

2
ข้อผิดพลาดสูงสุดของประเภทข้อมูลลอยอยู่ในระดับต่ำพอที่จะไม่เป็นปัญหา ฉันหมายความว่าคุณจะต้องตระหนักถึงข้อผิดพลาดการคูณ / การสะสมด้วยการใช้งานทั้งสองอยู่แล้ว
Spidey

@HLGEM - การปัดเศษทศนิยมจำนวนหนึ่งทำให้คุณอยู่ในจุดที่แตกต่างกันในโลก คำถามคือจุดที่แตกต่างกันอยู่ใกล้มากหรือไม่
Rick James

-3

A FLOATควรให้ความแม่นยำทั้งหมดที่คุณต้องการและจะดีกว่าสำหรับฟังก์ชั่นการเปรียบเทียบกว่าการจัดเก็บพิกัดแต่ละตัวเป็นสตริงหรือสิ่งที่คล้ายกัน

หาก MySQL รุ่นของคุณก่อนหน้านี้กว่า 5.0.3 คุณอาจจำเป็นต้องใช้ระวังบางข้อผิดพลาดการเปรียบเทียบจุดลอยอย่างไร

ก่อนหน้า MySQL 5.0.3 คอลัมน์ DECIMAL จะเก็บค่าด้วยความแม่นยำที่แน่นอนเพราะแสดงเป็นสตริง แต่การคำนวณค่า DECIMAL นั้นทำได้โดยใช้การดำเนินการทศนิยม จาก 5.0.3, MySQL ดำเนินการ DECIMAL ด้วยความแม่นยำของตัวเลขทศนิยม 64 หลักซึ่งควรแก้ปัญหาความไม่ถูกต้องที่พบบ่อยที่สุดเมื่อมันมาถึงคอลัมน์ DECIMAL


2
คุณต้องมีประเภทข้อมูลพิกัดละติจูด / ลองจิจูดจริงเพื่อการคำนวณทางคณิตศาสตร์ที่ง่าย ลองนึกภาพความสะดวกสบายของสิ่งต่าง ๆ เช่นเทียบเท่า "select * จากร้านค้าที่ระยะทาง (stores.location, mylocation) <5 ไมล์"
Kirk Strauser

1
ไม่เคยได้ยินเรื่องส่วนขยายของอวกาศมาก่อนซึ่งฟังดูสบายดีเมื่อก่อนหน้านี้เคยทำงานกับแอพที่สืบทอดมาซึ่งมีการคำนวณทางภูมิศาสตร์ค่อนข้างน้อยต้องลองดู
ConroyP

@ConroyP - ไม่คำพูดนั้นชี้ให้เห็นว่าDECIMALมีข้อผิดพลาดบางอย่าง (ก่อนหน้า 5.0.3) เนื่องจากการใช้การลอยตัว
Rick James
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.