为公司和股东设计数据库结构

Jan*_*Doe 5 postgresql foreign-key database-design primary-key

我正在做一个个人项目,我想在其中创建一个数据库,其中包含每个上市公司的每个所有者的信息。例如,说“Sears INC”,程序将获取“Sears INC”中每个所有者的信息。这就是想法。但是我很难构建这个数据库。我对这个主题只有有限的经验和知识,所以任何指导将不胜感激。

现在我首先考虑制作一个名为公司的表格,并为它们提供所有唯一的 ID,并为每个公司制作一张表格。然后在这些表中将是所有所有者及其唯一 ID。这将链接到他们的信息。我试图在这里形象化:

这里

现在我知道为每个公司创建一个表会很乏味,但我想不出任何其他方法来做到这一点。众所周知,每个公司可能有相同的所有者,因此使用 Owner_ID 来识别每个所有者是有意义的。

我在一个 CVS 文件中有所有其他数据,在我构建了数据库之后,我可以很容易地将这些数据导入到 PostgreSQL 中。

任何帮助将不胜感激。

TL:博士;创建一个可按公司名称搜索的股票所有者数据库,并且需要帮助来构建数据库以实现最大效率。

一个人只能有一个电话和一个地址。公司也是如此,因为我制作了一个脚本,可以根据这些信息查找电话。我区分知道一个实体是公司还是个人的方法是使用名为 F_Org 的列,该列对于公司来说将大于 4 位数字。这一切都由脚本处理。

MDC*_*CCL 8

我在数据库设计答案中经常涉及的一个主题是,在 (a) 考虑关系数据库的技术方面之前——例如,表、列等的声明——强烈建议首先 (b)精确定义感兴趣的业务环境的所有特征。这意味着识别实体类型、它们的属性以及它们之间存在的每个重要关联。

关于所述元素的一组定义通常被称为业务规则,它们构成了适用的概念模型。

此外,我认为还有其他关于 (1) 数据管理和 (2) 关系设计和操作的重要主题 (3) 可能有助于澄清您在问题中以及通过评论提出的某些方面。随着答案的进行,我将整合所有这些要点。

概念层面

让我们开始重新评估相关的抽象概念级别。在这方面,制定一系列描述业务环境的陈述很重要。因此,在您的特定情况下,请刻意保持业务规则尽可能简单:

  • 一个公司主要通过其标识编号
  • 一个公司是由交替的标识名称
  • 一个公司是在其成立FoundingDate
  • 一个主要通过他或她的ID 来识别
  • 一个交替由他或她的组合标识的名字姓氏GenderCode出生日期出生地
  • 一个人只保留一个地址
  • 一个人只使用一个电话号码
  • 一家公司由一对多的人拥有
  • 一个拥有零个或多个公司

说明性 IDEF1X 模型

然后,基于业务规则上述配制时,可以创建一个故意和相对简单的IDEF1X一个等所示的一个模型图1中,为了有一个图形装置,它整合的最在单个资源的显著特征:

图 1 - 公司和股东简化的 IDEF1X 模型

一个 用于信息建模集成定义IDEF1X)是被确立为一个非常可取的数据建模技术标准是由美国在1993年12月美国国家标准与技术研究院(NIST)。它完全基于 (a) 关系模型的创始人,即EF Codd 博士撰写的一些早期理论著作;关于 (b)实体关系视图,由PP Chen 博士开发;以及 (c) 逻辑数据库设计技术,由 Robert G. Brown 创建。

如图所示,在 IDEF1X 模型中,我们可以开始在逻辑级别包含技术考虑因素,例如必须通过 PRIMARY、ALTERNATE 和 FOREIGN KEY 定义(为简洁起见:分别为 PK、AK 和 FK)约束的属性或属性的指示。

前面讨论的最后两条业务规则表明,实体类型Company和之间Person存在多对多 (M:N) 关联或关系,这揭示了我称之为关联实体类型的存在CompanyOwner(这可能很好计价CompanyShareHolder或者一些菲茨更好地在商业领域中使用的术语)。

在当前的建模练习中特别重要的属性之一是我命名的属性StockShareQuantity(描绘了Shares的总和),因为它完全出现在CompanyOwner关联的上下文中;这样,它既不属于 aPerson也不属于a Company,而是属于这两种独立的实体类型之间可能出现的连接。

按照规定,每个CompanyOwner出现或实例都由其CompanyIdOwnerId值的组合标识,因此这些属性在实体类型描述中作为复合 PK 突出显示。该CompanyOwner.CompanyId属性用指向 的 FK 来区分Company.CompanyId,而CompanyOwner.OwnerId用引用 的 FK 来表示Person.PersonId

