为纵向测量数据设计数据库

jes*_*WUT 5 database-design

我正在尝试根据我已有的一堆 CSV 数据设计一个关系数据库。它主要用于组织和查询子集的目的,我不会通过事务添加任何新记录。我的数据描述了大约 60 个企业的属性,并且是纵向的,因为它们每年(25 年)接受调查。

我的问题是,在企业的整个生命周期中,某些属性会看到许多重复项,而其他属性会看到很少。这是一个愚蠢的例子:

id, year, company_name, type, num_employees, total_sales,
056, 2000, papa johns, fast food, 11, 45000
056, 2001, papa johns, fast food, 11, 45557
056, 2002, papa johns, fast food, 14, 50000
056, 2003, papa johns, pizza, 17, 55000
056, 2004, papa johns, pizza, 17, 55456
063, 1998, pops barbershop, barber shops, 3, 15000
063, 1999, fresh cutz, barber shops, 3, 15023
063, 2000, fresh cutz, barber shops, 3, 15000
Run Code Online (Sandbox Code Playgroud)

我正在查看大约 6000 万个唯一 ID,其中大多数也有很多年。如果数据库采用与此类似的形式,它将非常庞大且充满重复项。

关于组织方式的任何建议?

MDC*_*CCL 8

根据数据关系模型的创始人EF Codd 博士的说法:

关系的列中允许重复值,但禁止重复行。1

因此,在逻辑级别——并考虑到创建关系数据库的既定意图——在示例表中出现的列值重复是无害的。

如果相关数据库必须包含大量信息,则必须对其进行管理;然而,有一些方面可以在概念上逻辑上物理上实用进行优化。

重组建议

说明性IDEF1X图

以引用的示例表和数据作为参考,列的组合似乎(year, company_name)足以确保行唯一性(因此应将其声明为KEY,无论是PRIMARY还是ALTERNATE);因此,标记为的列id将是多余的(假设它是一个特殊的附录,旨在包含代理键2值,缺乏业务意义,并且很可能需要在物理级别处理额外的 INDEX )。

然而,company_name和 ( company_?)type似乎是保存物理“重”数据的列(就字节而言,可能是类型CHAR(n)VARCHAR(n)),所以我认为进行一些结构重组很方便,如包含在IDEF1X 3图中的图1

图 1. 公司信息 IDEF1X 图

示例 SQL-DDL 逻辑级设计

那么,根据上述IDEF1X图,我们可以推导出如下逻辑级DDL布局:

-- As these are only illustrative examples, 
-- you should determine which are the most fitting 
-- data types and sizes for all the table columns 
-- depending on your business context characteristics.

-- Also, you should make accurate tests to define the most
-- convenient INDEX strategies.

CREATE TABLE company (
    company_id       INT      NOT NULL,
    name             CHAR(30) NOT NULL,
    created_datetime DATETIME NOT NULL,
    --
    CONSTRAINT company_PK PRIMARY KEY (company_id),
    CONSTRAINT company_AK UNIQUE      (name) -- ALTERNATE KEY.
);

CREATE TABLE year ( -- “Look-up” table.
    year             SMALLINT NOT NULL,
    created_datetime DATETIME NOT NULL,
    --
    CONSTRAINT year_PK PRIMARY KEY (year)
);

CREATE TABLE company_type ( -- “Look-up” table.
    company_type_code CHAR(2)  NOT NULL,
    name              CHAR(30) NOT NULL,
    --
    CONSTRAINT company_type_PK PRIMARY KEY (company_type_code),
    CONSTRAINT company_type_AK UNIQUE      (name) -- ALTERNATE KEY.
);

CREATE TABLE organizational_register (
    company_id          INT           NOT NULL,
    year                SMALLINT      NOT NULL,
    company_type_code   CHAR(2)       NOT NULL,
    number_of_employees INT           NOT NULL,
    total_sales         PERTINENTTYPE NOT NULL
    created_datetime    DATETIME      NOT NULL,
    --
    CONSTRAINT organizational_register_PK                 PRIMARY KEY (company_id, year), -- Composite PRIMARY KEY.
    CONSTRAINT organizational_register_TO_company_FK      FOREIGN KEY (company_id)
        REFERENCES company (company_id),
    CONSTRAINT organizational_register_TO_year_FK         FOREIGN KEY (year)
        REFERENCES year    (year),
    CONSTRAINT organizational_register_TO_company_type_FK FOREIGN KEY (company_type_code)
        REFERENCES company (company_type_code)
);
Run Code Online (Sandbox Code Playgroud)

公司

company_id类型的列INT(或类似的东西,取决于选择的平台)肯定会比company.name(有助于例如加快数据检索)“轻”得多,这就是为什么我将其添加并修复为 PRIMARY KEY ( PK) 这个表。

每个公司名称只会在列中保留一次,如果这是(物理)问题之一,则优化磁盘空间使用。应通过 UNIQUE 和 NOT NULL 约束将所述列建立为 ALTERNATE KEY (AK),以防止相同名称值的重复(从而保护数据完整性和一致性)。company.name

公司类型

company_type表将起到“查找”作用。

它的 PK,称为company_type_code,属于类型CHAR(2)可以保留具有业务意义的值,并且远小于包含完整company_type.name, 提高数据检索速度和保持可读性的点的列(在例如最终用户解释结果集时非常有用) ,在应用程序代码调试阶段等使用报表工具分析数据)。

