正确使用查找表

Bra*_*ner 25 relational-theory

我无法确切地弄清楚如何为何时何地在数据库中使用查找表设置良好的边界。我看过的大多数资料都说我永远不会有太多,但在某些时候,似乎数据库会被分解成很多部分,虽然它可能是有效的,但它不再是可管理的。这是我正在使用的一个综合示例:

假设我有一个名为 Employees 的表:

ID  LName   FName   Gender  Position
1   Doe     John    Male    Manager
2   Doe     Jane    Female  Sales
3   Smith   John    Male    Sales
Run Code Online (Sandbox Code Playgroud)

假设数据更复杂并且包含数百行。我看到可以移动到查找表的最明显的事情是位置。我可以创建一个名为 Positions 的表,并将 Positions 表中的外键粘贴到 Position 列中的 Employees 表中。

ID  Position
1   Manager
2   Sales
Run Code Online (Sandbox Code Playgroud)

但是,在信息变得无法管理之前,我能在多大程度上继续将信息分解为更小的查找表?我可以创建一个性别表,并在单独的查找表中将 1 对应于男性,而 2 对应于女性。我什至可以将 LNames 和 FNames 放入表中。所有“John”条目都替换为指向 FName 表的外键 1,该表表示 ID 为 1 对应于 John。但是,如果您像这样深入这个兔子洞,那么您的 Employees 表就会变成一堆外键:

ID  LName   FName   Gender  Position
1   1       1       1       1
2   1       2       2       2
3   2       1       1       2
Run Code Online (Sandbox Code Playgroud)

虽然这对于服务器处理来说可能更有效也可能不会更有效,但这对于可能试图维护它的普通人来说肯定是不可读的,并且使应用程序开发人员试图访问它变得更加困难。所以,我真正的问题是多远太远了?是否有此类事情的“最佳实践”或某处的一套好的指导方针?我在网上找不到任何信息可以为我遇到的这个特定问题确定一套好的、有用的指导方针。数据库设计对我来说是老生常谈了,但 GOOD 数据库设计是非常新的,所以我可能无法回答过于技术性的答案。任何帮助,将不胜感激!

Mik*_*ll' 22

但是,在信息变得无法管理之前,我能在多大程度上继续将信息分解为更小的查找表?我可以创建一个性别表,并在单独的查找表中将 1 对应于男性,而 2 对应于女性。

你在混合两个不同的问题。一个问题是使用“查找”表;另一种是使用代理键(id 号)。

从这张表开始。

ID  LName   FName   Gender  Position
1   Doe     John    Male    Manager
2   Doe     Jane    Female  Sales
3   Smith   John    Male    Sales
Run Code Online (Sandbox Code Playgroud)

您可以为这样的职位创建一个“查找”表。

create table positions (
  pos_name varchar(10) primary key
);

insert into positions
select distinct position 
from employees;

alter table employees
add constraint emp_fk1
foreign key (position) 
  references positions (pos_name);
Run Code Online (Sandbox Code Playgroud)

您的原始表看起来与创建“查找”表之前完全一样。并且员工表不需要额外的联接就可以从中获取有用的、人类可读的数据。

使用“查找”表归结为:您的应用程序是否需要控制外键引用提供的输入值?如果是这样,那么您始终可以使用“查找”表。(无论它是否使用代理键。)

在某些情况下,您将能够在设计时完全填充该表。在其他情况下,用户需要能够在运行时向该表添加行。(并且您可能需要包括一些管理流程来审查新数据。)实际上具有ISO 标准的性别可以在设计时完全填充。可能必须在运行时添加国际在线产品订单的街道名称。

  • 我加入 DBA Stack Exchange 只是为了我可以对这个答案进行投票。这是美丽的,我从来没有想过。谢谢! (4认同)
  • 我不知道你能做到这一切!你的方法的工作方式有点漂亮。谢谢! (2认同)

gbn*_*gbn 8

在您的员工表中,我只会查找“职位”,因为它是一组可以扩展的有限数据。

  • 性别是自我描述的(比如MF),仅限于 2 个值,并且可以通过 CHECK 约束来强制执行。你不会添加新的性别(忽略政治正确的胡言乱语)
  • 名字“John”不是有限的、受限的数据集的一部分:潜在的数据集是巨大的,实际上是无限的,所以它不应该是一个查找

如果您想添加一个新位置,只需在查找表中添加一行即可。这也消除了数据修改异常,这是标准化的一个点

此外,一旦您拥有一百万员工,那么存储 tinyint PositionID 比 varchar 更有效。

让我们添加一个新列“工资货币”。我会在这里使用带有 CHF、GBP、EUR、USD 等键的查找表:我不会使用代理键。这可以通过 CHECK 约束(如 Gender)进行限制,但它是一组有限但可扩展的数据,如 Position。我举这个例子是因为我会使用自然键,即使它确实出现在一百万行员工数据中,尽管它是 char(3) 而不是 tinyint

