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

StackOverflow https://stackoverflow.com/questions/159255

Вопрос

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

Это было полезно?

Решение

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

Другие советы

Google предоставляет начальное решение PHP/MySQL для примера приложения «Поиск магазинов» с Картами Google.В этом примере значения широты и долготы сохраняются как «Float» с длиной «10,6».

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

В основном это зависит от точности, которая вам нужна для вашего местоположения.Используя DOUBLE, вы получите точность 3,5 нм.ДЕСЯТИЧНОЕ(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

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

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

Если вы имеете дело строго с точками и только с функцией РАССТОЯНИЕ, это нормально.Если вам нужно выполнить какие-либо вычисления с многоугольниками, линиями или буферизованными точками, пространственные операторы не дадут точных результатов, если вы не используете оператор «связь».Смотрите предупреждение вверху 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_grades#AccuracyСоответствующий тип данных в MySQL является десятичным (9,6) для хранения долготы и широты в отдельных полях.

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

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

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

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

Совершенно с другой и более простой точки зрения:

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

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

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

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

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

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

Обо всем этом я рассказал в недавнем посте в блоге: http://blog.webfoot.com/2013/03/12/optimizing-map-tile-generation/

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

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

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

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

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

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

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

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

ТЛ;ДР

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


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

Формат

  • градусы минуты секунды:40° 26′ 46″ с.ш. 79° 58′ 56″ з.д.
  • градусы десятичные минуты:40° 26,767′ с.ш. 79° 58,933′ з.д.
  • десятичные градусы 1:40,446° с.ш. 79,982° з.д.
  • десятичные градусы 2: -32.60875, 21.27812
  • Какой-то другой самодельный формат?Никто не запрещает вам создать собственную систему координат, ориентированную на дом, и хранить ее как курс и расстояние от дома.Это может иметь смысл для некоторых конкретных проблем, над которыми вы работаете.

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

Скорее всего, вы используете Google Maps или OSM для отображения своих данных, а GMaps использует формат «десятичные градусы 2».Так будет проще хранить координаты в одном формате.

Точность

Затем вы хотите определить необходимую вам точность.Конечно, вы можете хранить координаты типа «-32.608697550570334,21.278081997935146», но заботились ли вы когда-нибудь о миллиметрах при навигации к точке?Если вы не работаете в НАСА и не изучаете траектории спутников, ракет или самолетов, у вас все будет в порядке с точностью до нескольких метров.

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

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

Десятичный формат минут в градусах (40° 26,767′ с.ш., 79° 58,933′ з.д.) дает ту же точность, что и 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 в отдельных типах, потому что для lat достаточно FLOAT(7,5).См. типы чисел с плавающей запятой MySQL. ссылка.Любой из них будет похож на обычный FLOAT и в любом случае будет равен 4 байтам.

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

  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 выше, чем число, которое вы сохраните для выполнения математических вычислений.Я не знаю о типах данных моего SQL, но на SQL-сервере люди часто используют числа с плавающей запятой или вещественные числа вместо десятичных и попадают в неприятности, потому что это оценочные числа, а не реальные.Поэтому просто убедитесь, что тип данных, который вы используете, является истинным десятичным типом, а не плавающим десятичным типом, и все будет в порядке.

А ПЛАВАТЬ должен дать вам всю необходимую точность и лучше подходит для функций сравнения, чем сохранение каждой координаты в виде строки или чего-то подобного.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top