📜  数据库中的非规范化(1)

📅  最后修改于: 2023-12-03 14:54:54.425000             🧑  作者: Mango

非规范化数据设计

数据规范化是我们在设计数据库时最常使用的技术之一,它分解表为更小的部分以消除冗余的数据。但对于某些情况,非规范化设计是必须的。

非规范化设计指在设计数据库时,不采用范式化(normalization)方法来保证数据表的设计符合规范化形式。通常出于以下考虑:

  1. 有些时候数据数量非常大,按照规范化的设计可能会导致查询效率低下。
  2. 规范化的设计可能会导致多表关联查询操作频繁,另一个问题是在多表关联时引入的额外开销。
  3. 规范化是一种折衷策略,代价是需要保证不重复数据的同步更新操作,这可能会使简单的更新操作变得很麻烦。
非规范化设计的常见情况
  1. 数组字段

数组类型的字段通常被用来储存在数量不确定的数据。通常,我们会将数据以comma-separated-values (CSV)的形式存储。例如,一个名为“employees”的表包含以下两个字段:name和hobbies。hobbies字段可能包含多个爱好,储存在一个数组中。

| name | hobbies                |
|------|------------------------|
| Bob  | reading, running, hiking|
| Tom  | swimming, cycling       |
| Lucy | eating, shopping        |

这种情况下,我们可能会发现查询人员根据爱好会比较困难,通常我们需要将hobbies字段分割存储到独立表中以便更容易进行查询。但如果我们数据量较小, 少于10000条记录,而且经常需要查询人员的爱好,那么使用非规范化设计也许是正确的方法,可以减少数据表的关联查询。
  1. 集合型字段

某些字段可能会包含集合类型的数据,例如一个会员表会包含会员资格信息、会员所属组织信息等等。集合型字段可以使用数组来组织,而非规范化设计可以使查询语句更加直观和容易编写。例如下面是一个包含会员id和所属组织信息字段的表。

| Member ID | Organizations |
|-----------|----------------|
| 101       | Org A, Org B, Org C |
| 102       | Org C, Org D |
| 103       | Org B |

这种情况下,如果我们需要根据组织名查询会员信息,可能需要使用比较复杂的查询语句,例如使用`LIKE '%Org A%'`。但如果每个会员仅属于一个组织,那么使用非规范化设计不仅没有什么好处,反而会使查询更加麻烦。
  1. 聚合型字段

一些字段是基于数据聚合的,例如一个sales表包含以下字段:salesrep ID、month、product ID和sales amount。我们可能需要每个Salesrep月度销售总额, 这可以通过对表进行分组计算得到。但对于某些查询,我们可能需要单独查询每个Salesrep单月的销售情况,这时我们可以通过创建新表来储存这些聚合值,避免每次查询时重新计算。这种方式在数据量大,或者查询需求比较特殊时可能更有效率。

非规范化设计的缺点

非规范化的设计也有明显的缺点,以下是一些可能存在的问题:

  1. 数据冗余:非规范化设计容易导致数据冗余,这通常会导致更新、插入或删除操作更加复杂。这种冗余可能会导致数据不一致或者存在错误的数据。
  2. 数据储存:非规范化设计会导致数据量更大,需要更多的存储空间。
  3. 数据查询:非规范化设计可能会使一些查询更加复杂,因为进行查询时经常需要去重或对嵌套集合进行操作。
  4. 数据库容错性:非规范化设计容易出现故障,并且不易纠正。这样导致数据无能力保持一致性。
结论

在数据库设计时,非规范化设计应该是一个必要的考虑因素。如果数据量较小,查询需求明确,采用非规范化设计能让我们获得更好的查询效率。但如果数据很大,非规范化设计可能会导致查询效率低下,更新插入等操作更加困难。在设计过程中,需要根据实际场景来进行权衡取舍。

代码片段

下面是示例代码展示如何使用非规范化设计:

--创建一个employees表,包含名称和hobbies字段
CREATE TABLE employees (
  name VARCHAR(50),
  hobbies VARCHAR(128) 
);

--插入数据
INSERT INTO employees (name, hobbies)
VALUES ('Bob', 'reading, running, hiking'),
       ('Tom', 'swimming, cycling'),
       ('Lucy', 'eating, shopping');

--查询名叫Tom的员工的姓名和爱好
SELECT name, hobbies FROM employees WHERE name = 'Tom';