จำไว้ว่าฉันจะทำการคำนวณกับคู่ lat / long ประเภทข้อมูลที่เหมาะสมที่สุดสำหรับใช้กับฐานข้อมูล MySQL?
จำไว้ว่าฉันจะทำการคำนวณกับคู่ lat / long ประเภทข้อมูลที่เหมาะสมที่สุดสำหรับใช้กับฐานข้อมูล MySQL?
คำตอบ:
ใช้ส่วนขยายเชิงพื้นที่ของ MySQL กับ GIS
Google ให้บริการเริ่มต้นจนจบโซลูชัน PHP / MySQL สำหรับแอปพลิเคชัน "Store Locator" ด้วย Google Maps ในตัวอย่างนี้พวกเขาเก็บค่า lat / lng เป็น "ลอย" ที่มีความยาว "10,6"
FLOAT(10,6)
ทิ้ง 4 หลักสำหรับส่วนจำนวนเต็มของพิกัด และไม่สัญญาณไม่นับซึ่งมาจากแอตทริบิวต์ (un) ที่ลงชื่อ
Double
สำหรับ Laravel
โดยทั่วไปจะขึ้นอยู่กับความแม่นยำที่คุณต้องการสำหรับตำแหน่งของคุณ เมื่อใช้ 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
หวังว่านี่จะช่วยได้
MySQL Spatial Extensions เป็นตัวเลือกที่ดีที่สุดเพราะคุณมีรายชื่อผู้ดำเนินการเชิงพื้นที่และดัชนีทั้งหมด ดัชนีเชิงพื้นที่จะทำให้คุณสามารถคำนวณตามระยะทางได้อย่างรวดเร็ว โปรดทราบว่าตั้งแต่ 6.0 ส่วนขยายเชิงพื้นที่ยังคงไม่สมบูรณ์ ฉันไม่ได้วาง MySQL Spatial เพียงบอกให้คุณรู้ถึงข้อผิดพลาดก่อนที่คุณจะทำสิ่งนี้มากเกินไป
หากคุณจัดการกับคะแนนอย่างเคร่งครัดและมีเพียงฟังก์ชัน DISTANCE นี่เป็นเรื่องปกติ หากคุณจำเป็นต้องทำการคำนวณใด ๆ กับรูปหลายเหลี่ยมเส้นหรือบัฟเฟอร์บัฟเฟอร์คะแนนตัวดำเนินการเชิงพื้นที่จะไม่ให้ผลลัพธ์ที่แน่นอนยกเว้นว่าคุณใช้ตัวดำเนินการ "สัมพันธ์" ดูคำเตือนที่ด้านบนของ21.5.6 ความสัมพันธ์เช่นประกอบด้วยภายในหรือจุดตัดกำลังใช้ MBR ไม่ใช่รูปทรงเรขาคณิตที่แน่นอน (เช่นรูปวงรีจะถือว่าเป็นรูปสี่เหลี่ยมผืนผ้า)
นอกจากนี้ระยะทางใน MySQL Spatial ยังอยู่ในหน่วยเดียวกับเรขาคณิตแรกของคุณ ซึ่งหมายความว่าหากคุณกำลังใช้องศาทศนิยมการวัดระยะทางของคุณจะเป็นองศาทศนิยม สิ่งนี้จะทำให้ยากมากที่จะได้รับผลลัพธ์ที่แน่นอนเมื่อคุณได้รับจากเส้นศูนย์สูตร
เมื่อฉันทำสิ่งนี้สำหรับฐานข้อมูลการนำทางที่สร้างขึ้นจาก ARINC424 ฉันทำการทดสอบและดูรหัสฉันใช้ DecIMAL (18,12) (จริง ๆ แล้วเป็นตัวเลข (18,12) เพราะเป็น firebird)
การลอยและการดับเบิลไม่แม่นยำและอาจส่งผลให้เกิดข้อผิดพลาดในการปัดเศษซึ่งอาจเป็นสิ่งที่เลวร้ายมาก ฉันจำไม่ได้ว่าฉันพบข้อมูลจริงที่มีปัญหาหรือไม่ แต่ฉันค่อนข้างมั่นใจว่าการไม่สามารถจัดเก็บอย่างถูกต้องในแบบลอยหรือแบบสองครั้งอาจทำให้เกิดปัญหา
ประเด็นคือเมื่อใช้องศาหรือเรเดียนเรารู้ช่วงของค่า - และส่วนที่เป็นเศษส่วนต้องการตัวเลขมากที่สุด
MySQL เชิงพื้นที่ส่วนขยายเป็นทางเลือกที่ดีเพราะพวกเขาทำตามopengis เรขาคณิตรุ่น ฉันไม่ได้ใช้เพราะฉันต้องการให้ฐานข้อมูลของฉันพกพาได้
a*b
ไม่เท่ากันb*a
(สำหรับบางค่า) มีตัวอย่างมากมายที่ต้องการ: 2+2 = 3.9999
. มาตรฐานทำความสะอาดเป็นจำนวนมากและมีการใช้อย่างรวดเร็วโดยฮาร์ดแวร์และซอฟต์แวร์ทุกชิ้น การสนทนานี้มีผลใช้ได้ไม่เพียง แต่ตั้งแต่ปี 2008 แต่สำหรับหนึ่งในสามของศตวรรษ
ขึ้นอยู่กับความแม่นยำที่คุณต้องการ
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)
บันทึก:
POINT
POINT(latitude, longitude)
ระดับคำสั่งของข้อโต้แย้งพิกัดการจัดเก็บจะต้องST_Distance
) และการพิจารณาว่ามีจุดหนึ่งอยู่ภายในพื้นที่อื่นหรือไม่ ( ST_Contains
)CREATE TABLE geom (g GEOMETRY NOT NULL, SPATIAL INDEX(g)) ENGINE=MyISAM;
และคำเตือนเกี่ยวกับข้อ จำกัด ของ SDT ดังที่เจมส์พูดถึงบางทีคำตอบของคุณอาจจะกระชับและแม่นยำมากขึ้นในการช่วยเหลือผู้อื่นเช่นกัน ..
อ้างอิงจากบทความวิกินี้ http://en.wikipedia.org/wiki/Decimal_degrees#Accuracy ชนิดข้อมูลที่เหมาะสมใน MySQL คือฐานสิบ (9,6) สำหรับการจัดเก็บลองจิจูดและละติจูดในฟิลด์แยก
ใช้DECIMAL(8,6)
สำหรับละติจูด (90 ถึง -90 องศา) และDECIMAL(9,6)
ลองจิจูด (180 ถึง -180 องศา) ทศนิยม 6 ตำแหน่งถือว่าใช้ได้สำหรับแอปพลิเคชันส่วนใหญ่ ทั้งสองควรเป็น "เซ็นชื่อ" เพื่ออนุญาตให้มีค่าลบ
DECIMAL
type มีไว้สำหรับการคำนวณทางการเงินที่ไม่floor/ceil
ยอมรับ ธรรมดาอย่างมีนัยสำคัญมีประสิทธิภาพเหนือกว่าFLOAT
DECIMAL
ไม่จำเป็นต้องไปไกลเท่าไหร่ตาม Google Maps ที่ดีที่สุดคือ FLOAT (10,6) สำหรับ lat และ lng
lat FLOAT( 10, 6 ) NOT NULL,
lng FLOAT( 10, 6 ) NOT NULL
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
เราเก็บละติจูด / ลองจิจูด X 1,000,000 ไว้ในฐานข้อมูล oracle ของเราเป็นตัวเลขเพื่อหลีกเลี่ยงข้อผิดพลาดในการปัดเศษเป็นสองเท่า
เนื่องจากละติจูด / ลองจิจูดนั้นไปยังตำแหน่งทศนิยมที่ 6 นั้นมีความแม่นยำ 10 ซม. ซึ่งเป็นสิ่งที่เราต้องการ ฐานข้อมูลอื่น ๆ จำนวนมากยังเก็บ lat / long เป็นทศนิยมลำดับที่ 6
ในมุมมองที่แตกต่างและเรียบง่ายโดยสิ้นเชิง:
VARCHAR
) เช่น: " -0000.0000001, -0000.000000000000001 " (ความยาว 35 และถ้าตัวเลขมีทศนิยมมากกว่า 7 หลักมันจะถูกปัดเศษ);google.maps.geometry.poly.containsLocation(latLng, bermudaTrianglePolygon))
วิธีนี้คุณไม่จำเป็นต้องกังวลเกี่ยวกับการจัดทำดัชนีตัวเลขและปัญหาอื่น ๆ ที่เกี่ยวข้องกับชนิดข้อมูลที่อาจทำให้ค่าพิกัดของคุณแย่ลง
ฉันแนะนำให้ใช้ FLOAT (9,6)
แป้นเชิงพื้นที่จะให้คุณสมบัติเพิ่มเติมแก่คุณ แต่ด้วยมาตรฐานการผลิตทำให้การลอยนั้นเร็วกว่าแป้นเชิงพื้นที่มาก (0,01 VS 0,001 ใน AVG)
MySQL ใช้ double สำหรับ float ทั้งหมดดังนั้นใช้ type double การใช้โฟลตจะทำให้ค่าที่ปัดเศษไม่สามารถคาดการณ์ได้ในสถานการณ์ส่วนใหญ่
DOUBLE
MySQL ช่วยให้คุณสามารถจัดเก็บข้อมูลที่เป็นทั้ง 4 ไบต์FLOAT
หรือ DOUBLE
8 ดังนั้นจึงมีความเป็นไปได้ที่จะสูญเสียความแม่นยำเมื่อจัดเก็บนิพจน์ลงในFLOAT
คอลัมน์
แม้ว่ามันจะไม่เหมาะสำหรับการใช้งานทั้งหมด แต่หากคุณกำลังทำแผ่นแผนที่หรือทำงานกับเครื่องหมายจำนวนมาก (จุด) ด้วยการฉายเพียงครั้งเดียว (เช่น Mercator เช่น Google แผนที่และกรอบแผนที่ลื่น ๆ อื่น ๆ ) ฉันได้พบสิ่งที่ ฉันเรียกว่า "Vast Coordinate System" จะสะดวกและมีประโยชน์จริงๆ โดยทั่วไปคุณเก็บพิกัด x และ y pixel ในบางวิธีการซูมเข้า - ฉันใช้ระดับการซูม 23 สิ่งนี้มีประโยชน์หลายประการ:
ฉันพูดถึงสิ่งนี้ทั้งหมดในโพสต์บล็อกล่าสุด: http://blog.webfoot.com/2013/03/12/optimizing-map-tile-generation/
ฉันประหลาดใจอย่างมากกับคำตอบ / ความคิดเห็น
ทำไมบนโลกนี้ใคร ๆ ก็เต็มใจที่จะ "ลดความแม่นยำ" ล่วงหน้าอย่างสมัครใจและจากนั้นทำการคำนวณจำนวนที่แย่กว่านั้นในภายหลัง ฟังดูโง่ที่สุด
หากแหล่งกำเนิดมีความแม่นยำ 64 บิตแน่นอนว่ามันจะเป็นใบ้ที่จะทำการปรับสเกลโดยสมัครใจเช่น 6 ทศนิยมและจำกัดความแม่นยำไว้ที่ 9 Digts ที่สำคัญสูงสุด (ซึ่งเกิดขึ้นกับรูปแบบทศนิยม 9.6 ที่นำเสนอโดยทั่วไป)
ตามธรรมชาติแล้วหนึ่งเก็บข้อมูลด้วยความแม่นยำที่วัสดุแหล่งที่มามี เหตุผลเดียวในการลดความแม่นยำก็คือพื้นที่จัดเก็บที่ จำกัด
รูปแบบ 9.6- ทศนิยมทำให้เกิดปรากฏการณ์ snap-to-grid นั่นควรจะเป็นขั้นตอนสุดท้ายถ้ามันเกิดขึ้น
ฉันจะไม่เชิญข้อผิดพลาดสะสมในรังของฉัน
TL; DR
ใช้ FLOAT (8,5) หากคุณไม่ได้ทำงานในองค์การนาซ่า / กองทัพและไม่ได้สร้างระบบนำทางด้วยเครื่องบิน
ในการตอบคำถามของคุณอย่างเต็มที่คุณต้องพิจารณาหลายสิ่ง:
รูปแบบ
ดังนั้นส่วนแรกของคำตอบคือ - คุณสามารถเก็บพิกัดในรูปแบบที่แอปพลิเคชันของคุณใช้เพื่อหลีกเลี่ยงการแปลงค่ากลับไปกลับมาและทำการสืบค้น 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 บิต)
ฟังก์ชั่นเชิงพื้นที่ใน PostGIS นั้นมีฟังก์ชั่นมากกว่า (เช่นไม่ จำกัด การทำงานของ BBOX) มากกว่าฟังก์ชั่นเชิงพื้นที่ MySQL ลองดูสิ: ลิงค์ข้อความ
ละติจูดอยู่ในช่วง -90 ถึง +90 (องศา) ดังนั้น DECIMAL (10, 8) จึงใช้ได้
ลองจิจูดที่มีช่วงตั้งแต่ -180 ถึง +180 (องศา) ดังนั้นคุณต้องมี DECIMAL (11, 8)
หมายเหตุ: หมายเลขแรกคือจำนวนหลักทั้งหมดที่จัดเก็บและที่สองคือหมายเลขหลังจุดทศนิยม
ในระยะสั้น: lat DECIMAL(10, 8) NOT NULL, lng DECIMAL(11, 8) NOT NULL
ฉันขอแนะนำให้คุณใช้ประเภทข้อมูลลอยสำหรับ SQL Server
การคำนวณ Lat Long ต้องการความแม่นยำดังนั้นใช้ทศนิยมบางประเภทและทำให้ความแม่นยำสูงกว่าตัวเลขที่คุณจะเก็บไว้อย่างน้อย 2 เพื่อทำการคำนวณทางคณิตศาสตร์ ฉันไม่ทราบเกี่ยวกับประเภทข้อมูล sql ของฉัน แต่ใน SQL Server คนมักจะใช้ float หรือ real แทนที่จะเป็นทศนิยมและมีปัญหาเพราะสิ่งเหล่านี้เป็นตัวเลขโดยประมาณไม่ใช่ของจริง ดังนั้นตรวจสอบให้แน่ใจว่าชนิดข้อมูลที่คุณใช้เป็นทศนิยมจริงไม่ใช่ประเภททศนิยมแบบลอยตัวและคุณควรจะปรับ
A FLOAT
ควรให้ความแม่นยำทั้งหมดที่คุณต้องการและจะดีกว่าสำหรับฟังก์ชั่นการเปรียบเทียบกว่าการจัดเก็บพิกัดแต่ละตัวเป็นสตริงหรือสิ่งที่คล้ายกัน
หาก MySQL รุ่นของคุณก่อนหน้านี้กว่า 5.0.3 คุณอาจจำเป็นต้องใช้ระวังบางข้อผิดพลาดการเปรียบเทียบจุดลอยอย่างไร
ก่อนหน้า MySQL 5.0.3 คอลัมน์ DECIMAL จะเก็บค่าด้วยความแม่นยำที่แน่นอนเพราะแสดงเป็นสตริง แต่การคำนวณค่า DECIMAL นั้นทำได้โดยใช้การดำเนินการทศนิยม จาก 5.0.3, MySQL ดำเนินการ DECIMAL ด้วยความแม่นยำของตัวเลขทศนิยม 64 หลักซึ่งควรแก้ปัญหาความไม่ถูกต้องที่พบบ่อยที่สุดเมื่อมันมาถึงคอลัมน์ DECIMAL
DECIMAL
มีข้อผิดพลาดบางอย่าง (ก่อนหน้า 5.0.3) เนื่องจากการใช้การลอยตัว