2

我正在处理的一个 Web 应用程序遇到了一个意外的“错误”——该应用程序的数据库有两个表(以及许多其他表),分别称为“States”和“Cities”。

' States ' 表字段:

-------------------------------------------
idStates   |   State   |   Lat   |   Long
-------------------------------------------

' idStates ' 是一个自动递增的主键。

城市”表字段:

----------------------------------------------------------
idAreaCode   |   idStates   |   City   |   Lat   |   Long
----------------------------------------------------------

' idAreaCode ' 是一个主键,由国家代码 + 区号组成(例如 91422,其中 91 是印度的国家代码,422 是印度城市的区号)。“ idStates ”是从“ States ”表派生的外键,用于将“ Cities ”表中的每个城市与其对应的州相关联。

我们认为国家代码+地区代码组合对于每个城市都是唯一的,因此可以安全地用作主键。一切正常。但是印度的一个地方在 db 设计中发现了一个意想不到的“缺陷”——印度和美国一样是联邦民主国家,在地理上分为许多州或联邦领土。州和联邦领土数据都存储在“”表中。然而,有一个地方——昌迪加尔——属于两个州(哈里亚纳邦旁遮普邦),它本身也是一个联邦领土。

显然,当前的 db 设计不允许我们存储多个城市“ Chandigarh ”的记录。

建议的解决方案之一是创建一个组合列“ idAreaCode ”和“ idStates ”的主键。

我想知道这是否是最好的解决方案?

(仅供参考:我们正在使用带有 InnoDB 引擎的 MySQL)。


更多信息:

  • 该数据库存储每个城市的气象信息。因此,州和城市是每个查询的起点。
  • 每天使用 CSV 文件插入每个城市的新数据。CSV 文件包括用于标识每条记录的 idStates(用于州)和 idAreaCode(用于城市)列。
  • 数据库规范化对我们很重要。

注意:没有为 city 表使用自动递增主键的原因是数据库每天/每小时使用 CSV 文件(由另一个应用程序生成)更新。并且 CSV 文件中的每条记录都由 idStates 和 idAreaCode 列标识。因此,即使表被删除并再次刷新,城市表中使用的主键对于每个城市都是相同的。邮政编码(或 PIN 码)和区号(或 STD 代码)符合唯一、静态(不经常更改)的标准,并且这些现成的列表很容易获得。(我们现在决定使用区号,因为印度正在将其密码更新为新格式)。

我们决定的解决方案是在应用程序级别处理此问题,而不是更改数据库设计。在数据库中,我们将只存储“Chandigarh”的一条记录。在应用程序中,我们为任何搜索“Chandigarh, Punjab”或“Chandigarh, Haryana”创建了一个标志,以将搜索重定向到该记录。是的,这并不理想,但这是一个可以接受的折衷方案,因为这是迄今为止我们遇到的唯一例外。

4

8 回答 8

4

听起来您正在为电话簿收集数据。你是?为什么状态对你很重要?这个问题的答案可能会决定哪种数据库设计最适合您。

你可能认为城市是什么是显而易见的。它不是。这取决于您将如何处理数据。在美国,有一个称为 MSA(大都会统计区)的单位。堪萨斯城 MSA 横跨堪萨斯州堪萨斯城和密苏里州堪萨斯城。MSA 单元是否有意义取决于数据的预期用途。如果您使用美国的区号来确定城市,那么您最终会得到与 MSA 截然不同的分组。同样,这取决于您将如何处理数据。

一般来说,每当政治细分的等级模式崩溃时,最一般的解决方案是考虑多对多的关系。您解决此问题的方式与解决其他多对多问题的方式相同。通过创建一个带有两个外键的新表。在这种情况下,外键是 IdAreacode 和 IdStates。

现在您可以在多个州拥有一个区号,一个州可以跨越多个区号。接受这个额外的开销来覆盖一个例外似乎是一种耻辱。你知道你发现的异常是否只是冰山一角,这样的异常还有很多吗?

于 2009-08-12T11:57:00.090 回答
2

当您想要引用该表时,拥有复合键可能会出现问题,因为引用表必须具有主键具有的所有列。

如果是这种情况,您可能希望拥有一个序列主键,并在 UNIQUE NOT NULL 组中定义 idAreaCode 和 idStates。

于 2009-08-12T04:48:02.190 回答
2

我认为最好添加另一个表,国家。您的问题是为什么数据库规范化很重要的一个例子。您不能只是将不同的键混合和匹配到一列。

所以,我建议你创建这些表:

国家:

+------------+--------------+
| country_id | 国家名称 |
+------------+--------------+

状态:

+------------+----------+------------+
| country_id | state_id | 州名 |
+------------+----------+------------+

城市

+------------+----------+---------+------------+
| country_id | state_id | city_id | 城市名 |
+------------+----------+---------+------------+

数据

+------------+----------+---------+---------+----- -----+
| country_id | state_id | city_id | 数据ID | your_CSV |
+------------+----------+---------+---------+----- -----+

