数据库表设计问题

时间:2016-11-18 19:27:07

标签: sql sql-server database database-design

我是DB Design的新手,我最近继承了为现有设计添加一些新属性的责任。

以下是当前表格的示例:

提交表:

ID (int)
Subject (text)
Processed (bit)
SubmissionDate (datetime)
Submitted (bit)
...

新要求是:

  1. 提交可以标记为有效无效

  2. 当提交标记为无效时,必须提供原因。 (因此提交内容可能有 InvalidReason

  3. 提交内容可以相互关联,以便:多个有效提交内容可以设置为"替换"对于无效的提交。
  4. 所以我现在采用了 easy 解决方案,只是直接将新属性添加到提交表中,使其看起来像这样:

    新提交表:

    ID (int)
    Subject (text)
    Processed (bit)
    SubmissionDate (datetime)
    Submitted (bit)
    ...
    IsValid (bit)
    InvalidReason (text)
    ReplacedSubmissionID (int)
    

    这一切都很好,但看起来有点奇怪:

    1. InvalidReason 作为大多数提交的 NULL 列。
    2. ReplacedSubmissionID 作为大多数提交的 NULL 列。
    3. 如果我理解正常化, InvalidReason 可能过渡依赖于 IsValid 位。
    4. 似乎某些属性应该被提取到一个单独的表中,但我不知道如何使用这些要求创建该设计。

      这个单表设计好吗?任何人都有更好的替代想法吗?

3 个答案:

答案 0 :(得分:2)

你是否应该有一个单独的表设计真的取决于 1)您将如何查询数据 2)结果表中有多少数据最终可能为NULL。

在你的情况下它可能没问题,但它又取决于#1。如果您要单独查询以获取有关无效提交的信息,您可能需要创建一个单独的表来引用无效提交的ID以及原因:

New table: InvalidSubmissionInfo
Id (int) (of invalid submissions; will have FK contraint on Submission table)
InvalidReason (string)

此外,如果您要单独查询已更换的提交内容,您可能希望只为这些提供一个表格:

New table: ReplacementSubmissions
Id (int) (of the replacement submissions; will have FK contraint on Submission table)
ReplacedSubmissionId (int) (of what got replaced; will have FK constraint on submission table)

要获取其余信息,您仍需要加入提交表。

所有这一切都表示你不需要将它分成多个表格。具有NULL值仅占用1位内存,这不是坏事。如果您每次都需要查询并返回一个完整的Submission记录,那么将这些信息压缩到一个表中会更有意义。

答案 1 :(得分:1)

单表设计对我来说很好看,它应该适用于你的情况。

如果您不喜欢NULLS,可以将空字符串的默认值和ReplacedSubmissionID设置为0.默认值在数据库设计中始终是首选。 使用空字符串或默认值将使您的数据看起来更清晰。

请记住,如果添加默认值,您可能需要更改查询以获得正确的结果。

例如: - 获取未被替换的提交>

Select * from tblSubmission where ReplacedSubmissionID = 0

答案 2 :(得分:1)

不要害怕加入。寻找将所有内容放在一个表中的方法充其量只是浪费时间,最糟糕的是导致一个错综复杂的,无法维护的混乱。

你对 InvalidReason IsValid 是正确的。但是,您错过了 SubmittedDate 已提交

每当对将以某种方式处理并经历连续状态更改的实体进行建模时,这些状态实际上应该放在单独的表中。任何有关状态变化的信息 - 日期,变更原因,授权等 - 将对国家而不是整个实体具有功能依赖性,因此试图使状态信息成为实体元组的一部分将在2nf失败。

这个问题会在您的问题中显示出来。您已将已提交 SubmittedDate 合并到元组中。现在您有另一个要添加的状态。如果您已将提交数据标准化,则可以简单地添加另一个状态并继续。

create table StateDefs(
  ID      int  auto_generated primary key,
  Name    varchar( 16 ) not null, -- 'Submitted', 'Processed', 'Rejected', etc.
  ...     -- any other data concerning states

);

create table Submissions(
  ID      int  auto_generated primary key,
  Subject varchar( 128 ) not null,
  ...     -- other data
);

create table SubmissionStates(
  SubID   int  not null references Submissions( ID ),
  State   int  not null references StateDefs( ID ),
  When    date not null,
  Description varchar( 128 )
);

这表明状态包含日期和打开文本字段以放置任何其他信息。这可能适合您的需求。如果不同的状态需要不同的数据,您可能必须(喘气)创建其他状态表。无论您的需求是什么。

您可以将提交的第一个状态插入表中,并在状态更改时更新该记录。但是你失去了状态变化的历史,这是有用的信息。因此,每次州的变化都会要求每次都有新记录。阅读提交的历史将很容易。阅读当前状态将更加困难。

但并不太难:

select  ss.*
from    SubmissionStates ss
where   ss.SubID = :SubID
    and ss.When =(
        select Max( When )
        from   SubmissionStates
        where  SubID = ss.SubID
            and When <= Today() );

这将查找当前行,即具有最新日期的行。要查找在特定日期生效的状态,请将 Today()更改为:AsOf ,并将感兴趣的日期放在该变量中。将当前日期存储在该变量中将返回当前状态,以便您可以使用相同的查询来查找当前或过去的数据。