正确使用查询表


25

我在弄清楚如何为何时何地在数据库中使用查找表放置良好的边界时遇到了麻烦。我看过的大多数资料都说我永远不会有太多,但是在某些时候,似乎数据库会被分解成很多部分,尽管它可能是有效的,但不再可管理。这是我正在使用的东西的综合示例:

假设我有一个名为“雇员”的表:

ID  LName   FName   Gender  Position
1   Doe     John    Male    Manager
2   Doe     Jane    Female  Sales
3   Smith   John    Male    Sales

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

ID  Position
1   Manager
2   Sales

但是,在信息变得难以管理之前,我可以继续将信息分解为较小的查找表吗?我可以创建一个性别表,并在单独的查找表中将1对应于Male,将2对应于Female。我什至可以将LNames和FNames放入表中。所有“ John”条目都被外键1替换,该外键指向FName表,该表说ID为1对应于John。但是,如果您像这样在这个兔子洞中走得太远,那么Employees表就会变成一堆外键:

ID  LName   FName   Gender  Position
1   1       1       1       1
2   1       2       2       2
3   2       1       1       2

尽管这可能使服务器处理效率提高或降低,但对于试图维护该服务器的普通人来说这当然是不可读的,这使应用程序开发人员尝试访问它变得更加困难。所以,我真正的问题是距离有多远?在某处是否有针对此类事情的“最佳实践”或一套良好的准则?我在网上找不到任何信息,可以针对我遇到的这个特定问题确定一套良好且有用的指南。数据库设计对我来说是旧帽子,但是良好的数据库设计是非常新的,因此过于技术性的答案可能会困扰我。任何帮助,将不胜感激!


5
使用“查找”表是一回事。用ID号替换文本是完全不同的事情。
Mike Sherrill'Cat

1
性别可能并不总是固定为2个值!现在我们有了性别过渡,也就是说,应用程序可能不需要其他类别,例如“男生女”或“男生女”。

@Mike,好评!
Walter Mitty

在我的商店中,思想家仅在不透露男性,女性,变性者四个选择之后就停下来了。
kevinsky 2013年

Answers:


22

但是,在信息变得难以管理之前,我可以继续将信息分解为较小的查找表吗?我可以创建一个性别表,并在单独的查找表中将1对应于Male,将2对应于Female。

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

从此表开始。

ID  LName   FName   Gender  Position
1   Doe     John    Male    Manager
2   Doe     Jane    Female  Sales
3   Smith   John    Male    Sales

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

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);

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

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

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


2
我不知道你能做到所有!您的方法的工作方式很漂亮。谢谢!
布莱德纳(Brad Turner)

4
我加入DBA Stack Exchange只是为了投票赞成这个答案。这是美丽的,从来没有发生过。谢谢!
CindyH 2013年

我赞赏用于填充查找表的方法。我阅读此问题的原因是,查看查找表上的代理键是否有好处,我看不到。您为我确认了单个文本字段与它看起来一样好和有用。谢谢。
Sinthia V

8

在您的“雇员”表中,我仅查找“位置”,因为它只能扩展有限的一组数据。

  • 性别是自我描述(例如MF),限制为2个值,并且可以通过CHECK约束强制实施。您不会添加新的性别(忽略政治正确性)
  • 名字“ John”不是有限的,受限制的数据集的一部分:潜在的数据集非常大,实际上是无限的,因此不应该查找

如果要添加新的职位,您只需在查询表中添加一行即可。这也消除了数据修改异常,这是归一化的一点

此外,一旦拥有一百万名员工,则存储tinyint PositionID的效率将比varchar更高。

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

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

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

1
将性别放入查询表的一种可能原因是本地化。
a_horse_with_no_name 2011年

1
“性别...(例如M或F),限制为2个值...忽略政治正确性锁”-具有讽刺意味的是,正是您似乎憎恶的同一政治正确性导致人们错误地“性别”(男性”,“女性”)的意思是“性别”(“男性”,“女性”)。如果上下文是语法性别,那么通常会有两个以上的值。如果上下文是在记录新生儿的性别,则至少有四个值(“尚未经过正式评估”和“官方评估尚无定论”)。ps我不是要听起来刺耳,我很讽刺:)
一天,2012年

4
@onedaywhen:名为“ Sex”的列的正确值为“ Yes please”。除非你是英国
GBN

术语“香气”在这里被误用,因为该术语具有与归一化相关的不同特定含义,并且该链接是不合适的。
2014年

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);

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

  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);

看起来不错:一次选择即可获得具有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

现在尝试选择案例的所有属性,如果其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  

即使您使用更优雅的方式来处理此问题,这也很痛苦。但是,通过分解一个案例仅具有一个property_id的属性,可以添加一些反规范化,这可能会更好。

要找出表太多或不足的情况,请尝试向数据库查询应用程序的问题,然后使用报告和年度分析。


5
ID号与规范化无关。仅仅因为每个表都有一个ID号并不意味着它在5NF甚至3NF中。这仅意味着您必须进行大量联接才能从该表中获取可用数据。
Mike Sherrill'Cat
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.