为了描绘一个更精细的Person实体类型,我分解Person.NameFirstNameLastName,并包含了列GenderCodeBirthDateBirthPlace。总之,所有这些属性的组合值通常用于标识Person某些业务场景中的 ,但您可能只是对跟踪 感兴趣Person.FullName,如果这满足您的数据使用要求,因此您不必遵循关于正在讨论的特定数据库的相同方法。

说明性逻辑 SQL-DDL 结构

随后,通过数据库管理系统提供的数据定义语言来声明逻辑结构相对更容易,在这种情况下是PostgreSQL,如下例所示:

-- You have to determine which are the most fitting 
-- data types and sizes for all the table columns 
-- depending on the business context characteristics.

-- Also, you should make accurate tests to define the
-- most convenient physical implementation settings; e.g.,
-- a good INDEXing strategy based on query tendencies.

-- As one would expect, you are free to make use of 
-- your preferred (or required) naming conventions.

CREATE TABLE Company ( -- Stands for an independent entity type
    CompanyId       INT       NOT NULL, -- You may like to set it with the SERIAL type to retain system-assigned and system-generated surrogate values.
    Name            TEXT      NOT NULL,
    FoundingDate    DATE      NOT NULL,
    Etcetera        TEXT      NOT NULL,
    CreatedDateTime TIMESTAMP NOT NULL DEFAULT NOW(), -- You may also configure it so that the DEFAULT value is set by the CURRENT_TIMESTAMP function, if appropriate. 
    --
    CONSTRAINT Company_PK PRIMARY KEY (CompanyId),
    CONSTRAINT Company_AK UNIQUE      (Name) -- Single-colum ALTERNATE KEY.
);

CREATE TABLE Person ( -- Denotes an independent entity type.
    PersonId        INT       NOT NULL, -- You may like to set it with the SERIAL type to retain system-assigned and system-generated surrogate values.
    FirstName       TEXT      NOT NULL,
    LastName        TEXT      NOT NULL,
    GenderCode      TEXT      NOT NULL,
    BirthDate       DATE      NOT NULL,
    BirthPlace      TEXT      NOT NULL,  
    Address         TEXT      NOT NULL,   
    PhoneNumber     TEXT      NOT NULL,     
    CreatedDateTime TIMESTAMP NOT NULL DEFAULT NOW(), -- You may also configure it so that the DEFAULT value is set by the CURRENT_TIMESTAMP function, if appropriate.
    --
    CONSTRAINT Person_PK PRIMARY KEY (PersonId),
    CONSTRAINT Person_AK UNIQUE      ( -- Composite ALTERNATE KEY.
        FirstName,
        LastName,
        GenderCode,
        BirthDate,
        BirthPlace
    )
);

CREATE TABLE CompanyOwner ( -- Represents an associative entity type or M:N association. Attaching an extra column to hold system-generated and system-assigned surrogate values to this table is superfluous.
    CompanyId          INT       NOT NULL,
    OwnerId            INT       NOT NULL,
    StockShareQuantity INT       NOT NULL,
    Etcetera           TEXT      NOT NULL,  
    CreatedDateTime    TIMESTAMP NOT NULL DEFAULT NOW(), -- You may also configure it so that the DEFAULT value is set by the CURRENT_TIMESTAMP function, if appropriate. 
    --
    CONSTRAINT CompanyOwner_PK          PRIMARY KEY (CompanyId, OwnerId), -- Composite PRIMARY KEY.
    CONSTRAINT CompanyOwnerToCompany_FK FOREIGN KEY (CompanyId)
        REFERENCES Company (CompanyId),
    CONSTRAINT CompanyOwnerToPerson_FK  FOREIGN KEY (OwnerId)
        REFERENCES Person (PersonId),
    CONSTRAINT StockShareQtyIsValid_CK  CHECK       (StockShareQuantity >= 0) -- Appears to be required.
);
Run Code Online (Sandbox Code Playgroud)

正如所阐述的,在这个有意且相对简单的逻辑结构b 中(与您的图表中描绘的非常相似,但有一些重要的区别):

  • 每个基表表示一个单独的实体类型,以防止歧义;
  • 代表各自实体类型的一个属性;
  • 为每一固定了一个特定的数据类型,以确保它保留的所有都属于一个特定的集合(您必须适应您的确切需要,从PostgreSQL提供的选项中选择最合适的类型) ,无论是 INT、TIMESTAMP、TEXT 等;和
  • 设置了多个约束c、d(以声明方式)以保证所有表中保留的符合概念模型中确定的规则。

