What is the ideal data type to use when storing latitude / longitude in a MySQL database?

Учитывая, что я буду выполнять вычисления для пар широта / длинна, какой тип данных лучше всего подходит для использования с базой данных MySQL?

23 ответа

Решение

Используйте пространственные расширения MySQL вместе с ГИС.

В основном это зависит от точности, которая вам нужна для вашего местоположения. Используя DOUBLE, вы получите точность 3,5 нм. DECIMAL(8,6)/(9,6) опускается до 16см. Плавание составляет 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

Надеюсь это поможет.

Google предоставляет начало для завершения решения PHP/MySQL для примера приложения "Локатор магазина" с помощью Google Maps. В этом примере они хранят значения lat / lng как "Float" с длиной "10,6".

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

Пространственные расширения MySQL - лучший вариант, потому что у вас есть полный список пространственных операторов и индексов. Пространственный индекс позволит вам очень быстро выполнять расчеты на основе расстояния. Пожалуйста, имейте в виду, что по состоянию на 6.0 Пространственное расширение все еще не завершено. Я не опускаю MySQL Spatial, а лишь сообщаю вам о подводных камнях, прежде чем вы слишком далеко продвинетесь в этом.

Если вы имеете дело только с точками и только с функцией DISTANCE, это нормально. Если вам нужно выполнить какие-либо расчеты с полигонами, линиями или буферизованными точками, пространственные операторы не будут давать точных результатов, если вы не используете оператор "связать". См. Предупреждение в верхней части 21.5.6. Отношения, такие как содержит, внутри или пересекаются, используют MBR, а не точную геометрическую форму (то есть эллипс рассматривается как прямоугольник).

Кроме того, расстояния в MySQL Spatial указаны в тех же единицах, что и ваша первая геометрия. Это означает, что если вы используете десятичные градусы, то ваши измерения расстояния в десятичных градусах. Это очень затруднит получение точных результатов, поскольку вы получаете фуртур от экватора.

Когда я сделал это для навигационной базы данных, построенной из ARINC424, я провел большое количество тестов и, оглядываясь на код, использовал DECIMAL(18,12) (на самом деле NUMERIC(18,12), потому что это была firebird).

Плавающие и двойные числа не так точны и могут привести к ошибкам округления, что может быть очень плохо. Я не могу вспомнить, нашел ли я какие-либо реальные данные, у которых были проблемы - но я вполне уверен, что неспособность точно хранить в плавающем или двойном числе может вызвать проблемы

Дело в том, что при использовании градусов или радиан мы знаем диапазон значений - а дробная часть нуждается в наибольшем количестве цифр.

Пространственные расширения MySQL являются хорошей альтернативой, поскольку они следуют модели геометрии OpenGIS. Я не использовал их, потому что мне нужно было сохранять базу данных переносимой.

Зависит от точности, которая вам требуется.

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, рассмотрите возможность использования пространственных типов данных (SDT), в частности POINT для хранения одной координаты. До версии 5.7 SDT не поддерживает индексы (за исключением 5.6, когда тип таблицы - MyISAM).

Замечания:

  • Когда используешь POINT класс, порядок аргументов для хранения координат должен быть POINT(latitude, longitude),
  • Существует специальный синтаксис для создания пространственного индекса.
  • Самым большим преимуществом использования SDT является то, что у вас есть доступ к функциям пространственного анализа, например, для расчета расстояния между двумя точками (ST_Distance) и определение, содержится ли одна точка в другой области (ST_Contains).

Основываясь на этой статье вики http://en.wikipedia.org/wiki/Decimal_degrees, соответствующий тип данных в MySQL - Decimal(9,6) для хранения долготы и широты в отдельных полях.

Использование DECIMAL(8,6) для широты (от 90 до -90 градусов) и DECIMAL(9,6) для долготы (от 180 до -180 градусов). 6 знаков после запятой хорошо для большинства приложений. Оба должны быть "подписаны", чтобы учесть отрицательные значения.

Не нужно далеко ходить, по данным Google Maps, лучше всего FLOAT(10,6) для широты и долготы.

Мы храним широту / долготу X 1000 000 в нашей базе данных Oracle как NUMBERS, чтобы избежать ошибок округления с двойными числами.

Учитывая, что широта / долгота до 6-го знака после запятой составляет 10 см, это все, что нам нужно. Многие другие базы данных также хранят широту / долготу до 6-го знака после запятой.

