MySQL запрос на совпадение британских почтовых индексов независимо от количества пробелов - PullRequest
0 голосов
/ 10 марта 2011

У меня самая простая в мире таблица для поиска значений широты / долготы для почтового индекса Великобритании (загруженного полными данными почтового индекса Великобритании):

CREATE TABLE postcodes (
  postcode char(7) NOT NULL,
  lat double(10,6) NOT NULL,
  lng double(10,6) NOT NULL,
  KEY postcode (postcode)
)

Почтовые индексы в поле 'postcode' либо имеют 2 цифры в конце первой половины, либо одну, а затем пробел. Я думаю, что пространство важно для целостности их соответствия (??), и, кроме того, я не хочу удалять пробелы в таблице, так как я также вытаскиваю почтовые индексы для отображения (и я не не хочу дублировать поле, потому что я суетливый!). Примеры:

'LE115AF', 'BS6 5EE', 'W1A 1AA', 'BS216RS', 'M3 1NH'

Итак, у некоторых есть пробелы, у некоторых нет. Большинство из них имеют 7 символов, а некоторые только 6.

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

Вот как я это сделал до сих пор (с некоторой помощью PHP):

{...} WHERE `postcode` LIKE '" . str_replace(' ','%',$query) . "%' LIMIT 1

Это хорошо для:

  • полные почтовые индексы, которые не содержат пробелов в db
  • частичные почтовые индексы, если был введен пробел и имеется соответствующий пробел в БД, или запрашиваемая часть останавливается на месте пробела (например, «W1A» будет соответствовать «W1A 1AA», «M3 1» будет соответствовать «M3 1AR» и т. Д.).

Но не работает для этих запросов:

  • W1A1AA должно совпадать с W1A 1AA
  • 'BS65EE' должно совпадать с 'BS6 5EE'
  • 'BS65' должен соответствовать первому почтовому индексу 'BS6 5%' в дБ, то есть 'BS6 5AA'
  • 'M31' должно совпадать с 'M3 1AR'

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

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

Ответы [ 6 ]

5 голосов
/ 10 марта 2011

Создайте новый столбец, который является просто полем почтового индекса с зарезанными пробелами, и создайте уникальный индекс для него.Вы не должны найти дубликаты.Это должно убедить вас, что пробел на самом деле не важен:)

Затем используйте , что для поиска, после удаления пробелов на ваших входных почтовых кодах.что решения, которые включают применение строковых функций к столбцу postcode таблицы, могут помешать MySQL использовать любые индексы для этого столбца.(Индекс основан на точных данных в столбце, поэтому, если вы начнете применять функции к этим данным, оптимизатор, как правило, решит, что индекс бесполезен.)

Если вы чувствуете необходимость переформатировать данныесамый простой вариант - работать, зная, что хотя «исходящая» часть почтового индекса - часть до пробела - немного различается по формату, «входящая» часть - часть после пробел - это всегда одна цифра, за которой следуют две буквы.

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

1 голос
/ 10 марта 2011

Во-первых, я не думаю, что пространство важно. Описание на Royal На веб-странице почты нет упоминания пробелов Также для каждого почтового индекса с пробелом, который я видел, вторая группа всегда была длиной 3 символа, так что, вероятно, вы можете разделить ее сзади. На веб-странице написано «обычно это просто одно число», поэтому могут быть исключения.

Если вы хотите предварительно обработать строку запроса (как вы использовали php в вашем примере), вы можете решить эту проблему следующим образом: вы преобразуете свой почтовый код запроса в регулярное выражение, (1) удаляя все пробелы, а затем (2) добавление ? (т.е. необязательное совпадение для пробела) между всеми символами. Наконец добавьте .* в конце, чтобы учесть неполные коды. Примеры:

  • W1A1AA становится W ?1 ?A ?1 ?A ?A.*. Это соответствует "W1A1AA" и "W1A 1AA".
  • M31 становится M ?3 ?1.*.

Получив почтовый индекс запроса в этой форме, вы можете использовать оператор REGEXP MySQL для сопоставления:

{...} WHERE `postcode` LIKE 'M ?3 ?1.*' LIMIT 1

Наконец, в стороне, ваш трюк с заменой ' ' на % немного опасен. Таким образом, BS6 5 будет соответствовать BS6 456, потому что % будет соответствовать 4.

1 голос
/ 10 марта 2011

Вы также можете удалить пробелы на уровне базы данных:

{...} WHERE replace(`postcode`, ' ','') LIKE '" . str_replace(' ','%',$query) . "%' LIMIT 1
0 голосов
/ 06 февраля 2013

Я бы сгенерировал новые записи в таблице для сектора и области, что исключило бы необходимость в LIKE.

Условие LIKE не может использовать индексы и поэтому должно сканировать всю таблицу на предмет результата.Это медленно, особенно если у вас есть полная база данных почтовых индексов Великобритании с 1,7 миллионами значений.

Итак, создайте новую запись только для M3 в качестве почтового индекса.Сделайте то же самое для «M31» и так далее.Что касается значений lat / long, которые соответствуют этим новым объектам, вы можете выполнить некоторые основные математические расчеты, чтобы рассчитать среднее местоположение всех его отдельных почтовых индексов.

Попробуйте это:

SELECT
  SUBSTRING(postcode, 1, LOCATE(' ', postcode) - 1),
  AVG(lat),
  AVG(long)
FROM
  postcodes
GROUP BY
  SUBSTRING(postcode, 1, LOCATE(' ', postcode) - 1)

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

0 голосов
/ 11 марта 2011

Решение Мэтта работало хорошо. Однако мне нужно было разрешить пользователям специально выделять пространство в своем запросе и обрабатывать это, то есть:

  • «M31» должно соответствовать «M31 4AA», тогда как
  • 'M3 1' должно совпадать с 'M3 1AR'

Итак, мое расширенное решение (решает вышеуказанную проблему):

CREATE TABLE postcodes (
  postcode varchar(7) NOT NULL,
  postcode_display char(7) NOT NULL,
  lat double(10,6) NOT NULL,
  lng double(10,6) NOT NULL,
  UNIQUE KEY postcode (postcode),
  UNIQUE KEY postcode_display (postcode_display)
)

postcode вырезаны пробелы, postcode_display оставлены в ..

<?php
if (strlen($query) <= 7 && strpos($query,' ') !== false) { $hasSpace = true; }
?>

..

WHERE `postcode" . ($hasSpace ? '_display' : '') . "` LIKE '" . str_replace(' ',($hasSpace ? '%' : ''),$query) . "%' LIMIT 1

Есть комната для дальнейших доработок?

0 голосов
/ 10 марта 2011

Вы можете запросить его, разделив все буквы.

WHERE `postcode` LIKE '" . implode("%", str_split("W1A1AA")) . "%' LIMIT 1

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

...