b我上传了在 PostgreSQL 9.6 上运行的db<>fiddleSQL Fiddle,其中包含我定义的 DDL 结构和约束以及示例数据,以便您可以“实际”测试它。

c由于 (i) 数据的结构——即表、列和类型——以及 (ii) 强加在这种结构上以确保它只接受有效数据的约束——例如 PK、FK 和 CHECK,是 (iii) ) 关系数据库配置的两个不同但相关的因素,我建议 (iv) 将结构声明与约束声明分开——尽管 PostgreSQL 提供的 SQL 语言和方言允许声明“内联”列约束,如果您想选择该选项—。事实上,将约束定义移到 CREATE TABLE ... (...); 之外会更好。声明,因此这一点值得彻底评估。

d关于结构与约束分离的一个例外是,我实际上在“行内”列声明中修复了 NOT NULL,因为它是一种特殊的约束。为了使事情尽可能简短,允许 NULL 标记是 SQL 语言设计者引入的一种方法(在理论上被认为是有争议的),以尝试管理缺失信息问题,这是一个相当广泛的主题。反过来,NULL 标记意味着处理另一个称为三值逻辑的问题。根据关系理论,包含一个或多个包含 NULL 标记 (1) 的列的表不会表示一个数学关系——所说的标记是关于没有值的指标,那么它们不是域值——因此(2)这样的表在操作时不会“表现”为关系。因此,虽然列保持 NULL 标记是可能的,但我建议您研究有关这些主题的可靠材料,以便您可以做出明智的决定,了解与管理缺失数据有关的所有影响和方法。

Person.Address 列

如果您对操作Address列的某些编码部分感兴趣,让我们说PostCode,您可能想评估将其分解为几列并将相关信息移动到一个单独的表中,该Person表可能会通过带有 FK 约束的列。

为每个公司声明一个表?

您在评论中提出了以下想法:

正如我所说,设计会发生变化,因为 [...] 会有多家公司,人们可以拥有多家公司,因此会出现两次。我最初的问题是如何设计它。我考虑过为每家公司制作一张桌子,我认为这是我最后的选择,但我仍然没有其他指导或技巧来说明如何做到这一点。

,你应该不会为每个表公司,因为那将是非常不理想的。

在这方面,可以说保留在先前呈现的每个基表中的每一行都是关于属于某个类型的某个概念级实体的断言,即CompanyPersonCompanyOwner

因此,关于给定的行有限公司举行一次Company表和有关给定行的人保持只有一次Person表中。然后,因为一个可以拥有零,一个或多个公司,有关一个确切的连接各行的人相对于个别公司是封闭的只有一次CompanyOwner表中。

CompanyOwner表中的一行不包含有关Person的全部信息,它仅包含 (1)OwnerId列中的一个值 —约束为 FK — 引用 (2) 保留在Person.PersonId列中的一个值 —约束为PK—。某个CompanyOwner.OwnerId值可能会重复多次,因此也可能发生在某个CompanyOwner.CompanyId值上,但这没有问题。例如,(CompanyId, OwnerId)通过CompanyOwner复合 PK 定义防止了重复相同值组合的可能性。

衍生信息

当涉及到关系数据库时,一组基表绝对不是固定结构,因为除了易于扩展和适应之外,基表还有助于派生在设计时未配置的新表。

让我们假设您已经通过以下 INSERT 操作使用示例数据填充了数据库表:

INSERT INTO Company 
    (CompanyId, Name, FoundingDate, Etcetera)
VALUES
    (1748, 'Database Modeling Inc.', '1985-06-30', 'Foo'),
    (1750, 'Application Programming Co.', '1987-10-14', 'Bar');

INSERT INTO Person 
    (PersonId, FirstName, LastName, BirthDate, BirthPlace, GenderCode, Address, PhoneNumber)
VALUES
    (1, 'Edgar', 'Codd', '1923-08-19', 'Fortuneswell, UK', 'M', 'IBM Research Laboratory K01/282, 5600 Cottle Road, San Jose, CA, USA', '01-800-17-50-17-50'),
    (2, 'Alan', 'Turing', '1912-06-23', 'Maida Vale, UK','M', 'National Physical Laboratory, Hampton Road, Teddington, TW11 0LW, England', '01-800-17-48-17-48'),
    (3, 'Grace', 'Hopper', '1906-12-09', 'New York City, USA', 'F', 'Navy’s Office of Information Systems Planning, USA.', '01-800-17-50-17-50'),   
    (4, 'Diego', 'Velázquez', '1599-06-06', 'Seville, Spain', 'M', 'Palacio Real, Madrid, Spain', '01-800-17-50-17-50'),
    (5, 'Michelangelo', 'Buonarroti', '1475-03-06', 'Caprese, Italy', 'M', 'Sistine Chapel, Vatican City State', '01-800-17-50-17-50'); 