粗体字段是主键。输入标准 country_id,例如 1 代表美国,91 代表印度,依此类推。city_id 也应该使用他们的标准 id。

然后,您可以以最小的开销快速找到属于彼此的任何东西。然后所有数据可以直接输入到数据表中,从而作为一个入口点,将所有数据存储到一个点中。我不知道mysql,但如果你的数据库支持分区,你可以根据country_id或country_id+state_id将数据表分区到几个服务器数组,这样也会大大提高你的数据库性能。第一个、第二个和第三个表根本不会对服务器负载造成太大影响,仅作为参考。您将主要处理第四个数据表。您可以随心所欲地添加数据,而不会再有任何重复。

如果每个城市只有一个数据,则可以省略数据表并将 CSV_data 移动到城市表,如下所示:

城市

+------------+----------+---------+-----------+--- --------+
| country_id | state_id | city_id | 城市名 | CSV_data |
+------------+----------+---------+-----------+--- --------+
于 2010-07-10T17:29:36.423 回答
1

“我们认为国家代码+区号组合对于每个城市都是唯一的,因此可以安全地用作主键”

读完这篇文章后,我就停下来继续阅读这个主题的任何内容。怎么会有人这样想呢?
区号,根据定义(我在互联网上找到的第一个):
-“区号是用于根据北美号码计划识别地理区域的前缀号码。这个 3 位数的号码可以分配给任何号码在北美,包括加拿大、美国、墨西哥、拉丁美洲和加勒比地区”[1]

撇开它们仅在北美是可变的和定义的,区号在其他一些国家不是 3 位数(在某些国家/地区拥有数十万个位置,3 位数根本不够。顺便说一句,我母亲的区号有 5数字),并且它们与固定的地理位置没有严格的联系。

区号有迁徙地点,如飘着冰的北极营地、游牧部落、迁徙的军事单位,甚至大型海洋船只等。

那么,将几个城市合并为一个(反之亦然)怎么样?

[1]
http://www.successfuloffice.com/articles/answering-service-glossary-area-code.htm

于 2010-11-11T18:06:00.950 回答
1

如果您向键添加额外的列以便为给定城市添加额外的记录,那么您没有正确规范化您的数据。鉴于您现在发现一个城市可以是多个州的成员,我建议从 Cities 表中删除对某个州的任何引用,然后添加一个允许您将州与城市关联起来的 StateCity 表(创建 am:m关系)。

于 2009-08-12T05:19:55.383 回答
1

引入代理键。当区号更改号码或拆分时,您会怎么做?使用业务键作为主键几乎总是错误的。

你上面的总结是另一个例子。

于 2009-08-19T22:42:08.307 回答
0
  1. 数据库未标准化。它可能被部分归一化。结果,您会发现更多的可扩展性错误和限制。

  2. 国家然后国家然后城市的层次结构很好。您不需要像某些人建议的那样多对多的附加表。上述城市(以及美国的许多城市)在三个州繁衍。

  3. 通过将 CountryCode 和 AreaCode 串联在一个列中,您已经破坏了基本的数据库规则,更不用说在每次访问时添加代码了。此外,CountryCode 未标准化。

  4. 问题是 CountryCode+AreaCode 对于城市的键来说是一个糟糕的选择。实际上,它与城市几乎没有关系,它适用于大片土地。如果将 City 的含义更改为 town(例如,您的公司开始收集大城镇的数据),则 db 将完全崩溃。

  5. 魔术师有唯一接近正确的答案,这将使您摆脱由于缺乏规范化而导致的当前限制。说魔术师的答案是标准化的是不准确的;这是标识符的正确选择,在这种情况下形成了层次结构。但我会删除“id”列,因为它们是不必要的,100% 冗余列,100% 冗余索引。char() 列本身很好,对于 PK(复合键)也很好。请记住,无论如何您都需要 char() 列上的索引,以确保它是唯一的。

    • 如果你有这个,关系结构和关系标识符,你的问题就不会存在。
    • 并且您的可怜的用户不必弄清楚愚蠢的事情或跟踪无意义的标识符。他们只是自然地陈述:State.Name、City.Name、ReadingType、Data ...。
  6. 当您到达层次结构的下端(城市)时,复合 PK 变得繁重(3 x CHAR(20) ),我不想将它带到数据表中(尤其是如果每天有 CSV 导入以及每个城市的许多读数或行)。因此,仅对于 City,我会添加一个代理键,作为 PK。

  7. 但是对于发布的 DDL,即使它是,没有规范化数据库并使用关系标识符,是的,城市的 PK 是不正确的。它应该是 (idStates, idAreaCode),而不是相反。这将解决您的问题。

顺便说一句,非常糟糕的命名。

于 2010-11-10T12:26:06.453 回答
0

我建议向 Cities 表添加一个新的主键字段,该字段将简单地自动递增。KISS 方法(保持简单)。

在我看来,任何其他解决方案都很麻烦且令人困惑。

于 2009-08-12T04:46:20.600 回答