数据库中的规范化,以国家/地区为列

时间:2012-12-30 03:11:36

标签: sql database database-design normalization

这一直困扰着我一段时间,考虑一个包含这样的属性的表格:{ID, Value, Australia, India, France, Germany},其中ID是主键,Value是一些文字,比如汽车模型在Australia等每个属性下,India是与该值对应的汽车数量。

直觉上我知道通过{ID, Value, Cars-Manufactured, Country}设置这个的正确方法,但有人可以告诉我为什么这在数据库规范化方面是正确的吗?第一个表不符合哪种规范化。或者第一个表格也是正确的吗?

3 个答案:

答案 0 :(得分:4)

它违反的规则是“没有重复的群体”。这是第一范式的规则之一。

每个国家/地区的列都是重复组。每列下的数据是相同的数据,仅适用于不同的上下文。当那里只有一个价值 - 就像那个国家制造的汽车数量 - 这可能并不明显,也许它甚至有争议。但是假设我们需要为每个国家/地区提供两条信息,例如制造的数量和销售数量。现在该表有一组配对列:Australia_manufactured,Australia_sold,India_manufactured,India_sold,France_manufactured,France_sold等。您有一组两列重复多次。

有人可能会问,多个不同字段和重复组之间有什么区别? “India_manufactured,Australia_manufactured,France_manufactured”与“number_manufactured,price,description”有什么不同?不同之处在于,在第一种情况下,值的语义含义是相同的,所有不同的是上下文,即应用程序。在第二种情况下,语义含义是不同的。也就是说,很难想象一个查询或程序处理数据超出了一个微不足道的“找到最大的价值”或我们今天运行它处理number_manufactured,然后明天运行它完成相同的处理但是在销售价格。但我们可以很容易地想象今天在印度和明天在德国跑步。

当然有时它可能含糊不清。这就是数据库设计师为此付出巨大代价的原因。 : - )

好的,这是规则。该规则是否具有实用价值?

让我们考虑方案A,一个表:

model (model_id, description, india_manufactured, australia_manufactured, france_manufactured)

情景B,两个表格:

model (model_id, description)
production (model_id, country_code, manufactured)

情景A糟透了的原因有很多。这是最大的:

使用场景B,查询更加简单。我们不必将国家/地区硬编码到我们的程序或查询中。编写一个查询以接受国家/地区代码作为参数,并返回该国家/地区制造的每个模型的编号。在方案B中,简单:

select description, manufactured 
from model join production on model.model_id=production.model_id
where production.country_code=@country

易。现在用方案A来做。像:

select description,
  case when @country_code='IN' then india_manufactured
  when @country_code='AU' then australia_manufactured
  when @country_code='FR' then france_manufactured
  else null
  end as manufactured
from model

或者假设我们想要在所有国家生产的总量。情景B:

select description, sum(manufactured)
from model
join production on model.model_id=production.model_id

情景A:

select description, india_manufactured+australia_manufactured+france_manufactured
from model

(如果我们必须允许空值,可能会更复杂。)

我们可能在整个系统中有很多这样的查询。在现实生活中,许多人会比这更复杂,有多个如此凌乱的案例条款或杂耍多个栏目。现在假设我们添加另一个国家。在方案B中,这是零努力。我们可以添加和删除所有我们喜欢的国家,并且查询不会更改。但在方案A中,我们必须找到每个查询并进行更改。如果我们错过了一个,我们将不会得到任何编译错误或类似的东西。我们只是神秘地得到不正确的结果。

哦,顺便说一下,有时我们只想处理一些国家。比方说,有些国家有增值税,有些没有,或者其他什么。在方案B中,我们为此事实添加一列并对其进行测试。这只是“在country.country_code = production.country_code和country.vat = 1上加入国家/地区”。在方案A中,程序员几乎肯定会在每个查询中对特定国家/地区的列表进行硬编码。然后有人来了,看到查询X处理印度和法国,并查询Y进程法国和德国,并查询Z进程德国和新加坡,他可能不知道为什么。即使他知道,列表在每个查询中都是硬编码的,因此每次更新都需要更新每个查询,更改代码而不是更改数据。

假设我们遇到的查询只处理四个国家/地区中的三个。

哦,顺便说一句,

我们如何知道这是否是一个错误,有人在撰写查询时忘记了其中一个国家/或在添加新国家时错过了此查询;或者是否有某种原因导致该国被排除在外?

答案 1 :(得分:0)

第二种方法对您来说更好,因为您可以更清晰地了解数据,还可以避免INSERT DELETEUPDATE异常。 是的,使用第二种方法,您将获得更多的数字数据。

基本上,当您设计数据库时,通常的方法是3NF.

答案 2 :(得分:0)

Table COUNTRYANDCARS [MODEL (PK), AUSTRALIA, INDIA, FRANCE, GERMANY]

理想情况下,只有固定国家/地区时,上述方法才是正确的。

Table CARPRODUCTION [MODEL (PK), COUNTRY (PK), COUNT]

这将满足所有人。