INSERT INTO CompanyOwner 
    (CompanyId, OwnerId, StockShareQuantity, Etcetera)
VALUES
    (1748, 1, 2500, 'U'),
    (1750, 1, 2500, 'V'),
    (1750, 2, 8000, 'W'),
    (1750, 3, 3580, 'X'),
    (1748, 4, 12899, 'Y'),
    (1750, 5, 12899, 'Z'); 
Run Code Online (Sandbox Code Playgroud)

之后,如果你想,例如,生成一个包含所有公司所有者数据的表,你可以声明一个像下面这样的视图:

CREATE VIEW CompanyAndOwner AS
    SELECT C.CompanyId,
           C.Name AS CompanyName,
           P.PersonId,
           P.FirstName,
           P.LastName,
           P.BirthDate,
           P.BirthPlace,
           P.GenderCode,
           P.Address,
           P.PhoneNumber,
          CO.StockShareQuantity,
           P.CreatedDateTime
        FROM Person P
        JOIN CompanyOwner CO
          ON CO.OwnerId = P.PersonId
        JOIN Company C
          ON C.CompanyId = CO.CompanyId;
Run Code Online (Sandbox Code Playgroud)

然后你可以表达连续的操作c直接从那个 VIEW 选择;例如:

SELECT *            
  FROM CompanyAndOwner 
 WHERE CompanyId = 1750;
Run Code Online (Sandbox Code Playgroud)

SELECT CompanyName,
       FirstName AS OwnerFirstName,
       LastName  AS OwnerLastName,
       StockShareQuantity
  FROM CompanyAndOwner 
 WHERE CompanyId = 1750; 
Run Code Online (Sandbox Code Playgroud)

SELECT * 
  FROM CompanyAndOwner 
 WHERE CompanyId = 1748;
Run Code Online (Sandbox Code Playgroud)

SELECT CompanyName,
       FirstName AS OwnerFirstName,
       LastName  AS OwnerLastName,
       StockShareQuantity
  FROM CompanyAndOwner 
 WHERE CompanyId = 1748;
Run Code Online (Sandbox Code Playgroud)

或者您也可以直接从基表中选择:

SELECT COUNT(OwnerId) AS OwnedCompaniesQuantity
   FROM CompanyOwner
  WHERE OwnerId = 1;
Run Code Online (Sandbox Code Playgroud)

等等。

e此处包含的所有数据操作操作都包含在之前链接的db<>fiddleSQL Fiddle中,以便您可以分析它们产生的结果集。

公司地址和电话号码

在我们通过评论进行的一系列审议中,我问您是否有兴趣保留属于Companies 的AddressesPhoneNumbers,您的回答如下:

一个人只能有一个电话和一个地址。公司也是如此,因为我制作了一个脚本,可以根据这些信息查找电话。我区分知道一个实体是公司还是个人的方法是使用名为 F_Org 的列,该列对于公司来说将大于 4 位数字。这一切都由脚本处理。

我不知道如果这意味着,地址(ES)电话号码一的公司都存储在应用程序组件(可能是一个文件,记录和字段,或者类似的东西),但是,如果你在实际上处理对于这些信息,您应该使用适合该工作的工具,即将这些方面包括在数据库结构中,并利用数据库管理系统(即 PostgreSQL)提供的工具,以便您可以管理相关数据最佳方式(例如,凭借基于关系代数和声明性约束的逻辑级操作,在物理级由强大的集合处理引擎支持)。

表示超类型-子类型关系的可行 DDL 结构扩展

因此,如果您确定,在您的业务领域

  • 同一地址事件可以由多个个人和/或公司实例保存,

  • 同一个PhoneNumber可以被多个Person和/或Company实例使用,

你可能会喜欢分析我列入图这个答案对方案中的问题,这是非常相似,所考虑的一个(与其他功能的,在超表示无论是组织还是个人亚型,人们角色业主组织,以及地址PHONENUMBERS与联组织人民通过超),因为它们可以被用来作为一个试探性的扩展的引用。