当前位置: 首页 > 科技观察

如何实现良好的数据库设计?

时间:2023-03-22 14:29:58 科技观察

1。我们为什么要关心数据库设计?无论应用程序或数据库如何变化,数据始终是最重要的部分。通常,数据是系统的主要目的。这就是为什么我们不应该将数据库系统视为存储数据的黑匣子,而应将其视为验证和防止数据损坏的工具。为此,必须有一个健壮且经过深思熟虑的数据库设计。当然,业务逻辑是在应用层编码的,这保证了数据在到达数据库之前是正确格式的。然而,谁又能保证网络故障或缺陷不会放出不可靠的“客人”呢?此外,应用层并不是通往数据库的唯一“大门”。我们可以使用导入脚本,维护脚本,DBA和开发人员会和他们进行交互。我们可以在幕后采取预防措施,以确保数据在存储之前始终经过检查。拥有强大、可靠的数据也有助于开发和测试。将列设置为NotNull可以节省很多假设该列为null的测试场景,并且还可以简化代码,这样开发人员就不必(几乎)在每次访问该值时都检查该值。在强调了良好数据库设计的重要性之后,让我们看看有哪些工具可以实现这一点。2.规范化这无疑是优秀设计的首要原则。在这里,我们不打算深入研究归一化规则,只是想强调它的重要性。这里有关于这个主题的很好的资源,您可以进一步阅读。https://docs.microsoft.com/en-us/office/troubleshoot/access/database-normalization-description3.数据类型另一件需要注意的事情是定义适当的属性类型。这不仅提高了数据库的性能,而且在存储数据之前验证数据。所以,我们应该将数字数据存储在“整数”、“数字”字段中;将时间戳存储在“timestamp”、“timestamptz”字段中;存储在“bit”、“char(1)”或“boolean”字段中布尔值等。日期值得特别注意。如果Date属性假定只有日期部分(OrderDate、ReleaseDate),请使用没有时间部分的Date类型。如果您只需要保留时间(StartTime、EndTime),请使用适当的时间类型。如果不需要精度,则将其指定为零(“time(0)”)。带有时间部分的日期的一个问题是您必须始终截断时间部分以仅显示日期,并且当您在与数据库所在的不同时区中显示时,请确保它没有被格式化为昨天或明天.跳转到DST时,带有时间部分的日期时间的加法和减法也可能会出现问题。4.约束约束是本次讨论的重点。它们将无效数据排除在外并确保数据的稳健性。让我们一一看看。NotNull约束如果业务规则规定该属性应始终存在,请毫不犹豫地将其设置为NotNull。可以设置为NotNull的字段有Id、Name、AddedDate、IsActive、State、CategoryId(如果所有项目都应该有一个类别)、ItemCount、Price等等。通常,这些属性在业务逻辑中起着重要作用。其他可选信息字段可能仍设置为Null。但要注意不要对可空属性使用NotNull约束。例如,长时间运行的任务始终具有StartTimestamp(非空),但仅在任务完成时更新EndTimestamp(空)。另一个典型的例子是Employee表的ManagerId,不是所有员工都有经理。不要试图使ManagerId非空,并为没有经理的员工插入“0”或“-1”。当我们添加外键约束时,这会导致其他问题。UniqueConstraints另外,根据业务规则,一些属性(或属性的组合)应该是唯一的,例如Id、PinNumber、BookId和AuthorId、OrderNo等。通过添加唯一约束来保证这些属性是唯一的。还有一点需要注意:您可以使用唯一索引来达到相同的效果,但添加约束是更好的方法。因为当添加唯一约束时,会自动创建一个非唯一索引。因此,如果由于某种原因您必须暂时禁用/启用约束,这很容易。对于唯一索引,您必须删除/重新创建索引,这在性能和时间方面都是一项昂贵的操作。主键NotNull和唯一约束一起构成主键。当我们想到主键时,很快就会想到像Id或ObjectId这样的列。但是主键也可以是复合的,比如BookId和AuthorId。这里的困境是,我应该单独使用Id列作为主键,还是两者结合使用?通常,单独的Id列是一种更好的方法,因为它使连接更清晰,并且还可以轻松地将另一列添加到唯一组合中。但是,即使使用单独的主键(Id),我们仍然必须为BookId和AuthorId列添加唯一约束。检查约束检查约束允许我们定义数据的有效值/范围。适合Check约束的属性有百分比(0~100之间)、status(0、1、2)、price、amount、total(大于等于0)、PinNumber(固定长度)等。同理,不要尝试将业务逻辑编码到Check约束中。我记得有一次在AccountBalance列中添加了一个“大于或等于零”的检查约束,以避免意外透支。默认约束默认约束也很重要。它们允许我们向现有表添加新的NotNull列,并保持“旧”API与新结构兼容,直到所有各方都升级(尽管在完全升级后默认约束应该被删除)。这里有一些事情要记住。不要在默认约束中编写业务逻辑。例如,函数“now()”可能非常适合(尽管并不总是)作为日志表中时间戳字段的默认值,但不适用于订单表中的订单日期字段。您可能想在插入语句中省略OrderDate并依赖默认约束,但这意味着将业务逻辑扩展到数据库层。还有,在某些情况下,业务可能会在订单通过后才给OrderDate赋值,因为默认的约束条件深藏在数据库中,所以当我们在应用层修改代码时,不会被修改。如此明显。外键约束外键约束是关系数据库设计中的王道。外键与主键配合使用,以确保表之间的数据一致性。规范化规则告诉我们何时将数据提取到表中并使用外键引用它。在这里我们将着重讨论细节上的差异,例如OnDelete和OnUpdate规则。DBeaver中的外键约束编辑器让我们从简单的部分开始:OnUpdate。外键是指很少(如果有的话)修改的主键。所以,OnUpdate规则不是很常用,但是将它设置为Cascade还是有意义的,因为我们有时可能需要更新某些行的主键(通常在迁移之后)。这样数据库将允许我们进行更新并将新的id传播到子表。OnDelete规则有点复杂。根据数据库的不同,我们有NoAction、Restrict、SetNull、SetDefault和Cascade选项。那么,选择哪一个呢?通常,我们为引用查找的键或不引用实体的实体选择NoAction。例如Products->Categories,Books->Authors等。在大多数情况下,Restrict和NoAction是一样的,但对于某些数据库,它们有细微的差别。https://www.vertabelo.com/blog/on-delete-restrict-vs-on-delete-no-action/另一方面,当没有父记录就不能存在子记录时,选择级联。在BookandAuthor的例子中,当一本书被删除时,我们也应该从BookAuthor表中删除记录。其他的例子还有OrderDetails->Orders,PostComments->Posts等。这里可能有人不同意,数据库不应该自动删除子行,应该由应用层删除。按照业务逻辑,是这样的。但有时“不重要”的子键删除可以委托给数据库。很少使用SetNull。例如,Employee.ManagerId和Employee.Id之间的外键可以是SetNull。当一个经理被解雇时,他的下属就没有经理了。显然,只有列可以为空时才能选择此规则。在这些规则中,SetDefault是最罕见的。当删除父记录时,它将列设置为其默认值。因为外键指的是主键,所以很难想象带有硬编码默认值的外键字段。但无论如何,这个选项是存在的,我们可能仍然需要它。5.索引索引是良好数据库设计的重要组成部分,但与我们的讨论有点不同,因为它们对保护我们的数据几乎没有作用(唯一索引除外)。需要注意一点:有些RDBMS系统(如Oracle)在创建外键时会自动创建索引,无需我们操心。MSSQLServer等其他数据库不会这样做,我们必须自己添加索引。6.总结一个深思熟虑的设计可以为我们节省大量的编码、测试和故障排除时间。在设计良好的数据库上编写查询和报告是一种乐趣。向新系统发布和迁移数据也非常容易。编码愉快!