TL;DR

Используйте FLOAT(8,5), если вы не работаете в NASA / военные и не производите навигационные системы самолетов.


Чтобы полностью ответить на ваш вопрос, вам нужно рассмотреть несколько вещей:

Формат

  • градусы минуты секунды: 40° 26′ 46″ с.ш. 79° 58′ 56″ з.д.
  • градусы десятичные минуты: 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", но вас когда-нибудь волновали миллиметры при навигации к точке? Если вы не работаете в НАСА и не выполняете спутники, ракеты или траектории самолетов, вам должно быть хорошо с точностью до нескольких метров.

Обычно используемый формат состоит из 5 цифр после точек, что дает точность 50 см.

Пример: расстояние между X, 21.2780818 и X, 21.2780819 составляет 1 см. Таким образом, 7 цифр после точки дают вам точность в 1/2 см, а 5 цифр после точки дают вам точность в 1/2 метра (поскольку минимальное расстояние между точками составляет 1 м, поэтому ошибка округления не может превышать половины). Для большинства гражданских целей этого должно быть достаточно.

Формат в градусах и десятичных минутах (40° 26,767 'N 79° 58,933' W) дает вам точно такую ​​же точность, как 5 цифр после точки

Пространственное хранение

Если вы выбрали десятичный формат, то ваша координата - пара (-32.60875, 21.27812). Очевидно, 2 x (1 бит для знака, 2 цифры для степени и 5 цифр для показателя степени) будет достаточно.

Поэтому здесь я бы хотел поддержать Аликс Акселя из комментариев о том, что предложение Google сохранить его в FLOAT(10,6) действительно лишнее, потому что вам не нужны 4 цифры для основной части (так как знак отделен и широта ограничена до 90 и долгота ограничена до 180). Вы можете легко использовать FLOAT(8,5) для точности 1 / 2м или FLOAT(9,6) для точности 50/2см. Или вы можете даже хранить lat и long в отдельных типах, потому что FLOAT(7,5) достаточно для lat. См. MySQL справочник типов плавания. Любой из них будет как обычный FLOAT и в любом случае будет равен 4 байта.

Обычно пространство не является проблемой в настоящее время, но если вы по какой-то причине действительно хотите оптимизировать хранилище (Отказ от ответственности: не выполняйте предварительную оптимизацию), вы можете сжать lat(не более 91 000 значений + знак) + long(нет более 181 000 значений + знак) до 21 бита, что значительно меньше 2xFLOAT (8 байт == 64 бита)

В совершенно другой и более простой перспективе:

  • если вы полагаетесь на Google для показа своих карт, маркеров, полигонов и т. д., тогда пусть Google сделает расчеты!
  • вы сохраняете ресурсы на своем сервере и просто сохраняете широту и долготу вместе как одну строку (VARCHAR), Например: " -0000.0000001, -0000.000000000000001 " (длина 35, и если число имеет более 7 десятичных цифр, оно округляется);
  • если Google возвращает более 7 десятичных цифр на число, вы в любом случае можете сохранить эти данные в вашей строке, на случай, если в будущем вы захотите обнаружить некоторые блохи или микробы;
  • Вы можете использовать их матрицу расстояний или их геометрическую библиотеку для расчета расстояний или обнаружения точек в определенных областях с помощью вызовов, таких как: google.maps.geometry.poly.containsLocation(latLng, bermudaTrianglePolygon))
  • Есть много "серверных" API-интерфейсов, которые вы можете использовать (в Python, Ruby on Rails, PHP, CodeIgniter, Laravel, Yii, Zend Framework и т. д.), которые используют API Карт Google.

Таким образом, вам не нужно беспокоиться об индексации чисел и обо всех других проблемах, связанных с типами данных, которые могут испортить ваши координаты.

  1. Широты варьируются от -90 до +90 (градусов), поэтому DECIMAL(10, 8) подходит для этого

  2. долготы варьируются от -180 до +180 (градусов), поэтому вам нужно DECIMAL(11, 8).

Примечание. Первое число - это общее количество сохраненных цифр, а второе - число после десятичной точки.

Короче: lat DECIMAL(10, 8) NOT NULL, lng DECIMAL(11, 8) NOT NULL

