【问题标题】:Difficulty in database table normalization (2NF)数据库表规范化困难(2NF)
【发布时间】:2015-06-20 23:56:28
【问题描述】:

下面的表格设计(完整架构见下文)有很多不足之处,也造成了很多困难,但我不知道如何最好地规范化它们。这些表格的用途是:

  1. ICD9 — 提供 CICD9CDESC 组合的主查找。每个组合在ICD9 表中都是唯一的&hellip 不能重复。
  2. ICD9 表中唯一键 (CICD9,CDESC) 的更改将级联到 DX 表。 DX 表永远不会有ICD9 表中未表示的 (CICD9,CDESC) 组合。
  3. DX -- 此表中的记录提供了有关此人在特定时间的特定信息。此表中的记录不能基于 Key(CICD9,CDESC,GROUPID,TPOSTED) 进行复制,其中 GROUPID 是个人唯一的,TPOSTED 是创建记录的时间。
  4. 如下所示,DX 表包含与具有唯一键 (CICD9,CDESC,GROUPID,TPOSTED) 的记录相关的非键信息。

由于数据错误,有时会在 ICD9DX 中创建重复记录。例如:

ICD9
|CICD9|CDESC    |
|1234 |"  TEST1"|
|1234 |"TEST1"  |

DX
|CICD9|CDESC   |GROUPID|TPOSTED |
|1234 |"  TEST1|H      |12301212|
|1234 |"TEST1" |H      |12301212|

为了解决重复数据,更新ICD9表,修改CDESC,使" TEST1"变为"TEST1"。因为这是 Key(CICD9,CDESC) 的一部分,所以在 DX 中的外键上设置的级联将导致 DX 中的 CDESC 列也发生变化。此更改可能会导致DX 表中的记录与DX 表中的其他记录发生冲突,并违反DX 表中Key(CICD9,CDESC,GROUPID,TPOSTED) 上的不允许重复。

我可以删除ICD9 中的重复记录,我已经完成了这项工作。但我需要保留DX 中的其余数据(如RESOLVEDTREATED),所以我不能只删除其中一行。我还需要保留唯一的密钥(CICD9,CDESC,GROUPID,TPOSTED)。有人建议这需要标准化,但我不知道该怎么做。

      CREATE TABLE icd9
(
  recid serial NOT NULL,
  cicd9 character varying(8),
  cdesc character varying(80) NOT NULL,
  "timestamp" timestamp without time zone DEFAULT now(),
  modified timestamp without time zone DEFAULT now(),
  chronic boolean,
  common boolean,
  CONSTRAINT pk_icd9_recid PRIMARY KEY (recid),
  CONSTRAINT constraint_cdesc UNIQUE (cicd9, cdesc),
  CONSTRAINT desccheck CHECK (cdesc::text <> ''::text)
)
WITH (
  OIDS=FALSE
);


     CREATE TABLE dx
(
  recid serial NOT NULL,
  cpatient character varying(33) NOT NULL,
  cicd9 character varying(8),
  cdesc character varying(80) NOT NULL,
  tposted timestamp without time zone NOT NULL,
  "timestamp" timestamp without time zone DEFAULT now(),
  modified timestamp without time zone DEFAULT now(),
  resolved boolean DEFAULT false,
  treated boolean DEFAULT false,
  chronic boolean DEFAULT false,
  groupid character varying(33) NOT NULL,
  service integer DEFAULT 0,
  explanation text,
  pmh boolean DEFAULT false,
  CONSTRAINT pk_dx_recid PRIMARY KEY (recid),
  CONSTRAINT dx_cpatient_fkey FOREIGN KEY (cpatient)
      REFERENCES patients (cpatient) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE RESTRICT,
  CONSTRAINT dx_groupid_fkey FOREIGN KEY (groupid)
      REFERENCES charts (groupid) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE RESTRICT,
  CONSTRAINT fk_icd9 FOREIGN KEY (cicd9, cdesc)
      REFERENCES icd9 (cicd9, cdesc) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED,
  CONSTRAINT noduplicate_dx UNIQUE (cicd9, cdesc, groupid, tposted),
  CONSTRAINT desccheck CHECK (cdesc::text <> ''::text),
  CONSTRAINT groupcheck CHECK (groupid::bpchar <> ''::bpchar),
  CONSTRAINT patientcheck CHECK (cpatient::bpchar <> ''::bpchar)
)
WITH (
  OIDS=FALSE
);

