数据库规范化 - 这是正确的做法吗?

时间:2014-04-25 18:52:21

标签: sql sql-server database-normalization

所以我和我的团队成员之间有一个关于数据库设计的论点,我们正在做的另一个数据库规范化。

外部数据仓库具有以下平台:

Customer

customerID int not null unique pk auto_increment
customerName varchar(255) not null
.....
whole lot of columns here in a flat manner 
   (which should go to the appropriate tables when normalized)

当前数据位于此外部表(链接服务器)中。

我们的任务是创建一个程序,根据历史数据为客户创建新的报价。

我的队友设计了以下两个表格:

CustomerHistory

c_id int not null unique pk auto_increment
customerID int not null
customerName varchar(255) not null

Quote

quoteID int not null unique pk auto_increment
c_id int not null unique pk auto_increment
dtCreateDate datetime now()

当我看到这个时,我很震惊。

你怎么能忽略一个完全有效的PK并在它上面创建一个新的?

即使您这样做了,如果没有某种类型的日期时间/时间戳指向更改,您怎么能这样做?

我的队友解释说:

  

我无法信任来自数据仓库的数据,因此我创建了新的ID

当被问及跟踪时间变化时,他回答说:

  

没有必要,因为我在每次更改时拍摄数据库的快照

我很惊讶。

然后我提出了以下解决方案:

CustomerHistory

customerID int not null
customerName varchar(255) not null
dtChanged datetime
composite key on customerID and dtChanged

这样,我想,我们可以在表格中查询任何给定的报价以及与正确的客户和他的名字签发的时间。

我的队友一直在争论他的方法是“安全的”,我们已经将项目推迟了两周。

拜托,你能帮我们解决这个问题吗?

如果我错了,我真的很有兴趣了解为什么,如果我说得对,对我有好处;)

修改 假设: 1.在任何时候我都需要在实际创建报价的customerName上提供quote。 2.访问Customer表只适用于CURRENT客户(而不是过去) 3.能够跟踪customerName更改。

2 个答案:

答案 0 :(得分:0)

在我看来,你现在将有三张桌子:

Customer
CustomerHistory
Quote

我认为你的伴侣有正确的想法。客户有customerId个主键。 CustomerHistory使用customerId作为外键,因此在这种情况下它需要它自己的主键(c_id?我可能会尝试将它命名为更合理的东西id - 停止使用smurf命名!)

这会使您的Quote表格使用历史记录的密钥作为外键c_id,并为自己添加新的主键quoteId

但是,

CustomerHistory不需要customerName字段,因为这已经存在于您的Customer表格中。

如果您不需要CustomerHistoryCustomer成为两个单独的表,那么您是对的 - 您可以相信NOT NULLUNIQUE限制只需Customer - 而且您不需要为其添加额外的主键。 UNIQUE保证该字段是唯一标识符 - 因此,如果这是他所不信任的字段,那么然后告诉他他错了:P然而,如果你最终得到了上面的三张桌子,那么他并没有完全错,但是他的理由是“不信任”#34;某些事情没有意义。

编辑:

从特定日期范围获取特定客户报价的示例查询将如下所示:

SELECT CustomerHistory.customerName, Quote.*
FROM CustomerHistory
INNER JOIN Quote ON (CustomerHistory.c_id = Quote.c_id)
WHERE CustomerHistory.createdOn BETWEEN 'Jan 1, 2010' AND 'Jan 30, 2010'
AND CustomerHistory.customerId = 5000

您的复合键可能会略微更改查询 - 但老实说,我不认为答案是"正确"或"错误"。当他说他不需要时间戳时,我不确定你的伴侣是什么意思,因为他正在拍摄数据库的快照...你两个拍摄快照时,您应该记录拍摄快照时每个字段的日期。

我真的只是认为复合键过于复杂,而不是必要 - 但这是我的个人观点。如果它适用于您的情况,并且数据正是您在完成更改后正在寻找的 - 那么重要的是;)我怀疑两者都会有类似的表现,假设您有指数设定得非常好。

答案 1 :(得分:0)

这是一个讨论主题,你不会在这里得到“正确答案”。我所有的问题都是要问你的队友。

对我来说,关键因素是:当他说他“不信任来自数据仓库的数据”时,他的意思是什么?

  • 客户ID在仓库中是唯一的吗?
  • 可以将同一个CustomerID分配给多个客户吗?
  • 给定个人的CustomerID是否可以更改(相同的Id,意味着随时间的变化)?
  • 您是否从多个来源获得CustomerID(允许以上两种情况)?

如果CustomerID可以用于唯一标识每个客户随着时间的推移,那么就使用它。如果不能,后续问题是:

  • 您是否需要跟踪从仓库中导入的客户信息?
  • 如果没有,那么您可能不需要(原始且不可靠)的CustomerID,将其扔掉。
  • 如果您这样做,通过更改ID,您如何在仓库的多个导入中跟踪同一客户的数据?

[编辑后更新]

听起来像是一种记录情况......

  • 数据源(仓库)包含CustomerID和Name
  • CustomerID不会更改,但名称会更改
  • 您必须定期将数据从仓库复制到新系统
  • 您需要跟踪与副本制作的时间点*相关的姓名*
  • 因此,Name不是CustomerID的属性(在新系统中)
  • 相反,它听起来像Name是引用的属性

我,我不会再使用另一个代理键,我会使用像

这样的东西
CUSTOMERHISTORY

CustomerId  int  not null  PK

QUOTE
CustomerId  int  not null  FK  PK 1/2
LoadedAt  datetime  not null  PK 2/2
Name  varchar(255)  no null

这确实假设您不必处理在完全相同的时间点提交的多个引号 - 如果这是真的,那么您可以从quoteID中受益......如果事实上您确实需要此日志记录表上的主键。 (日志表通常是简单的数据转储,带有索引用于检索,但不需要唯一标识并检索任何一行。)

这仍然无法回答“不信任主键”问题。如果它是在Microsoft SQL Server中实现的真实和正确的主键,那么他不相信它提供真实和准确的信息要么(a)完全混淆和误导,让自己成为新的承包商,或(b)其余的过去15年来一直依赖它的我们一直都是错的。 (当然,他的架构可行,但它不必要地复杂。)