Хотя это не оптимально для всех операций, если вы создаете листы карты или работаете с большим количеством маркеров (точек) только с одной проекцией (например, Mercator, как Google Maps и многие другие фреймворки скользких карт), я обнаружил, что Я называю "Обширную систему координат" очень удобной. По сути, вы сохраняете координаты пикселей x и y при некотором увеличении - я использую уровень масштабирования 23. Это имеет несколько преимуществ:

  • Вы выполняете дорогостоящее преобразование пикселей в латы / мергаты один раз, а не каждый раз, когда обрабатываете точку
  • Получение координаты тайла из записи при заданном уровне масштабирования занимает один сдвиг вправо.
  • Для получения координаты пикселя из записи требуется один сдвиг вправо и одно побитовое AND.
  • Сдвиги настолько легки, что практично делать их в SQL, что означает, что вы можете сделать DISTINCT, чтобы вернуть только одну запись на пиксельное местоположение, что сократит количество записей, возвращаемых бэкэндом, что означает меньшую обработку на внешний интерфейс.

Я говорил обо всем этом в недавнем сообщении в блоге: http://blog.webfoot.com/2013/03/12/optimizing-map-tile-generation/

Я очень удивлен некоторыми ответами / комментариями.

С какой стати кто-то захотел бы добровольно "предварительно уменьшить" точность, а затем выполнить вычисления на худших числах? Звучит в конечном итоге глупо.

Если источник имеет 64-битную точность, конечно, было бы глупо добровольно фиксировать масштаб, например. 6 десятичных знаков и ограничивают точность максимум 9 значащими цифрами (что происходит с обычно предлагаемым десятичным форматом 9,6).

Естественно, каждый хранит данные с точностью, которой обладает исходный материал. Единственной причиной снижения точности будет ограниченное пространство для хранения.

  • Хранить исходные данные с оригинальной точностью
  • Сохраняйте цифры, рассчитанные из источника, в точности, в которой происходит вычисление (например, если код приложения использует удваивается, сохраняйте результаты как двойные)

Десятичный формат 9,6 вызывает явление привязки к сетке. Это должен быть самый последний шаг, если это вообще произойдет.

Я не хотел бы приглашать накопленные ошибки в мое гнездо.

В зависимости от вашего приложения, я предлагаю использовать FLOAT(9,6)

Пространственные ключи дадут вам больше возможностей, но в производственных тестах поплавки намного быстрее, чем пространственные ключи. (0,01 против 0,001 в AVG)

Пространственные функции в PostGIS намного более функциональны (т.е. не ограничены операциями BBOX), чем функции в пространственных функциях MySQL. Проверьте это: текст ссылки

MySQL использует double для всех типов с плавающей запятой... Так что используйте тип double. Использование float приведет к непредсказуемым округленным значениям в большинстве ситуаций

GeoLocationCoordinates возвращает тип данных double, представляющий широту и долготу положения в десятичных градусах. Вы можете попробовать использовать double.

Идеальный тип данных для хранения значений широты и долготы - десятичный (9,6).

Это с точностью примерно 10 см при использовании всего 5 байтов памяти.

например, CAST(123,456789 как десятичное (9,6))

Я предлагаю вам использовать тип данных Float для SQL Server.

Lat Long вычисления требуют точности, поэтому используйте какой-либо тип десятичного типа и сделайте точность как минимум на 2 больше, чем число, которое вы будете хранить для выполнения математических вычислений. Я не знаю о моих типах данных sql, но в SQL-сервере люди часто используют float или real вместо десятичной и сталкиваются с проблемами, потому что это приблизительные числа, а не реальные. Так что просто убедитесь, что тип данных, который вы используете, является истинным десятичным типом, а не плавающим десятичным типом, и у вас все будет хорошо.

FLOAT должен дать вам всю необходимую точность и быть лучше для функций сравнения, чем сохранять каждую координату в виде строки или тому подобного.

Если ваша версия MySQL более ранняя, чем 5.0.3, вам может потребоваться учесть некоторые ошибки сравнения с плавающей запятой.

До MySQL 5.0.3 столбцы DECIMAL сохраняли значения с точной точностью, потому что они представлены в виде строк, но вычисления значений DECIMAL выполняются с использованием операций с плавающей запятой. Начиная с 5.0.3, MySQL выполняет операции DECIMAL с точностью до 64 десятичных цифр, что должно решить наиболее распространенные проблемы неточности, когда речь идет о столбцах DECIMAL.

Другие вопросы по тегам