【问题讨论】:

  • @joDouglass 是的。当在 ICD9 表中创建重复记录时,icd9 中的约束确实会触发——我使用存储过程在失败前检测和删除。然而,“坏数据”通常只是拼写错误或描述不足——实际上并不是“错误”的意思,只是写得不好。所以删除 Dx 中的子记录不是一个可接受的解决方案,ICD9 表中的任何更改都需要将其 Dx 子记录移至“更好”的 ICD9 记录。本质上,我需要避免在 Dx 中丢失数据,纠正它,是的,丢失它,不。 :)
  • 顺便说一句:所有这些时间戳在 icd9 表中的作用是什么?您是否打算允许码本的多个版本/修订共存?
  • @wildplasser 原始记录将具有“创建”时间戳,以及何时(或是否)发生任何更改的时间戳。这在将记录备份到另一台机器时使用。 tposted 时间戳是特定的,并且永远不会更改,因为它不是“容器”的一部分,而是“数据”的一部分。有没有更好的办法?谢谢。

标签: database postgresql database-design


【解决方案1】:

简化更新和删除

首先,由于您使用的是代理主键,因此您的外键应该指向那些,而不是您的代理键所代表的唯一键。使用代理键的好处之一就是,当您拥有一个唯一键(其中一个或多个列可能会更改)时,它会使生活变得更加轻松;您不再需要级联更新,因为这些字段只保存在一个地方。您已经有合适的独特约束来管理数据质量。

dx 中删除cicd9cdesc 列。添加一个名为idc9_recid 的列。更改 dx 中的唯一约束以考虑此更改。理想情况下,您也应该在数据库的其他地方进行此更改。

那么,您应该能够删除您添加的一些过度设计。我通常会大量使用触发器,因为您一直将其描述为出现问题的警告信号。进行这些更改,然后考虑真正需要什么。您需要做的就是这样的事情:

update dx
set idc9_recid = 25
where idc9_recid = 12;

delete idc9
where recid = 12;

显然,您需要确定您感兴趣的行才能确定这些 ID - 因此您实际上可能有一个查询首先获取 ID,或者有一个选择正确 ID 的子查询.但无论如何,它比多个触发器要简单得多,依靠约束来设置更新等失败。无论是从开发的角度来看,还是从支持的角度来看,都是如此。

绕过唯一约束错误

关于不良数据/重复问题:简而言之,问题是由于数据质量差,您有时会得到两条 icd9 记录,它们实际上应该是同一条记录。这些都可以在dx 中有子记录,虽然您需要dx 中的唯一约束保持不变,但您还需要保留dx 中其他字段中的数据。答案是将dx拆分成两张表,将dx中不依赖唯一键的字段全部移到新表中,然后将新表中的外键包含回dx .

示例模型

提醒一句,这不一定是正确的设计,下面是一个简单的模型,可以帮助您将上述两个建议可视化:

(我使用draw.io 制作模型 - 如果您没有任何其他建模软件,强烈建议您使用它,因为您可以在将更改实际应用到数据库之前使用它来完成更改。它会如果您需要回来在这里提出更多问题,我们会很有帮助。)


从这里去哪里

我怀疑有人向您提到规范化的原因是很明显,这个数据库是在没有遵循规范化过程的情况下创建的。 不要只实现我上面给你的。它可能会帮助你解决这个问题,但我怀疑即使在你的数据库的这个小样本中也存在其他问题,并且不可避免地会出现是其他地方的进一步问题。

首先,您在dx 中的唯一键绝对正确吗?你真的不能在同一 idc9、同一组和同一时间发布两条记录吗?即使是patients 中的不同记录?或者该表中不应该有patients 的外键? (请注意,这些只是示例,可以让您思考 - 无需在这里回答。)

您肯定需要考虑所有已移至dx_detail 的字段依赖于哪个键。可能是有些属于dx,有些属于dx_detail——但我不能告诉你是哪一个,因为我不知道数据的含义。

所以:最重要的是你了解数据建模和规范化 - there are lots of resources online(这是 7 个链接,对吧那里!)。或者,如果你可以向你的雇主解释说,在你拿起这么大的东西之前你真的需要一些培训——这并不丢人,数据建模是它自己的学科。我参加了为期一周的数据建模专业课程,解释了创建概念数据模型的过程,然后转向逻辑数据模型并应用规范化规则以达到 Boyce-Codd 范式。这是无价的。

简而言之,您需要考虑您的数据代表什么,并且您需要考虑什么实际上取决于什么。通过逐步规范化过程执行此操作有助于避免遗漏任何内容,尤其是当您不熟悉该过程时。

如果可能,我还建议您修改表和列,使其具有真正有意义的名称。除了帮助出现并需要支持该系统或从中读取一些数据的其他人之外,您可能会发现它可以帮助您在处理数据时思考数据的含义。再说一次,如果您需要回来在这里寻求帮助,它会让事情变得更简单。即使您在数据库中更改它为时已晚,也可能值得制作一个图表,其中包含数据库级别的名称以及每个名称的描述性术语或短语;当您所建模的内容的含义显而易见时,数据建模会容易得多。

【讨论】:

    猜你喜欢
    • 2016-08-12
    • 2016-04-02
    • 2014-12-14
    • 1970-01-01
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-06
    相关资源
    最近更新 更多