该方案由三个表组成,街道、城市和邮政编码,它们之间都具有多对多的关系。
我想要做的是查询数据库以查找可能的匹配项以及预测缺失数据的预测。举个例子:假设用户开始输入一个城市,也许是“Wash”。他已经输入了一条街道,为了我不知道任何街道,我们称它为“新街大道”。这是一个约束,意味着它必须在我们的预测中实现。现在我希望城市查询“Wash”的可能完成者,并提供缺失的数据,在这种情况下,邮政编码有一条名为“New Street Avenue”的街道
以下是天真的方法:
- 过滤名称与“New Street Avenue”匹配的所有键的街道
- 过滤所有街道键的 street_city 并返回城市键
- 内连接 city_zipcode 上的城市键,然后内连接这些到邮政编码
这种方法的问题:街道和邮政编码之间的知识被完全忽略了。这意味着我们最终可能会得到邮政编码和城市对,我们知道城市包含我们寻找的街道,但邮政编码不一定。这将是一个无效的返回预测,因为地址不存在!
这意味着我需要“持久化”有关街道的信息。
我的方法是这样的:
- 在街道上过滤满足我们条件的键。
- 从#1 到城市的内部连接街道的钥匙。保留两个关键列。
- 内部连接到 city_zipcode 并保留邮政编码键
- 过滤掉所有不在 street_zipcode 中的街道和邮政编码对
(附带说明:为了“希望”提高性能,在每个“预测”之后,我会放一个 LIMIT 10(或类似的)命令,因为我们只需要合理数量的预测)。
这种方法应该有效。它可能不是最好的优化,但它会返回正确的结果。然而,这不仅让我觉得很脏,而且还带来了另一个问题:
我很确定它在表格大小以及其他表格中的扩展非常可怕
很有可能会有另一个表与街道和邮政编码具有多对多关系。现在,如果我想搜索一个有街道约束的城市,我需要做我上面所做的,并添加更多内容以过滤掉街道和新表之间的任何关系。
我觉得我用这种方法进入了死胡同。我很想有人在这里帮助我如何更好地解决这个问题。
3 个表就是我所说的“过度规范化”——它会导致性能问题。
“规范化”用于两个目的,这两个目的都不适用于您的用例。
隔离一些东西,说“城市”,这样就可以很容易地改变它。在现实生活中,这种情况很少发生在城市中。(“孟买”->“孟买”(印度),“温泉”->“真相或后果”(新墨西哥州))
节省空间。即使你拥有地球上所有的 300 万个城市,每次将它们拼出国家也不会有太大的空间负担。(对于国家/地区,我推荐标准的 2 字母国家/地区代码并使用
CHARACTER SET ascii
。可选地,有一个表格将它们映射到拼写名称。)邮政编码:在美国,大约有 42K 邮政编码。要规范化,您可以使用 2-byte
SMALLINT UNSIGNED
。但是邮政编码本身可以存储在 3-byteMEDIUMINT(5) UNSIGNED ZEROFILL
中。邮政编码确实发生了变化。但这通常涉及迫使一个邮政编码的一半用户采用“新”邮政编码。您必须检查该邮政编码的所有用户(可能是INDEXed
)才能确定要更改的用户。南斯拉夫和捷克斯洛伐克的分裂也是如此。另一方面,将“上沃尔特共和国”更改为“布基纳法索”很容易,如果您已将其“标准化”。
计划 A(数百万个位置):简单地拼出每个项目的位置。
B计划(十亿个位置):一分为二:街道地址和邮编+城市+国家
换一种方式想一想:您将如何处理该地址?
SELECT SUM... GROUP BY country_code
。这意味着这country_code
是一个单独的列。