该表可能包含如下所示的数据:

 +————————————————————-+-————————————-+
 | company_type_code | 姓名         |  
 +————————————————————-+-————————————-+
 | 外援 | 快餐 |
 +--------------+--------------+
 | PI | 披萨 |
 +--------------+--------------+
 | 学士 | 理发店|
 +--------------+--------------+

由于这些company_type.name值将仅保留在该表中,因此这种重新排列也将允许以更有利的方式利用磁盘空间。作为 AK 的这一列也需要 UNIQUE 和 NOT NULL 约束。

在您的数据库中可能还有其他一些列用于“查找”用途,因此可以采用类似的方法。

我集成了一个year表,因为我认为强制保留与已在该表中插入的年份相关的数据会很有帮助。

组织注册

上面讨论的所有修改将对与该organizational_register表有关的过程产生更大的影响。

因为,相对而言,该表很可能是保留更多行的表,具有两个减小大小的 FK 约束,即company_idcompany_type_code,磁盘空间消耗会更小。

此外,由于“减少”的 FK 值很可能作为搜索条件过滤器参与 SELECT 操作,它们将加快物理层的数据检索速度。

可选列

正如你提到的,真实的表有 60 多列——根据我的经验,这些列相当广泛——而且你通常会查询它们的子集,确定哪些列是可选的也是有利的,然后将它们移到单独的表中,将具有概念级关联的表分组。这个过程自然会产生几个更窄的表(按列的顺序),当然与宽表相比“更轻” ,在检索和/或计算过程方面通常表现得更快(例如,计算一个平均)在物理水平上。

这样,让我们​​假设company_type_codenumber_of_employees可选列(通常通过 NULL 标记处理)。因此,说明性 IDEF1X Diagram 可能会演变为如图2所示:

图 2. 公司信息 IDEF1X 图 - 示例修改

暂定的逻辑安排将修改如下:

CREATE TABLE company (
    company_id       INT      NOT NULL,
    name             CHAR(30) NOT NULL,
    created_datetime DATETIME NOT NULL,
    --
    CONSTRAINT company_PK PRIMARY KEY (company_id),
    CONSTRAINT company_AK UNIQUE      (name) -- ALTERNATE KEY.
);

CREATE TABLE year (
    year             SMALLINT NOT NULL,
    created_datetime DATETIME NOT NULL,
    --
    CONSTRAINT year_PK PRIMARY KEY (year)
);

CREATE TABLE sales (
    company_id       INT           NOT NULL,
    year             SMALLINT      NOT NULL,
    total_sales      PERTINENTTYPE NOT NULL
    column_x         FOO           NOT NULL,
    column_y         BAR           NOT NULL,
    created_datetime DATETIME      NOT NULL,
    --
    CONSTRAINT yearly_sales_PK     PRIMARY KEY (company_id, year), -- Composite PRIMARY KEY.
    CONSTRAINT sales_TO_company_FK FOREIGN KEY (company_id)
        REFERENCES company (company_id),
    CONSTRAINT sales_TO_year_FK    FOREIGN KEY (year)
        REFERENCES year    (year),
);

CREATE TABLE company_type (
    company_type_code CHAR(2)  NOT NULL,
    name              CHAR(30) NOT NULL,
    --
    CONSTRAINT company_type_PK PRIMARY KEY (company_type_code),
    CONSTRAINT company_type_AK UNIQUE      (name) -- ALTERNATE KEY.
);

CREATE TABLE organizational_register (
    company_id          INT      NOT NULL,
    year                SMALLINT NOT NULL,
    company_type_code   CHAR(2)  NOT NULL,
    number_of_employees INT      NOT NULL,
    created_datetime    DATETIME NOT NULL,
    --
    CONSTRAINT organizational_register_PK                 PRIMARY KEY (company_id, year), -- Composite PRIMARY KEY.
    CONSTRAINT organizational_register_TO_company_FK      FOREIGN KEY (company_id)
        REFERENCES company (company_id),
    CONSTRAINT organizational_register_TO_year_FK         FOREIGN KEY (year)
        REFERENCES year    (year),
    CONSTRAINT organizational_register_TO_company_type_FK FOREIGN KEY (company_type_code)
        REFERENCES company (company_type_code)
);
Run Code Online (Sandbox Code Playgroud)

因此,organizational_register将分为两个表:

  • sales,这将包含必需的列,并且
  • organizational_register,这将包含可选的。

使用 ACID 事务

所讨论的重组将需要多次 INSERT 才能操作和保留所有相关数据;在这方面,所有相关操作可能需要在同一个ACID TRANSACTION 中执行,以便它们作为单个工作单元要么成功要么失败。也许,某种批处理过程值得研究和(或)测试。

JOIN 和视图

然后,为了导出问题中显示的表的所有列,您必须创建一个 SELECT 语句来连接必要的表。

当然,基于包含上述 JOIN 的查询定义一个或多个视图在特定情况下非常实用,因为您可以轻松地直接从相关视图中选择信息。

概括

总的来说,我的建议是:

  • 对具有“行为”(物理上)更快的数据类型和大小的列设置 PK 约束;
  • 尽可能保留(有时是多列)PK 和 FK 的业务含义;
  • 确定哪些列可以移动到新表中,并根据概念级关联相应地放置它们;和
  • 根据适用的检索趋势定义(物理)索引策略。

尾注

1 Codd, EF(1990 年 1 月)。前言。在数据库管理的关系模型:版本 2 (p. viii) 中。美国马萨诸塞州波士顿:Addison-Wesley。

2例如,Microsoft SQL Server 中具有IDENTITY属性的列,或MySQL 中具有AUTO_INCREMENT属性的列等。

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