0

我有世界上最简单的表格,用于查找英国邮政编码的 lat/lng 值(加载了完整的英国邮政编码数据):

CREATE TABLE postcodes (
  postcode char(7) NOT NULL,
  lat double(10,6) NOT NULL,
  lng double(10,6) NOT NULL,
  KEY 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' ', ETC)。

但不适用于这些查询:

  • 'W1A1AA' 应该匹配 'W1A 1AA'
  • “BS65EE”应匹配“BS6 5EE”
  • 'BS65' 应该匹配 db 中的第一个 'BS6 5%' 邮政编码,即 'BS6 5AA'
  • 'M31' 应该同样匹配 'M3 1AR'

我猜我需要以某种方式做一些 MySQL 字符串函数魔术来确定行的邮政编码字段中是否有空格,并相应地调整我的 WHERE 子句逻辑?有人对最佳方法有任何建议吗?理想情况下,我还想:

  • 避免 MySQL 存储过程(首选内联函数)
  • 在 PHP 部分也只做内联字符串函数
4

6 回答 6

5

创建一个新列,它只是去掉空格的邮政编码字段,并在其上创建一个唯一索引。您不应该找到任何重复项。这应该让你放心,空间真的不重要:)

然后在删除输入邮政编码上的空格后将其用于查找。

请记住,涉及将字符串函数应用于postcode表列的解决方案可能会阻止 MySQL 使用该列上的任何索引。(索引基于列中的确切数据,因此如果您开始对该数据应用函数,优化器通常会认为该索引是无用的。)

如果您确实觉得需要重新格式化,最简单的选择是了解虽然邮政编码的“出站”部分(空格之前的部分)的格式略有不同,但“入站”部分 - - 空格后面的部分-始终是一个数字后跟两个字母。

顺便说一句,我找到的关于格式的最佳资源可能是Wikipedia entry

于 2011-03-10T20:39:13.610 回答
1

您也可以删除数据库级别的空格:

{...} WHERE replace(`postcode`, ' ','') LIKE '" . str_replace(' ','%',$query) . "%' LIMIT 1
于 2011-03-10T20:32:23.957 回答
1

首先,我认为空间并不重要。皇家邮政网页上的描述没有提到空格。此外,对于我所看到的每个带有空格的邮政编码,第二组总是 3 个字符长,所以你可能可以从后面拆分它。网页上说“通常只有一个数字”,所以可能会有例外。

如果您愿意预处理查询字符串(就像您在示例中使用 php 一样),您可以通过以下方式解决问题:通过 (1) 删除所有空格然后 (2) 将查询后代码转换为正则表达式在所有字符之间添加?(即空格的可选匹配)。最后.*在末尾添加一个以允许不完整的代码。例子:

  • W1A1AA 变为W ?1 ?A ?1 ?A ?A.*。这匹配“W1A1AA”和“W1A 1AA”。
  • M31 变成M ?3 ?1.*.

一旦你有了这种形式的查询邮政编码,你就可以使用 MySQL 的REGEXP操作符进行匹配:

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

' '最后,顺便说一句,您替换为的技巧%有点危险。这种方式BS6 5会匹配BS6 456,因为%会匹配4.

于 2011-03-10T20:52:17.333 回答
0

您可以通过拆分所有字母来查询它。

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

因为您正在查询一个长度有限的字段,所以就误报而言,这不会产生太多问题,并且您可以在检索后按代码中的相似性排序(我假设这是用于自动完成)。性能应该很差。

于 2011-03-10T20:30:45.890 回答
0

马特的解决方案效果很好。但是,我仍然需要允许用户在他们的查询中专门有一个空格,并处理它,即:

  • '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

有进一步完善的空间吗?

于 2011-03-11T09:54:47.653 回答
0

我将在表格中为扇区和区域生成新条目,这将消除对 LIKE 的需要。

LIKE 条件不能使用索引,因此需要扫描整个表以获取结果。这很慢,尤其是当您拥有包含 170 万个值的完整英国邮政编码数据库时。

因此,只需为“M3”创建一个新条目作为邮政编码。对“M31”执行相同的操作,依此类推。至于与这些新整体相对应的纬度/经度值,您可以做一些基本的数学运算来计算其所有单个邮政编码的平均位置。

试试这个:

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

然后,您可以将结果反馈到您的邮政编码表中。

于 2013-02-06T11:52:26.920 回答