因此,总而言之,您使用查找表

  1. 列中有有限但可扩展的集合数据
  2. 哪里不是自我描述
  3. 避免数据修改异常

  • @onedaywhen:名为“Sex”的列的正确值是“Yes please”。除非你是 [英国人](http://en.wikipedia.org/wiki/No_Sex_Please,_We%27re_British_%28film%29) (4认同)

kev*_*kio 5

答案是“视情况而定”。不是很令人满意,但有很多影响推动和拉动设计。如果您有应用程序程序员设计数据库,那么您描述的结构对他们有用,因为 ORM 隐藏了复杂性。当你写报告时,你会很费劲,必须加入十张桌子才能得到一个地址。

为用途、预期用途和可能的未来用途而设计。这就是您对业务流程的了解的地方。如果您正在为兽医业务设计数据库,那么对规模、用途和功能方向的合理假设将与高科技初创公司大不相同。

重复使用最喜欢的报价

“一位智者曾经告诉我“正常化直到它受到伤害,去正常化直到它起作用”。

在那里的某个地方是甜蜜的地方。我的经验是,如果您从不更改主键,那么在多个表中拥有一个键 ID 并不像某些人认为的那样严重。

以这个来自真实系统的高度规范化表的缩写示例为例

CREATE TABLE PROPERTY
(ID                          NUMBER(9)           NOT NULL);

CREATE TABLE PROPERTY_TYPE
(ID                          NUMBER(9)           NOT NULL);

CREATE TABLE PROPERTY_LOCALE 
PROPERTY_ID                  NUMBER(9)           NOT NULL,
(LOCALE_ID                   NUMBER(9)           NOT NULL,  --language 
VALUE                        VARCHAR2(200)       NOT NULL);

CREATE TABLE PROPERTY_DEPENDENCY
(PROPERTY_ID                 NUMBER(9)           NOT NULL,
 PARENT_PROPERTY_ID          NUMBER(9)                   ,
 PROPERTY_TYPE_ID            NUMBER(9)           NOT NULL);
Run Code Online (Sandbox Code Playgroud)

这些表设置了单个属性和父子属性的链接列表,并在此处使用

  CREATE TABLE CASE_PROPERTY
  (ID                        NUMBER(9)           NOT NULL,
  PARENT_ID                  NUMBER(9),
  CASE_ID                    NUMBER(9)           NOT NULL,
  PROPERTY_ID                NUMBER(9),
  PROPERTY_TYPE_ID           NUMBER(9)           NOT NULL);
Run Code Online (Sandbox Code Playgroud)

这看起来不错:在一次选择中获取具有 property_id 的所有案例

让我们从列表中挑选

 Select pl.value, pd.property_id
 from property_locale pl, property_dependency pd
 where pl.property_id = pd.property_id
 and pd.property_type_id = 2;  --example number
Run Code Online (Sandbox Code Playgroud)

现在尝试选择一个案例的所有属性,如果它的 property_types 为 3、4 和 5,或者不是...

SELECT   cp2.case_id,
         (SELECT   pl.VALUE
            FROM   case_property cp, property_locale pl
           WHERE       cp.property_id = pl.property_id
                   AND CP.PROPERTY_TYPE_ID = 2
                   AND pl.locale_id = 2
                   AND cp.case_id = cp2.case_id)
            AS VALUE1,
         (SELECT   pl.VALUE
            FROM   case_property cp, property_locale pl
           WHERE       cp.property_id = pl.property_id
                   AND CP.PROPERTY_TYPE_ID = 34
                   AND pl.locale_id = 2
                   AND cp.case_id = cp2.case_id)
            AS VALUE2,
         (SELECT   pl.VALUE
            FROM   case_property cp, property_locale pl
           WHERE       cp.property_id = pl.property_id
                   AND CP.PROPERTY_TYPE_ID = 4
                   AND pl.locale_id = 2
                   AND cp.case_id = cp2.case_id)
            AS VALUE3
  FROM   case_property cp2
 WHERE   cp2.case_id = 10293  
Run Code Online (Sandbox Code Playgroud)

这只是伤害......即使你使用更优雅的方式来处理这个问题。但是,通过打破一个案例只有一个 property_id 的属性来添加一些反规范化,这可能会好得多。

要了解何时有太多表或表不够,请尝试使用应用程序、报告和年度分析将使用的问题查询数据库。

  • ID 号与规范化无关。仅仅因为每个表都有一个 id 号并不意味着它在 5NF 中,甚至在 3NF 中。这只是意味着您必须执行*很多* 连接才能从该表中获取可用数据。 (5认同)